Skip to content

Commit 0aff70e

Browse files
authored
chore(backend): Reject OAuth JWTs for session token token type (#7765)
1 parent ed150f0 commit 0aff70e

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clerk/backend": patch
3+
---
4+
5+
Improve token type validation in authentication requests

integration/tests/machine-auth/oauth.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ test.describe('OAuth machine authentication @machine', () => {
5050
.commit();
5151

5252
await app.setup();
53-
await app.withEnv(appConfigs.envs.withEmailCodes);
53+
await app.withEnv(appConfigs.envs.withAPIKeys);
5454
await app.dev();
5555

5656
// Test user that will authorize the OAuth application

packages/backend/src/tokens/__tests__/request.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import {
1010
mockJwtPayload,
1111
mockMalformedJwt,
1212
} from '../../fixtures';
13-
import { mockMachineAuthResponses, mockTokens, mockVerificationResults } from '../../fixtures/machine';
13+
import {
14+
mockMachineAuthResponses,
15+
mockSignedOAuthAccessTokenJwt,
16+
mockTokens,
17+
mockVerificationResults,
18+
} from '../../fixtures/machine';
1419
import { server } from '../../mock-server';
1520
import type { AuthReason } from '../authStatus';
1621
import { AuthErrorReason, AuthStatus } from '../authStatus';
@@ -1496,6 +1501,17 @@ describe('tokens.authenticateRequest(options)', () => {
14961501
isAuthenticated: false,
14971502
});
14981503
});
1504+
1505+
test('rejects OAuth JWT token when acceptsToken is session_token', async () => {
1506+
const request = mockRequest({ authorization: `Bearer ${mockSignedOAuthAccessTokenJwt}` });
1507+
const result = await authenticateRequest(request, mockOptions({ acceptsToken: 'session_token' }));
1508+
1509+
expect(result).toBeSignedOut({
1510+
reason: AuthErrorReason.TokenTypeMismatch,
1511+
message: '',
1512+
});
1513+
expect(result.toAuth()).toBeSignedOutToAuth();
1514+
});
14991515
});
15001516

15011517
describe('Array of Accepted Token Types', () => {

packages/backend/src/tokens/request.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { AuthErrorReason, handshake, signedIn, signedOut, signedOutInvalidToken
1414
import { createClerkRequest } from './clerkRequest';
1515
import { getCookieName, getCookieValue } from './cookie';
1616
import { HandshakeService } from './handshake';
17-
import { getMachineTokenType, isMachineToken, isTokenTypeAccepted } from './machine';
17+
import { getMachineTokenType, isMachineToken, isOAuthJwt, isTokenTypeAccepted } from './machine';
1818
import { OrganizationMatcher } from './organizationMatcher';
1919
import type { MachineTokenType, SessionTokenType } from './tokenTypes';
2020
import { TokenType } from './tokenTypes';
@@ -411,6 +411,19 @@ export const authenticateRequest: AuthenticateRequest = (async (
411411
async function authenticateRequestWithTokenInHeader() {
412412
const { tokenInHeader } = authenticateContext;
413413

414+
// Reject OAuth JWTs that may appear in headers when expecting session tokens.
415+
// OAuth JWTs are valid Clerk-signed JWTs and will pass verifyToken() verification,
416+
// but should not be accepted as session tokens.
417+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
418+
if (isOAuthJwt(tokenInHeader!)) {
419+
return signedOut({
420+
tokenType: TokenType.SessionToken,
421+
authenticateContext,
422+
reason: AuthErrorReason.TokenTypeMismatch,
423+
message: '',
424+
});
425+
}
426+
414427
try {
415428
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
416429
const { data, errors } = await verifyToken(tokenInHeader!, authenticateContext);

0 commit comments

Comments
 (0)