Skip to content

Commit 5e0630b

Browse files
committed
chore: clean up and reuse token type checker utilities
1 parent 9c42e05 commit 5e0630b

File tree

6 files changed

+57
-52
lines changed

6 files changed

+57
-52
lines changed

packages/nextjs/src/app-router/server/auth.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { AuthObject } from '@clerk/backend';
22
import type {
33
AuthenticatedMachineObject,
4+
AuthenticateRequestOptions,
45
RedirectFun,
56
SignedInAuthObject,
67
SignedOutAuthObject,
@@ -51,7 +52,7 @@ type MachineAuth<T extends TokenType> = (AuthenticatedMachineObject | Unauthenti
5152
tokenType: T;
5253
};
5354

54-
export type AuthOptions = { acceptsToken?: TokenType | TokenType[] | 'any' };
55+
export type AuthOptions = { acceptsToken?: AuthenticateRequestOptions['acceptsToken'] };
5556

5657
export interface AuthFn<TRedirect = ReturnType<typeof redirect>> {
5758
/**

packages/nextjs/src/server/clerkMiddleware.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type { NextMiddleware, NextRequest } from 'next/server';
2121
import { NextResponse } from 'next/server';
2222

2323
import type { AuthFn } from '../app-router/server/auth';
24+
import type { GetAuthOptions } from '../server/createGetAuth';
2425
import { isRedirect, serverRedirectWithAuth, setHeader } from '../utils';
2526
import { withLogger } from '../utils/debugLogger';
2627
import { canUseKeyless } from '../utils/feature-flags';
@@ -46,6 +47,7 @@ import {
4647
assertKey,
4748
decorateRequest,
4849
handleMultiDomainAndProxy,
50+
isTokenTypeAccepted,
4951
redirectAdapter,
5052
setRequestHeadersOnNextResponse,
5153
} from './utils';
@@ -408,27 +410,21 @@ const createMiddlewareAuthHandler = (
408410
: {},
409411
);
410412

411-
const authHandler = async (
412-
options = {
413-
acceptsToken: 'session_token',
414-
},
415-
) => {
416-
const acceptsToken = options.acceptsToken;
413+
const authHandler = async (options: GetAuthOptions) => {
414+
const acceptsToken = options.acceptsToken || 'session_token';
417415

418416
if (acceptsToken === 'any') {
419417
return authObjWithMethods;
420418
}
421419

422-
const tokenTypes = Array.isArray(acceptsToken) ? acceptsToken : [acceptsToken];
423-
if (!tokenTypes.includes(authObject.tokenType)) {
420+
if (!isTokenTypeAccepted(authObject.tokenType, acceptsToken)) {
424421
if (authObject.tokenType === 'session_token') {
425422
return {
426423
...signedOutAuthObject(),
427424
redirectToSignIn,
428425
redirectToSignUp,
429426
};
430427
}
431-
432428
return unauthenticatedMachineObject(authObject.tokenType);
433429
}
434430

packages/nextjs/src/server/createGetAuth.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
import type { AuthObject } from '@clerk/backend';
2-
import type { TokenType } from '@clerk/backend/internal';
32
import { constants } from '@clerk/backend/internal';
43
import { isTruthy } from '@clerk/shared/underscore';
54

65
import { withLogger } from '../utils/debugLogger';
76
import { isNextWithUnstableServerActions } from '../utils/sdk-versions';
87
import type { GetAuthDataFromRequestOptions } from './data/getAuthDataFromRequest';
98
import {
10-
getAuthDataFromRequest as getAuthDataFromRequestOriginal,
9+
getAuthDataFromRequestAsync as getAuthDataFromRequestAsyncOriginal,
1110
getAuthDataFromRequestSync as getAuthDataFromRequestSyncOriginal,
1211
} from './data/getAuthDataFromRequest';
1312
import { getAuthAuthHeaderMissing } from './errors';
1413
import { detectClerkMiddleware, getHeader } from './headers-utils';
1514
import type { RequestLike } from './types';
1615
import { assertAuthStatus } from './utils';
1716

18-
type GetAuthOptions = {
19-
acceptsToken?: TokenType | TokenType[] | 'any';
17+
export type GetAuthOptions = {
18+
acceptsToken?: GetAuthDataFromRequestOptions['acceptsToken'];
2019
};
2120

2221
/**
@@ -56,11 +55,11 @@ export const createAsyncGetAuth = ({
5655
assertAuthStatus(req, noAuthStatusMessage);
5756
}
5857

59-
const getAuthDataFromRequest = (req: RequestLike, opts: GetAuthDataFromRequestOptions = {}) => {
60-
return getAuthDataFromRequestOriginal(req, { ...opts, logger, acceptsToken: options?.acceptsToken });
58+
const getAuthDataFromRequestAsync = (req: RequestLike, opts: GetAuthDataFromRequestOptions = {}) => {
59+
return getAuthDataFromRequestAsyncOriginal(req, { ...opts, logger, acceptsToken: options?.acceptsToken });
6160
};
6261

63-
return getAuthDataFromRequest(req, { ...opts, logger, acceptsToken: options?.acceptsToken });
62+
return getAuthDataFromRequestAsync(req, { ...opts, logger, acceptsToken: options?.acceptsToken });
6463
};
6564
});
6665

packages/nextjs/src/server/data/getAuthDataFromRequest.ts

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import type { AuthObject } from '@clerk/backend';
2-
import type { SignedInAuthObject, SignedOutAuthObject, TokenType } from '@clerk/backend/internal';
2+
import type { AuthenticateRequestOptions, SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend/internal';
33
import {
44
authenticatedMachineObject,
55
AuthStatus,
66
constants,
7+
getMachineTokenType,
78
isMachineToken,
89
signedInAuthObject,
910
signedOutAuthObject,
@@ -16,12 +17,12 @@ import type { LoggerNoCommit } from '../../utils/debugLogger';
1617
import { API_URL, API_VERSION, PUBLISHABLE_KEY, SECRET_KEY } from '../constants';
1718
import { getAuthKeyFromRequest, getHeader } from '../headers-utils';
1819
import type { RequestLike } from '../types';
19-
import { assertTokenSignature, decryptClerkRequestData } from '../utils';
20+
import { assertTokenSignature, decryptClerkRequestData, isTokenTypeAccepted } from '../utils';
2021

2122
export type GetAuthDataFromRequestOptions = {
2223
secretKey?: string;
2324
logger?: LoggerNoCommit;
24-
acceptsToken?: TokenType | TokenType[] | 'any';
25+
acceptsToken?: AuthenticateRequestOptions['acceptsToken'];
2526
};
2627

2728
type SessionAuthObject = SignedInAuthObject | SignedOutAuthObject;
@@ -51,7 +52,11 @@ export const getAuthDataFromRequestSync = (
5152
authReason,
5253
};
5354

54-
opts.logger?.debug('auth options', options);
55+
// Only accept session tokens in sync version
56+
const acceptsToken = opts.acceptsToken ?? 'session_token';
57+
if (acceptsToken !== 'session_token' && acceptsToken !== 'any') {
58+
return signedOutAuthObject(options);
59+
}
5560

5661
if (!authStatus || authStatus !== AuthStatus.SignedIn) {
5762
return signedOutAuthObject(options);
@@ -69,45 +74,35 @@ export const getAuthDataFromRequestSync = (
6974
* Given a request object, builds an auth object from the request data. Used in server-side environments to get access
7075
* to auth data for a given request.
7176
*/
72-
export const getAuthDataFromRequest = async (
77+
export const getAuthDataFromRequestAsync = async (
7378
req: RequestLike,
7479
opts: GetAuthDataFromRequestOptions = {},
7580
): Promise<AuthObject> => {
76-
const authStatus = getAuthKeyFromRequest(req, 'AuthStatus');
77-
const authMessage = getAuthKeyFromRequest(req, 'AuthMessage');
78-
const authReason = getAuthKeyFromRequest(req, 'AuthReason');
79-
80-
opts.logger?.debug('headers', { authStatus, authMessage, authReason });
81+
const bearerToken = getHeader(req, constants.Headers.Authorization)?.replace('Bearer ', '');
82+
const acceptsToken = opts.acceptsToken ?? 'session_token';
8183

82-
const encryptedRequestData = getHeader(req, constants.Headers.ClerkRequestData);
83-
const decryptedRequestData = decryptClerkRequestData(encryptedRequestData);
84+
if (bearerToken && isMachineToken(bearerToken)) {
85+
const tokenType = getMachineTokenType(bearerToken);
8486

85-
const options = {
86-
secretKey: opts?.secretKey || decryptedRequestData.secretKey || SECRET_KEY,
87-
publishableKey: decryptedRequestData.publishableKey || PUBLISHABLE_KEY,
88-
apiUrl: API_URL,
89-
apiVersion: API_VERSION,
90-
authStatus,
91-
authMessage,
92-
authReason,
93-
};
87+
if (!isTokenTypeAccepted(tokenType, acceptsToken)) {
88+
return unauthenticatedMachineObject(tokenType);
89+
}
9490

95-
const bearerToken = getHeader(req, constants.Headers.Authorization)?.replace('Bearer ', '');
91+
const options = {
92+
secretKey: opts?.secretKey || SECRET_KEY,
93+
publishableKey: PUBLISHABLE_KEY,
94+
apiUrl: API_URL,
95+
apiVersion: API_VERSION,
96+
};
9697

97-
// Handle machine tokens if bearer token exists and is a machine token
98-
if (bearerToken && isMachineToken(bearerToken)) {
99-
const { data, errors, tokenType } = await verifyMachineAuthToken(bearerToken, options);
98+
// TODO: Cache the result of verifyMachineAuthToken
99+
const { data, errors } = await verifyMachineAuthToken(bearerToken, options);
100100
if (errors) {
101-
return unauthenticatedMachineObject(tokenType, {
102-
reason: authReason,
103-
message: authMessage,
104-
headers: new Headers(),
105-
});
101+
return unauthenticatedMachineObject(tokenType);
106102
}
107103

108104
return authenticatedMachineObject(tokenType, bearerToken, data);
109105
}
110106

111-
// Fall back to sync version for session tokens
112107
return getAuthDataFromRequestSync(req, opts);
113108
};

packages/nextjs/src/server/protect.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
} from '@clerk/types';
1111

1212
import { constants as nextConstants } from '../constants';
13+
import { isTokenTypeAccepted } from '../server/utils';
1314
import { isNextFetcher } from './nextFetcher';
1415
import type { InferAuthObjectFromToken, InferAuthObjectFromTokenArray } from './types';
1516

@@ -121,10 +122,8 @@ export function createProtect(opts: {
121122
return notFound();
122123
};
123124

124-
// Check if the token type matches the requested token
125125
if (requestedToken) {
126-
const tokenTypes = Array.isArray(requestedToken) ? requestedToken : [requestedToken];
127-
if (!tokenTypes.includes(authObject.tokenType)) {
126+
if (!isTokenTypeAccepted(authObject.tokenType, requestedToken)) {
128127
return handleUnauthorized();
129128
}
130129
return authObject;

packages/nextjs/src/server/utils.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { AuthenticateRequestOptions, ClerkRequest, RequestState } from '@clerk/backend/internal';
1+
import type { AuthenticateRequestOptions, ClerkRequest, RequestState, TokenType } from '@clerk/backend/internal';
22
import { constants } from '@clerk/backend/internal';
33
import { isDevelopmentFromSecretKey } from '@clerk/shared/keys';
44
import { logger } from '@clerk/shared/logger';
@@ -259,3 +259,18 @@ function decryptData(data: string, key: string) {
259259
const encoded = decryptedBytes.toString(Utf8);
260260
return JSON.parse(encoded);
261261
}
262+
263+
/**
264+
* Check if a token type is accepted given a requested token type.
265+
*/
266+
export const isTokenTypeAccepted = (
267+
tokenType: TokenType,
268+
acceptsToken: NonNullable<AuthenticateRequestOptions['acceptsToken']>,
269+
): boolean => {
270+
if (acceptsToken === 'any') {
271+
return true;
272+
}
273+
274+
const tokenTypes = Array.isArray(acceptsToken) ? acceptsToken : [acceptsToken];
275+
return tokenTypes.includes(tokenType);
276+
};

0 commit comments

Comments
 (0)