Jun 6, 2026·6 min read·19 visits
NocoDB fails to revoke OAuth tokens when a user changes or resets their password, allowing pre-existing OAuth grants to retain active API access.
NocoDB is subject to an insufficient session expiration vulnerability where OAuth access and refresh tokens are not invalidated or revoked during security-sensitive actions such as password changes, forgot-password requests, or password resets. This allows an attacker possessing an active OAuth token to maintain unauthorized persistence.
NocoDB is an open-source, no-code database platform that allows teams to transform databases into smart spreadsheets. Because NocoDB acts as a central hub for organizational data, it supports complex authentication structures, including standard web sessions and third-party integrations via OAuth. These interfaces expose various attack surfaces, especially when managing token lifetimes across security boundaries.
This security advisory details a critical session management vulnerability classified under CWE-613 (Insufficient Session Expiration). In NocoDB versions prior to 2026.05.1, OAuth access and refresh tokens are not invalidated or revoked when a user completes a password change, requests a forgot-password link, or executes a password reset. This defect allows previously issued OAuth grants to remain valid, establishing a persistent channel of unauthorized access.
While NocoDB correctly invalidated standard web sessions (via UserRefreshToken) during security-sensitive actions, the omission of OAuth token revocation represents a significant gap in the platform's security boundary. If an attacker possesses a valid OAuth token, they can maintain persistent, silent access to the database despite the victim actively changing their credentials.
The root cause of this vulnerability lies in the incomplete implementation of the token revocation logic within NocoDB's user management service. Session management in NocoDB operates on two distinct parallel paths: UserRefreshToken for browser-based user sessions, and OAuthToken for API and third-party integration authorization. This design separates standard user sessions from third-party application access to prevent user actions from unintentionally breaking valid external workflows.
Prior to the resolution in version 2026.05.1, when a user triggered security-sensitive events like password modification, forgot-password requests, or password resets, NocoDB invoked the UserRefreshToken.deleteAllUserToken(user.id) method. This successfully cleared active web-based sessions. However, the accompanying method responsible for purging OAuth-specific credentials, named revokeAllOAuthTokensByUser, was defined only as an empty stub inside the underlying UsersService layer.
As a consequence of this empty implementation, the metadata database holding OAuth mappings—including tables such as nc_api_tokens, nc_api_token_scopes, or related token schemas—remained unpurged. The application continued to accept these existing tokens because the validation middleware verified them against active database records that were never marked as revoked or deleted. Consequently, a compromised account could not be fully secured by changing its password alone.
The fix was introduced in Pull Request #13599 and split across two major commits to ensure correct integration without breaking compliant OAuth behavior. The primary modification implements a static revokeAllByUser helper in the OAuthToken model and integrates it within UsersService.
// packages/nocodb/src/models/OAuthToken.ts
static async revokeAllByUser(userId: string, ncMeta = Noco.ncMeta) {
const tokens = await this.listByUser(userId, ncMeta);
if (tokens?.length) {
// Concurrently revoke all retrieved tokens
await Promise.all(tokens.map((t) => this.revoke(t.id, ncMeta)));
}
}This method retrieves all active tokens and maps over them to invoke the revoke function. This model method is then integrated into the three main security-sensitive code paths in packages/nocodb/src/services/users/users.service.ts:
// In Password Change Flow
await UserRefreshToken.deleteAllUserToken(user.id);
await OAuthToken.revokeAllByUser(user.id); // Added to revoke OAuth grants
this.appHooksService.emit(AppEvents.USER_PASSWORD_CHANGE, { ... });
// In Forgot Password Flow
await OAuthToken.revokeAllByUser(user.id); // Added to clear grants immediately
this.appHooksService.emit(AppEvents.USER_PASSWORD_FORGOT, { ... });
// In Password Reset Flow
await UserRefreshToken.deleteAllUserToken(user.id);
await OAuthToken.revokeAllByUser(user.id); // Added to revoke OAuth grants
this.appHooksService.emit(AppEvents.USER_PASSWORD_RESET, { ... });Importantly, a secondary commit reverted the application of OAuthToken.revokeAllByUser(user.id) from the standard signout flow. This distinction is critical for standards compliance: a regular sign-out ends only the immediate user-agent session (such as a browser cookie session) and should not disrupt persistent third-party API or service integrations. In contrast, password changes, forgot requests, and resets assume credential compromise, necessitating full token revocation.
Exploiting this vulnerability requires that the attacker already possesses an active OAuth access or refresh token prior to the victim's password alteration event. This precursor state could occur if an attacker successfully compromises a user's password to generate an OAuth token, or exploits a brief physical/logical access vulnerability to authorize a rogue client application.
Once the OAuth token is generated, the victim may detect anomalous activity and attempt to remediate the breach by executing a password reset. While this action invalidates the session cookies, the empty stub in the original code allows the attacker's OAuth token to remain fully active. The attacker can then continue to query NocoDB endpoints without needing to re-authenticate, executing actions with the same privileges as the victim.
# Example of persistent API access using the unrevoked bearer token
curl -X GET "http://nocodb.local/api/v2/meta/bases" \
-H "Authorization: Bearer [unrevoked_oauth_token]" \
-H "Content-Type: application/json"This request will succeed and return administrative data, database metadata, or table contents, bypassing the user's explicit attempt to secure the account via credential rotation.
The security impact of this vulnerability is assessed at CVSS 6.3 (Medium). Because NocoDB handles business-critical databases, the persistence of unauthorized API access can lead to prolonged, undetected data exfiltration. Attackers can leverage active integrations or model context protocols (MCP) to read, modify, or delete sensitive records across all tables that the victim is authorized to access.
From an architectural perspective, the vulnerability facilitates a persistent foothold. Even if an enterprise deploys active monitoring for user logins, the usage of existing, valid OAuth bearer tokens bypasses standard login portals, multi-factor authentication (MFA) prompts, and single sign-on (SSO) reassessments. This directly violates the principle of least privilege and frustrates remediation efforts during incident response.
Furthermore, because the revocation relies on a concurrent Promise.all loop, security teams must evaluate the possibility of database performance degradation if a user with thousands of active OAuth tokens triggers a password reset. Although a minor concern, this concurrency design could be leveraged under specific circumstances to induce resource exhaustion on the metadata database.
The primary and recommended mitigation is upgrading the NocoDB deployment to version 2026.05.1 or later. This version incorporates the complete implementation of OAuthToken.revokeAllByUser across all password alteration events, ensuring complete session and token cleanup.
If upgrading is not immediately feasible, administrators can manually invalidate active OAuth tokens by running a targeted deletion query against the NocoDB metadata database (RootDB). The following SQL command targets the OAuth token table to remove all tokens associated with a compromised user ID:
-- Manually purge OAuth tokens for a specific compromised user
DELETE FROM nc_oauth_tokens
WHERE user_id = 'usr_xxxxxx';Additionally, security operations teams should implement logical event correlation. By auditing log events, detection systems can flag instances where an OAuth token is utilized shortly after a USER_PASSWORD_RESET or USER_PASSWORD_CHANGE hook is emitted for that specific user. Such patterns strongly suggest an active exploitation attempt or a dormant backdoor integration.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
nocodb NocoDB | <= 2026.05.0 | 2026.05.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-613: Insufficient Session Expiration |
| Attack Vector | Network (AV:N) |
| CVSS Score | 6.3 (Medium) |
| Exploit Status | No public exploit available |
| Vulnerable Versions | <= 2026.05.0 |
| Patched Version | 2026.05.1 |
| CISA KEV Status | Not Listed |
The application does not invalidate the session or corresponding authorization tokens (OAuth access/refresh tokens) during events where the user's credentials change.
A denial-of-service (DoS) vulnerability in Pillow (Python Imaging Library) versions 12.0.0 through 12.2.0 allows unauthenticated remote attackers to trigger 100% CPU utilization and hang the processing thread. The issue occurs within the Encapsulated PostScript (EPS) image parser (PIL/EpsImagePlugin.py) due to missing validation on the byte count parsed from %%BeginBinary: comments, allowing negative values to cause an infinite backward stream seek loop. This formatting-level state-looping issue occurs during the initial format sniffing phase inside Image.open() and does not require the system Ghostscript interpreter to be executed or present. It is resolved in version 12.3.0.
A Denial of Service vulnerability exists in the JPEG2000 decoder of Pillow (versions 8.2.0 to 12.2.0) due to memory allocation state accumulation across tiles, leading to rapid process termination.
CVE-2026-59205 is a high-severity heap-based out-of-bounds write vulnerability affecting Pillow prior to version 12.3.0. The flaw stems from a validation omission in the ImageCmsTransform class where source and destination image modes are not checked against the configurations defined during the creation of the transform. An attacker can exploit this discrepancy to trigger a heap buffer overflow or an out-of-bounds read by supplying an under-allocated target image buffer.
A vulnerability in the 'body-parser' Node.js middleware allows unauthenticated attackers to trigger a Denial of Service. When the 'limit' configuration option is misconfigured with an unparseable type or empty value, size limits fail open. This leads to unrestricted heap memory allocation and process crash via Out of Memory (OOM).
A security vulnerability in @astrojs/netlify allows attackers to bypass remote image path restrictions by leveraging unescaped regular expression metacharacters. The integration adapter fails to sanitize developer-defined pathnames before interpolating them into a configuration JSON file consumed by Netlify's Edge Image CDN. This results in overly permissive matching behavior at the edge routing layer, enabling path-traversal and filter bypasses.
Prior to version 1.2.11, the LangChain LLM framework is affected by a Server-Side Request Forgery (SSRF) vulnerability inside its image token counting mechanism. Specifically, the ChatOpenAI.get_num_tokens_from_messages() method retrieves arbitrary image_url values from user prompts without validating the destination host or IP address. Attackers can exploit this issue to scan internal infrastructure, access local services, or harvest credentials from cloud metadata services.