fix: revoke OAuth 2.0 old refresh tokens when refreshing#27581
Closed
fix: revoke OAuth 2.0 old refresh tokens when refreshing#27581
Conversation
- Fix getClient endpoint to use proper REST API error format instead of OAuth token error format (confidence 9/10) - Add missing space after comma in error format string in token.input.pipe.ts (confidence 9/10) - Support both camelCase and snake_case inputs in authorize endpoint for backward compatibility (confidence 10/10) - Restore legacy /exchange and /refresh endpoints alongside new /token endpoint for backward compatibility (confidence 10/10) - Add OAuth2TokensResponseDto for legacy endpoint wrapped responses - Add OAuth2LegacyExchangeInput and OAuth2LegacyRefreshInput for legacy endpoints Co-Authored-By: unknown <>
- Log errors when status code >= 500 in handleClientError (confidence 9/10) - Add Cache-Control: no-store and Pragma: no-cache headers to legacy /exchange and /refresh endpoints (confidence 9/10) Co-Authored-By: unknown <>
This reverts commit 39cc4aa.
This reverts commit 97bf593.
- Fix getClient to use handleClientError instead of handleTokenError (confidence 10) - Restore legacy /exchange and /refresh endpoints for backward compatibility (confidence 9) - Fix RFC 6749 error format: use human-readable messages in error_description (confidence 9) - Fix errorDescription in OAuthService to use OAUTH_ERROR_REASONS mapping (confidence 9) Co-Authored-By: unknown <>
- Fix security issue: Replace 'CALENDSO_ENCRYPTION_KEY is not set' with generic 'Internal server configuration error' message (confidence 10/10) - Fix backward compatibility: Create OAuth2LegacyTokensDto with camelCase properties for legacy /exchange and /refresh endpoints (confidence 9/10) - Skipped: RFC 6749 error field issue (confidence 8/10, below threshold) Co-Authored-By: unknown <>
This reverts commit a080e93.
This reverts commit 04986a1.
- Restore OAuth2LegacyExchangeInput and OAuth2LegacyRefreshInput classes - Restore legacy /exchange and /refresh endpoints in OAuth2Controller - Restore OAuth2LegacyTokensDto and OAuth2TokensResponseDto classes - Restore OAUTH_ERROR_DESCRIPTIONS mapping in oauth2-error.service.ts - Restore OAUTH_ERROR_REASONS lookup in OAuthService.ts mapErrorToOAuthError - Fix encryption_key_missing error to not expose internal env var name Addresses Cubic AI feedback with confidence >= 9/10: - Comment 32 (9/10): Legacy endpoints and input classes - Comment 34 (9/10): Error description mapping in OAuthService - Comment 35 (10/10): OAUTH_ERROR_DESCRIPTIONS in error service Skipped (confidence < 9/10): - Comment 33 (8/10): getClient handleTokenError vs handleClientError Co-Authored-By: unknown <>
…-manager.devin.ai/proxy/github.com/calcom/cal.com into lauris/cal-7090-test-oauth-v2-endpoints
This reverts commit 416bef9.
…ent-is-in-review' into lauris/cal-7035-fix-invalidate-old-refresh-tokens
…e-oauth-client-is-in-review
…ent-is-in-review' into lauris/cal-7035-fix-invalidate-old-refresh-tokens
Base automatically changed from
lauris/cal-7029-feat-enable-test-access-while-oauth-client-is-in-review
to
main
February 19, 2026 11:11
The base branch was changed.
Contributor
E2E results are ready! |
08ccf0d to
5e56c63
Compare
Contributor
|
Moving back to draft while we discuss |
Contributor
Author
marking it as ready for review for Pedro |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #26538
Summary by cubic
Adds OAuth 2.0 refresh token rotation with server-side tracking so old refresh tokens are rejected after use. Introduces a unified POST /token endpoint (form-encoded, snake_case) and keeps legacy endpoints. Addresses CAL-7035.
Bug Fixes
Migration
Written for commit 48be831. Summary will update on new commits.
Updates since last revision
OAuthRefreshTokenRepository.rotateToken()where the conditionuserId != nullfailed to distinguish betweenundefinedandnull. WhenuserIdis explicitlynull(client credentials token), the previous condition would skip addinguserIdto the where clause, causingdeleteManyto match ALL tokens for the client—including tokens belonging to actual users. Changed touserId !== undefinedto properly handle all cases.throw new Error(...)with typedthrow new ErrorWithCode(ErrorCode.BadRequest, "invalid_grant", ...)in the else branch ofrefreshAccessToken()so clients get a proper OAuth error response instead of a 500 (identified by Cubic AI review).What does this PR do?
Implements OAuth 2.0 refresh token rotation to prevent token reuse attacks. When a refresh token is used, it is invalidated and a new one is issued. Attempting to reuse an old refresh token returns
invalid_grantwith reasonrefresh_token_revoked.Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
invalid_grant/refresh_token_revokedoauth2.controller.e2e-spec.ts) and web (oauth-refresh-tokens.e2e.ts)Human Review Checklist
undefinedvsnullfix inrotateToken()correctly isolates client credentials tokens (whereuserIdisnull) from user tokenssecretfield) works correctly for one-time useloggerimport inOAuthService.tsmay be unused after error handling refactor (lint warning, non-blocking)Link to Devin run: https://app.devin.ai/sessions/78d60d454fa2432cb97575bc00747c05
Requested by: unknown ()