From dae043afb1f9649d63fd155d5c025689527af4d1 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 05:40:48 -0700 Subject: [PATCH 1/8] chore: handle intent based token types --- .../backend/src/__tests__/exports.test.ts | 3 +- packages/backend/src/internal.ts | 2 +- .../src/tokens/__tests__/authObjects.test.ts | 39 +++++ .../src/tokens/__tests__/machine.test.ts | 29 +++- packages/backend/src/tokens/authObjects.ts | 57 +++---- packages/backend/src/tokens/machine.ts | 12 +- packages/backend/src/tokens/request.ts | 6 +- .../__tests__/getAuthDataFromRequest.test.ts | 42 ++++- packages/nextjs/src/server/clerkMiddleware.ts | 4 +- .../src/server/data/getAuthDataFromRequest.ts | 147 ++++++++++++++---- 10 files changed, 256 insertions(+), 85 deletions(-) diff --git a/packages/backend/src/__tests__/exports.test.ts b/packages/backend/src/__tests__/exports.test.ts index a9fa7faf256..d597203dd7c 100644 --- a/packages/backend/src/__tests__/exports.test.ts +++ b/packages/backend/src/__tests__/exports.test.ts @@ -50,7 +50,8 @@ describe('subpath /internal exports', () => { "getAuthObjectForAcceptedToken", "getAuthObjectFromJwt", "getMachineTokenType", - "isMachineToken", + "isMachineTokenByPrefix", + "isMachineTokenType", "isTokenTypeAccepted", "makeAuthObjectSerializable", "reverificationError", diff --git a/packages/backend/src/internal.ts b/packages/backend/src/internal.ts index 4f176cc0489..02a86836949 100644 --- a/packages/backend/src/internal.ts +++ b/packages/backend/src/internal.ts @@ -53,4 +53,4 @@ export { reverificationError, reverificationErrorResponse } from '@clerk/shared/ export { verifyMachineAuthToken } from './tokens/verify'; -export { isMachineToken, getMachineTokenType, isTokenTypeAccepted } from './tokens/machine'; +export { isMachineTokenByPrefix, isMachineTokenType, getMachineTokenType, isTokenTypeAccepted } from './tokens/machine'; diff --git a/packages/backend/src/tokens/__tests__/authObjects.test.ts b/packages/backend/src/tokens/__tests__/authObjects.test.ts index 55b162bc025..758fa01f235 100644 --- a/packages/backend/src/tokens/__tests__/authObjects.test.ts +++ b/packages/backend/src/tokens/__tests__/authObjects.test.ts @@ -3,8 +3,10 @@ import { describe, expect, it } from 'vitest'; import { mockTokens, mockVerificationResults } from '../../fixtures/machine'; import type { AuthenticateContext } from '../authenticateContext'; +import type { SignedOutAuthObject, UnauthenticatedMachineObject } from '../authObjects'; import { authenticatedMachineObject, + getAuthObjectForAcceptedToken, makeAuthObjectSerializable, signedInAuthObject, signedOutAuthObject, @@ -387,3 +389,40 @@ describe('unauthenticatedMachineObject', () => { expect(retrievedToken).toBeNull(); }); }); + +describe('getAuthObjectForAcceptedToken', () => { + const debugData = { foo: 'bar' }; + const sessionAuth = signedOutAuthObject(debugData); + const machineAuth = authenticatedMachineObject('api_key', 'ak_xxx', mockVerificationResults.api_key, debugData); + + it('returns original object if acceptsToken is "any"', () => { + const result = getAuthObjectForAcceptedToken({ authObject: machineAuth, acceptsToken: 'any' }); + expect(result).toBe(machineAuth); + }); + + it('returns original object if token type matches', () => { + const result = getAuthObjectForAcceptedToken({ authObject: machineAuth, acceptsToken: 'api_key' }); + expect(result).toBe(machineAuth); + }); + + it('returns unauthenticated machine object for parsed type if acceptsToken is array and token type does not match', () => { + const result = getAuthObjectForAcceptedToken({ + authObject: machineAuth, + acceptsToken: ['machine_token', 'oauth_token'], + }); + expect((result as UnauthenticatedMachineObject<'api_key'>).tokenType).toBe('api_key'); + expect((result as UnauthenticatedMachineObject<'api_key'>).id).toBeNull(); + }); + + it('returns signed-out session object if parsed type is not a machine token and does not match', () => { + const result = getAuthObjectForAcceptedToken({ authObject: sessionAuth, acceptsToken: ['api_key', 'oauth_token'] }); + expect((result as SignedOutAuthObject).tokenType).toBe('session_token'); + expect((result as SignedOutAuthObject).userId).toBeNull(); + }); + + it('returns unauthenticated object for requested type if acceptsToken is a single value and does not match', () => { + const result = getAuthObjectForAcceptedToken({ authObject: machineAuth, acceptsToken: 'machine_token' }); + expect((result as UnauthenticatedMachineObject<'machine_token'>).tokenType).toBe('machine_token'); + expect((result as UnauthenticatedMachineObject<'machine_token'>).id).toBeNull(); + }); +}); diff --git a/packages/backend/src/tokens/__tests__/machine.test.ts b/packages/backend/src/tokens/__tests__/machine.test.ts index dd84e40d3bb..fcff9e566b2 100644 --- a/packages/backend/src/tokens/__tests__/machine.test.ts +++ b/packages/backend/src/tokens/__tests__/machine.test.ts @@ -3,7 +3,8 @@ import { describe, expect, it } from 'vitest'; import { API_KEY_PREFIX, getMachineTokenType, - isMachineToken, + isMachineTokenByPrefix, + isMachineTokenType, isTokenTypeAccepted, M2M_TOKEN_PREFIX, OAUTH_TOKEN_PREFIX, @@ -11,25 +12,25 @@ import { describe('isMachineToken', () => { it('returns true for tokens with M2M prefix', () => { - expect(isMachineToken(`${M2M_TOKEN_PREFIX}some-token-value`)).toBe(true); + expect(isMachineTokenByPrefix(`${M2M_TOKEN_PREFIX}some-token-value`)).toBe(true); }); it('returns true for tokens with OAuth prefix', () => { - expect(isMachineToken(`${OAUTH_TOKEN_PREFIX}some-token-value`)).toBe(true); + expect(isMachineTokenByPrefix(`${OAUTH_TOKEN_PREFIX}some-token-value`)).toBe(true); }); it('returns true for tokens with API key prefix', () => { - expect(isMachineToken(`${API_KEY_PREFIX}some-token-value`)).toBe(true); + expect(isMachineTokenByPrefix(`${API_KEY_PREFIX}some-token-value`)).toBe(true); }); it('returns false for tokens without a recognized prefix', () => { - expect(isMachineToken('unknown_prefix_token')).toBe(false); - expect(isMachineToken('session_token_value')).toBe(false); - expect(isMachineToken('jwt_token_value')).toBe(false); + expect(isMachineTokenByPrefix('unknown_prefix_token')).toBe(false); + expect(isMachineTokenByPrefix('session_token_value')).toBe(false); + expect(isMachineTokenByPrefix('jwt_token_value')).toBe(false); }); it('returns false for empty tokens', () => { - expect(isMachineToken('')).toBe(false); + expect(isMachineTokenByPrefix('')).toBe(false); }); }); @@ -78,3 +79,15 @@ describe('isTokenTypeAccepted', () => { expect(isTokenTypeAccepted('api_key', 'machine_token')).toBe(false); }); }); + +describe('isMachineTokenType', () => { + it('returns true for machine token types', () => { + expect(isMachineTokenType('api_key')).toBe(true); + expect(isMachineTokenType('machine_token')).toBe(true); + expect(isMachineTokenType('oauth_token')).toBe(true); + }); + + it('returns false for non-machine token types', () => { + expect(isMachineTokenType('session_token')).toBe(false); + }); +}); diff --git a/packages/backend/src/tokens/authObjects.ts b/packages/backend/src/tokens/authObjects.ts index a876fddbd03..4596c2578bd 100644 --- a/packages/backend/src/tokens/authObjects.ts +++ b/packages/backend/src/tokens/authObjects.ts @@ -15,6 +15,7 @@ import type { APIKey, CreateBackendApiOptions, IdPOAuthAccessToken, MachineToken import { createBackendApiClient } from '../api'; import { isTokenTypeAccepted } from '../internal'; import type { AuthenticateContext } from './authenticateContext'; +import { isMachineTokenType } from './machine'; import type { MachineTokenType, SessionTokenType } from './tokenTypes'; import { TokenType } from './tokenTypes'; import type { AuthenticateRequestOptions, MachineAuthType } from './types'; @@ -394,42 +395,13 @@ export const getAuthObjectFromJwt = ( /** * @internal - * Filters and coerces an AuthObject based on the accepted token type(s). + * Returns an auth object matching the requested token type(s). * - * This function is used after authentication to ensure that the returned auth object - * matches the expected token type(s) specified by `acceptsToken`. If the token type - * of the provided `authObject` does not match any of the types in `acceptsToken`, - * it returns an unauthenticated or signed-out version of the object, depending on the token type. + * If the parsed token type does not match any in acceptsToken, returns: + * - an unauthenticated machine object for the first machine token type in acceptsToken (if present), or + * - a signed-out session object otherwise. * - * - If `acceptsToken` is `'any'`, the original auth object is returned. - * - If `acceptsToken` is a single token type or an array of token types, the function checks if - * `authObject.tokenType` matches any of them. - * - If the token type does not match and is a session token, a signed-out object is returned. - * - If the token type does not match and is a machine token, an unauthenticated machine object is returned. - * - If the token type matches, the original auth object is returned. - * - * @param {Object} params - * @param {AuthObject} params.authObject - The authenticated object to filter. - * @param {AuthenticateRequestOptions['acceptsToken']} [params.acceptsToken=TokenType.SessionToken] - The accepted token type(s). Can be a string, array of strings, or 'any'. - * @returns {AuthObject} The filtered or coerced auth object. - * - * @example - * // Accept only 'api_key' tokens - * const authObject = { tokenType: 'session_token', userId: 'user_123' }; - * const result = getAuthObjectForAcceptedToken({ authObject, acceptsToken: 'api_key' }); - * // result will be a signed-out object (since tokenType is 'session_token' and does not match) - * - * @example - * // Accept 'api_key' or 'machine_token' - * const authObject = { tokenType: 'machine_token', id: 'm2m_123' }; - * const result = getAuthObjectForAcceptedToken({ authObject, acceptsToken: ['api_key', 'machine_token'] }); - * // result will be the original authObject (since tokenType matches one in the array) - * - * @example - * // Accept any token type - * const authObject = { tokenType: 'api_key', id: 'ak_123' }; - * const result = getAuthObjectForAcceptedToken({ authObject, acceptsToken: 'any' }); - * // result will be the original authObject + * This ensures the returned object always matches the developer's intent. */ export function getAuthObjectForAcceptedToken({ authObject, @@ -442,11 +414,22 @@ export function getAuthObjectForAcceptedToken({ return authObject; } - if (!isTokenTypeAccepted(authObject.tokenType, acceptsToken)) { - if (authObject.tokenType === TokenType.SessionToken) { + if (Array.isArray(acceptsToken)) { + if (!isTokenTypeAccepted(authObject.tokenType, acceptsToken)) { + if (isMachineTokenType(authObject.tokenType)) { + return unauthenticatedMachineObject(authObject.tokenType, authObject.debug); + } return signedOutAuthObject(authObject.debug); } - return unauthenticatedMachineObject(authObject.tokenType, authObject.debug); + return authObject; + } + + // Single value: Intent based + if (!isTokenTypeAccepted(authObject.tokenType, acceptsToken)) { + if (isMachineTokenType(acceptsToken)) { + return unauthenticatedMachineObject(acceptsToken, authObject.debug); + } + return signedOutAuthObject(authObject.debug); } return authObject; diff --git a/packages/backend/src/tokens/machine.ts b/packages/backend/src/tokens/machine.ts index 33ad67e21f9..6be97d75103 100644 --- a/packages/backend/src/tokens/machine.ts +++ b/packages/backend/src/tokens/machine.ts @@ -18,7 +18,7 @@ const MACHINE_TOKEN_PREFIXES = [M2M_TOKEN_PREFIX, OAUTH_TOKEN_PREFIX, API_KEY_PR * @param token - The token string to check * @returns true if the token starts with a recognized machine token prefix */ -export function isMachineToken(token: string): boolean { +export function isMachineTokenByPrefix(token: string): boolean { return MACHINE_TOKEN_PREFIXES.some(prefix => token.startsWith(prefix)); } @@ -67,3 +67,13 @@ export const isTokenTypeAccepted = ( const tokenTypes = Array.isArray(acceptsToken) ? acceptsToken : [acceptsToken]; return tokenTypes.includes(tokenType); }; + +/** + * Checks if a token type string is a machine token type (api_key, machine_token, or oauth_token). + * + * @param type - The token type string to check + * @returns true if the type is a machine token type + */ +export function isMachineTokenType(type: string): type is MachineTokenType { + return type === TokenType.ApiKey || type === TokenType.MachineToken || type === TokenType.OAuthToken; +} diff --git a/packages/backend/src/tokens/request.ts b/packages/backend/src/tokens/request.ts index 8b9143c1487..9b7e5f6c12a 100644 --- a/packages/backend/src/tokens/request.ts +++ b/packages/backend/src/tokens/request.ts @@ -14,7 +14,7 @@ import { AuthErrorReason, handshake, signedIn, signedOut } from './authStatus'; import { createClerkRequest } from './clerkRequest'; import { getCookieName, getCookieValue } from './cookie'; import { HandshakeService } from './handshake'; -import { getMachineTokenType, isMachineToken, isTokenTypeAccepted } from './machine'; +import { getMachineTokenType, isMachineTokenByPrefix, isTokenTypeAccepted } from './machine'; import { OrganizationMatcher } from './organizationMatcher'; import type { MachineTokenType, SessionTokenType } from './tokenTypes'; import { TokenType } from './tokenTypes'; @@ -653,7 +653,7 @@ export const authenticateRequest: AuthenticateRequest = (async ( } // Handle case where tokenType is any and the token is not a machine token - if (!isMachineToken(tokenInHeader)) { + if (!isMachineTokenByPrefix(tokenInHeader)) { return signedOut({ tokenType: acceptsToken as MachineTokenType, authenticateContext, @@ -688,7 +688,7 @@ export const authenticateRequest: AuthenticateRequest = (async ( } // Handle as a machine token - if (isMachineToken(tokenInHeader)) { + if (isMachineTokenByPrefix(tokenInHeader)) { const parsedTokenType = getMachineTokenType(tokenInHeader); const mismatchState = checkTokenTypeMismatch(parsedTokenType, acceptsToken, authenticateContext); if (mismatchState) { diff --git a/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts b/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts index 17a2d967a35..09e80594c75 100644 --- a/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts +++ b/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts @@ -42,8 +42,46 @@ describe('getAuthDataFromRequestAsync', () => { acceptsToken: 'machine_token', }); + expect(auth.tokenType).toBe('machine_token'); + expect((auth as AuthenticatedMachineObject<'machine_token'>).machineId).toBeNull(); + }); + + it('returns unauthenticated machine object for the actual parsed machine token type when token type does not match any in acceptsToken array', async () => { + const req = mockRequest({ + url: '/api/protected', + headers: new Headers({ + [constants.Headers.Authorization]: 'Bearer ak_xxx', + }), + }); + + const auth = await getAuthDataFromRequestAsync(req, { + acceptsToken: ['machine_token', 'oauth_token', 'session_token'], + }); + + expect(auth.tokenType).toBe('api_key'); + expect((auth as AuthenticatedMachineObject<'api_key'>).userId).toBeNull(); + }); + + it('returns authenticated api_key object when array contains only api_key and token is ak_xxx and verification passes', async () => { + vi.mocked(verifyMachineAuthToken).mockResolvedValueOnce({ + data: { id: 'ak_123', subject: 'user_12345' } as any, + tokenType: 'api_key', + errors: undefined, + }); + + const req = mockRequest({ + url: '/api/protected', + headers: new Headers({ + [constants.Headers.Authorization]: 'Bearer ak_xxx', + }), + }); + + const auth = await getAuthDataFromRequestAsync(req, { + acceptsToken: ['api_key'], + }); + expect(auth.tokenType).toBe('api_key'); - expect((auth as AuthenticatedMachineObject).id).toBeNull(); + expect((auth as AuthenticatedMachineObject<'api_key'>).id).toBe('ak_123'); }); it('returns authenticated machine object when token type matches', async () => { @@ -65,7 +103,7 @@ describe('getAuthDataFromRequestAsync', () => { }); expect(auth.tokenType).toBe('api_key'); - expect((auth as AuthenticatedMachineObject).id).toBe('ak_123'); + expect((auth as AuthenticatedMachineObject<'api_key'>).id).toBe('ak_123'); }); it('falls back to session token handling', async () => { diff --git a/packages/nextjs/src/server/clerkMiddleware.ts b/packages/nextjs/src/server/clerkMiddleware.ts index 9dd32a2d49b..f698399410e 100644 --- a/packages/nextjs/src/server/clerkMiddleware.ts +++ b/packages/nextjs/src/server/clerkMiddleware.ts @@ -12,7 +12,7 @@ import { constants, createClerkRequest, createRedirect, - isMachineToken, + isMachineTokenByPrefix, isTokenTypeAccepted, signedOutAuthObject, TokenType, @@ -288,7 +288,7 @@ export const clerkMiddleware = ((...args: unknown[]): NextMiddleware | NextMiddl /** * In keyless mode, if the publishable key is missing, let the request through, to render `` that will resume the flow gracefully. */ - if (isMissingPublishableKey && !isMachineToken(authHeader)) { + if (isMissingPublishableKey && !isMachineTokenByPrefix(authHeader)) { const res = NextResponse.next(); setRequestHeadersOnNextResponse(res, request, { [constants.Headers.AuthStatus]: 'signed-out', diff --git a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts index d5cd3039ceb..5f199dba3d5 100644 --- a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts +++ b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts @@ -1,13 +1,17 @@ import type { AuthObject } from '@clerk/backend'; -import type { AuthenticateRequestOptions, SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend/internal'; import { authenticatedMachineObject, + type AuthenticateRequestOptions, AuthStatus, constants, getAuthObjectFromJwt, getMachineTokenType, - isMachineToken, + isMachineTokenByPrefix, + isMachineTokenType, isTokenTypeAccepted, + type MachineTokenType, + type SignedInAuthObject, + type SignedOutAuthObject, signedOutAuthObject, TokenType, unauthenticatedMachineObject, @@ -85,46 +89,60 @@ export const getAuthDataFromRequestAsync = async ( opts: GetAuthDataFromRequestOptions = {}, ): Promise => { const { authStatus, authMessage, authReason } = getAuthHeaders(req); - opts.logger?.debug('headers', { authStatus, authMessage, authReason }); const bearerToken = getHeader(req, constants.Headers.Authorization)?.replace('Bearer ', ''); const acceptsToken = opts.acceptsToken || TokenType.SessionToken; + const options = { + secretKey: opts?.secretKey || SECRET_KEY, + publishableKey: PUBLISHABLE_KEY, + apiUrl: API_URL, + authStatus, + authMessage, + authReason, + }; - if (bearerToken && isMachineToken(bearerToken)) { - const tokenType = getMachineTokenType(bearerToken); - - const options = { - secretKey: opts?.secretKey || SECRET_KEY, - publishableKey: PUBLISHABLE_KEY, - apiUrl: API_URL, - authStatus, - authMessage, - authReason, - }; - - if (!isTokenTypeAccepted(tokenType, acceptsToken)) { - return unauthenticatedMachineObject(tokenType, options); - } - - // TODO(Rob): Cache the result of verifyMachineAuthToken - const { data, errors } = await verifyMachineAuthToken(bearerToken, options); - if (errors) { - return unauthenticatedMachineObject(tokenType, options); + if (bearerToken) { + const isMachine = isMachineTokenByPrefix(bearerToken); + const tokenType = isMachine ? getMachineTokenType(bearerToken) : undefined; + + if (Array.isArray(acceptsToken)) { + if (isMachine) { + if (!tokenType) { + return signedOutAuthObject(options); + } + return handleMachineToken({ bearerToken, tokenType, acceptsToken, options }); + } else { + return signedOutAuthObject(options); + } + } else { + let intendedType: TokenType | undefined; + if (isMachineTokenType(acceptsToken)) { + intendedType = acceptsToken; + } + const result = await handleIntentBased({ + isMachine, + tokenType, + intendedType, + bearerToken, + acceptsToken, + options, + }); + if (result) { + return result; + } } - - return authenticatedMachineObject(tokenType, bearerToken, data); } return getAuthDataFromRequestSync(req, opts); }; const getAuthHeaders = (req: RequestLike) => { - const authStatus = getAuthKeyFromRequest(req, 'AuthStatus'); - const authToken = getAuthKeyFromRequest(req, 'AuthToken'); - const authMessage = getAuthKeyFromRequest(req, 'AuthMessage'); - const authReason = getAuthKeyFromRequest(req, 'AuthReason'); - const authSignature = getAuthKeyFromRequest(req, 'AuthSignature'); + const authStatus = getAuthKeyFromRequest(req, 'AuthStatus') ?? null; + const authToken = getAuthKeyFromRequest(req, 'AuthToken') ?? null; + const authMessage = getAuthKeyFromRequest(req, 'AuthMessage') ?? null; + const authReason = getAuthKeyFromRequest(req, 'AuthReason') ?? null; + const authSignature = getAuthKeyFromRequest(req, 'AuthSignature') ?? null; return { authStatus, @@ -134,3 +152,72 @@ const getAuthHeaders = (req: RequestLike) => { authSignature, }; }; + +/** + * Handles verification and response shaping for machine tokens. + * Returns an authenticated or unauthenticated machine object based on verification and type acceptance. + */ +async function handleMachineToken({ + bearerToken, + tokenType, + acceptsToken, + options, +}: { + bearerToken: string; + tokenType: MachineTokenType; + acceptsToken: AuthenticateRequestOptions['acceptsToken']; + options: Record; +}) { + if (!tokenType) { + return signedOutAuthObject(options); + } + if (!isTokenTypeAccepted(tokenType, acceptsToken ?? TokenType.SessionToken)) { + return unauthenticatedMachineObject(tokenType, options); + } + const { data, errors } = await verifyMachineAuthToken(bearerToken, options); + if (errors) { + return unauthenticatedMachineObject(tokenType, options); + } + return authenticatedMachineObject(tokenType, bearerToken, data); +} + +/** + * Handles intent-based fallback for single-value acceptsToken. + * Returns an unauthenticated object for the intended type, or falls back to session logic if not applicable. + */ +async function handleIntentBased({ + isMachine, + tokenType, + intendedType, + bearerToken, + acceptsToken, + options, +}: { + isMachine: boolean; + tokenType: TokenType | undefined; + intendedType: TokenType | undefined; + bearerToken: string; + acceptsToken: AuthenticateRequestOptions['acceptsToken']; + options: Record; +}) { + if (isMachine) { + if (!tokenType) { + return signedOutAuthObject(options); + } + if (!isTokenTypeAccepted(tokenType, acceptsToken ?? TokenType.SessionToken)) { + if (intendedType && isMachineTokenType(intendedType)) { + return unauthenticatedMachineObject(intendedType as MachineTokenType, options); + } + return signedOutAuthObject(options); + } + const { data, errors } = await verifyMachineAuthToken(bearerToken, options); + if (errors) { + return unauthenticatedMachineObject(tokenType as MachineTokenType, options); + } + return authenticatedMachineObject(tokenType as MachineTokenType, bearerToken, data); + } else if (intendedType && isMachineTokenType(intendedType)) { + return unauthenticatedMachineObject(intendedType, options); + } + // else: fall through to session logic + return null; +} From 0d9d7aa621c5ab512db40746fcb003e9a366e3b0 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 05:41:38 -0700 Subject: [PATCH 2/8] chore: keep header return --- .../nextjs/src/server/data/getAuthDataFromRequest.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts index 5f199dba3d5..771498245ee 100644 --- a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts +++ b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts @@ -138,11 +138,11 @@ export const getAuthDataFromRequestAsync = async ( }; const getAuthHeaders = (req: RequestLike) => { - const authStatus = getAuthKeyFromRequest(req, 'AuthStatus') ?? null; - const authToken = getAuthKeyFromRequest(req, 'AuthToken') ?? null; - const authMessage = getAuthKeyFromRequest(req, 'AuthMessage') ?? null; - const authReason = getAuthKeyFromRequest(req, 'AuthReason') ?? null; - const authSignature = getAuthKeyFromRequest(req, 'AuthSignature') ?? null; + const authStatus = getAuthKeyFromRequest(req, 'AuthStatus'); + const authToken = getAuthKeyFromRequest(req, 'AuthToken'); + const authMessage = getAuthKeyFromRequest(req, 'AuthMessage'); + const authReason = getAuthKeyFromRequest(req, 'AuthReason'); + const authSignature = getAuthKeyFromRequest(req, 'AuthSignature'); return { authStatus, From 9993f00fa9d77923dfd9fc461c3596c852a8f9ef Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 06:11:56 -0700 Subject: [PATCH 3/8] fix tests and add changeset --- .changeset/open-swans-count.md | 6 ++++++ packages/express/src/__tests__/getAuth.test.ts | 2 +- packages/fastify/src/__tests__/getAuth.test.ts | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/open-swans-count.md diff --git a/.changeset/open-swans-count.md b/.changeset/open-swans-count.md new file mode 100644 index 00000000000..17fa978acfa --- /dev/null +++ b/.changeset/open-swans-count.md @@ -0,0 +1,6 @@ +--- +'@clerk/backend': patch +'@clerk/nextjs': patch +--- + +Respect `acceptsToken` when returning unauthenticated session or machine object. diff --git a/packages/express/src/__tests__/getAuth.test.ts b/packages/express/src/__tests__/getAuth.test.ts index 7ce9acda9ee..382d5648312 100644 --- a/packages/express/src/__tests__/getAuth.test.ts +++ b/packages/express/src/__tests__/getAuth.test.ts @@ -40,7 +40,7 @@ describe('getAuth', () => { it('returns an unauthenticated auth object when the tokenType does not match acceptsToken', () => { const req = mockRequestWithAuth({ tokenType: 'session_token', userId: 'user_12345' }); const result = getAuth(req, { acceptsToken: 'api_key' }); - expect(result.tokenType).toBe('session_token'); // reflects the actual token found + expect(result.tokenType).toBe('api_key'); // reflects the actual token found // Properties specific to authenticated objects should be null or undefined expect(result.userId).toBeNull(); expect(result.orgId).toBeNull(); diff --git a/packages/fastify/src/__tests__/getAuth.test.ts b/packages/fastify/src/__tests__/getAuth.test.ts index 0c9f94a56dd..124ca033219 100644 --- a/packages/fastify/src/__tests__/getAuth.test.ts +++ b/packages/fastify/src/__tests__/getAuth.test.ts @@ -37,7 +37,7 @@ describe('getAuth(req)', () => { it('returns an unauthenticated auth object when the tokenType does not match acceptsToken', () => { const req = { auth: { tokenType: 'session_token', userId: 'user_12345' } } as unknown as FastifyRequest; const result = getAuth(req, { acceptsToken: 'api_key' }); - expect(result.tokenType).toBe('session_token'); // reflects the actual token found + expect(result.tokenType).toBe('api_key'); // reflects the actual token found expect(result.userId).toBeNull(); expect(result.orgId).toBeNull(); }); From cdf5ec8c87af0fe3e6282c9a4ec91b42a9265d2c Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 11:11:14 -0700 Subject: [PATCH 4/8] chore: add isAuthenticated property to auth object --- .changeset/open-swans-count.md | 4 ++-- packages/backend/src/tokens/authObjects.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.changeset/open-swans-count.md b/.changeset/open-swans-count.md index 17fa978acfa..bc87ae0de0f 100644 --- a/.changeset/open-swans-count.md +++ b/.changeset/open-swans-count.md @@ -1,6 +1,6 @@ --- -'@clerk/backend': patch -'@clerk/nextjs': patch +'@clerk/backend': minor +'@clerk/nextjs': minor --- Respect `acceptsToken` when returning unauthenticated session or machine object. diff --git a/packages/backend/src/tokens/authObjects.ts b/packages/backend/src/tokens/authObjects.ts index 4596c2578bd..c3be389bc66 100644 --- a/packages/backend/src/tokens/authObjects.ts +++ b/packages/backend/src/tokens/authObjects.ts @@ -58,6 +58,7 @@ export type SignedInAuthObject = SharedSignedInAuthObjectProperties & { * Used to help debug issues when using Clerk in development. */ debug: AuthObjectDebug; + isAuthenticated: true; }; /** @@ -78,6 +79,7 @@ export type SignedOutAuthObject = { getToken: ServerGetToken; has: CheckAuthorizationFromSessionClaims; debug: AuthObjectDebug; + isAuthenticated: false; }; /** @@ -121,6 +123,7 @@ export type AuthenticatedMachineObject[T] : never; @@ -141,6 +144,7 @@ export type UnauthenticatedMachineObject[T] : never; @@ -201,6 +205,7 @@ export function signedInAuthObject( plans: (sessionClaims.pla as string) || '', }), debug: createDebug({ ...authenticateContext, sessionToken }), + isAuthenticated: true, }; } @@ -226,6 +231,7 @@ export function signedOutAuthObject( getToken: () => Promise.resolve(null), has: () => false, debug: createDebug(debugData), + isAuthenticated: false, }; } @@ -244,6 +250,7 @@ export function authenticatedMachineObject( getToken: () => Promise.resolve(token), has: () => false, debug: createDebug(debugData), + isAuthenticated: true, }; // Type assertions are safe here since we know the verification result type matches the tokenType. @@ -303,6 +310,7 @@ export function unauthenticatedMachineObject( has: () => false, getToken: () => Promise.resolve(null), debug: createDebug(debugData), + isAuthenticated: false, }; switch (tokenType) { From a864c05a1ebe45666648bdc1034b8b96e78b35c7 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 13:02:07 -0700 Subject: [PATCH 5/8] chore: add invalid token auth object --- .../backend/src/__tests__/exports.test.ts | 1 + packages/backend/src/index.ts | 2 +- packages/backend/src/internal.ts | 1 + .../src/tokens/__tests__/authObjects.test.ts | 18 +++++++---- .../src/tokens/__tests__/getAuth.test-d.ts | 6 ++-- packages/backend/src/tokens/authObjects.ts | 30 +++++++++++++++---- packages/backend/src/tokens/machine.ts | 8 +++-- packages/backend/src/tokens/types.ts | 4 ++- packages/nextjs/src/app-router/server/auth.ts | 13 ++++---- packages/nextjs/src/server/protect.ts | 4 +++ 10 files changed, 63 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/__tests__/exports.test.ts b/packages/backend/src/__tests__/exports.test.ts index d597203dd7c..c9709c61722 100644 --- a/packages/backend/src/__tests__/exports.test.ts +++ b/packages/backend/src/__tests__/exports.test.ts @@ -50,6 +50,7 @@ describe('subpath /internal exports', () => { "getAuthObjectForAcceptedToken", "getAuthObjectFromJwt", "getMachineTokenType", + "invalidTokenAuthObject", "isMachineTokenByPrefix", "isMachineTokenType", "isTokenTypeAccepted", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 2b33d227388..78ec3aab09a 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -161,5 +161,5 @@ export type { /** * Auth objects */ -export type { AuthObject } from './tokens/authObjects'; +export type { AuthObject, InvalidTokenAuthObject } from './tokens/authObjects'; export type { SessionAuthObject, MachineAuthObject } from './tokens/types'; diff --git a/packages/backend/src/internal.ts b/packages/backend/src/internal.ts index 02a86836949..614bd13443e 100644 --- a/packages/backend/src/internal.ts +++ b/packages/backend/src/internal.ts @@ -31,6 +31,7 @@ export { signedInAuthObject, authenticatedMachineObject, unauthenticatedMachineObject, + invalidTokenAuthObject, getAuthObjectFromJwt, getAuthObjectForAcceptedToken, } from './tokens/authObjects'; diff --git a/packages/backend/src/tokens/__tests__/authObjects.test.ts b/packages/backend/src/tokens/__tests__/authObjects.test.ts index 758fa01f235..b521e82a8b4 100644 --- a/packages/backend/src/tokens/__tests__/authObjects.test.ts +++ b/packages/backend/src/tokens/__tests__/authObjects.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import { mockTokens, mockVerificationResults } from '../../fixtures/machine'; import type { AuthenticateContext } from '../authenticateContext'; -import type { SignedOutAuthObject, UnauthenticatedMachineObject } from '../authObjects'; +import type { InvalidTokenAuthObject, UnauthenticatedMachineObject } from '../authObjects'; import { authenticatedMachineObject, getAuthObjectForAcceptedToken, @@ -405,19 +405,25 @@ describe('getAuthObjectForAcceptedToken', () => { expect(result).toBe(machineAuth); }); - it('returns unauthenticated machine object for parsed type if acceptsToken is array and token type does not match', () => { + it('returns InvalidTokenAuthObject if acceptsToken is array and token type does not match', () => { const result = getAuthObjectForAcceptedToken({ authObject: machineAuth, acceptsToken: ['machine_token', 'oauth_token'], }); - expect((result as UnauthenticatedMachineObject<'api_key'>).tokenType).toBe('api_key'); - expect((result as UnauthenticatedMachineObject<'api_key'>).id).toBeNull(); + expect((result as InvalidTokenAuthObject).tokenType).toBeNull(); + expect((result as InvalidTokenAuthObject).isAuthenticated).toBe(false); + }); + + it('returns InvalidTokenAuthObject if parsed type is not a machine token and does not match any in acceptsToken array', () => { + const result = getAuthObjectForAcceptedToken({ authObject: sessionAuth, acceptsToken: ['api_key', 'oauth_token'] }); + expect((result as InvalidTokenAuthObject).tokenType).toBeNull(); + expect((result as InvalidTokenAuthObject).isAuthenticated).toBe(false); }); it('returns signed-out session object if parsed type is not a machine token and does not match', () => { const result = getAuthObjectForAcceptedToken({ authObject: sessionAuth, acceptsToken: ['api_key', 'oauth_token'] }); - expect((result as SignedOutAuthObject).tokenType).toBe('session_token'); - expect((result as SignedOutAuthObject).userId).toBeNull(); + expect((result as InvalidTokenAuthObject).tokenType).toBeNull(); + expect((result as InvalidTokenAuthObject).isAuthenticated).toBe(false); }); it('returns unauthenticated object for requested type if acceptsToken is a single value and does not match', () => { diff --git a/packages/backend/src/tokens/__tests__/getAuth.test-d.ts b/packages/backend/src/tokens/__tests__/getAuth.test-d.ts index b0417b9b094..13e1a8873e7 100644 --- a/packages/backend/src/tokens/__tests__/getAuth.test-d.ts +++ b/packages/backend/src/tokens/__tests__/getAuth.test-d.ts @@ -1,6 +1,6 @@ import { expectTypeOf, test } from 'vitest'; -import type { AuthObject } from '../authObjects'; +import type { AuthObject, InvalidTokenAuthObject } from '../authObjects'; import type { GetAuthFn, MachineAuthObject, SessionAuthObject } from '../types'; // Across our SDKs, we have a getAuth() function @@ -22,10 +22,10 @@ test('infers the correct AuthObject type for each accepted token type', () => { // Array of token types expectTypeOf(getAuth(request, { acceptsToken: ['session_token', 'machine_token'] })).toMatchTypeOf< - SessionAuthObject | MachineAuthObject<'machine_token'> + SessionAuthObject | MachineAuthObject<'machine_token'> | InvalidTokenAuthObject >(); expectTypeOf(getAuth(request, { acceptsToken: ['machine_token', 'oauth_token'] })).toMatchTypeOf< - MachineAuthObject<'machine_token' | 'oauth_token'> + MachineAuthObject<'machine_token' | 'oauth_token'> | InvalidTokenAuthObject >(); // Any token type diff --git a/packages/backend/src/tokens/authObjects.ts b/packages/backend/src/tokens/authObjects.ts index c3be389bc66..cadd4166476 100644 --- a/packages/backend/src/tokens/authObjects.ts +++ b/packages/backend/src/tokens/authObjects.ts @@ -148,6 +148,14 @@ export type UnauthenticatedMachineObject[T] : never; +export type InvalidTokenAuthObject = { + isAuthenticated: false; + tokenType: null; + getToken: () => Promise; + has: () => false; + debug: AuthObjectDebug; +}; + /** * @interface */ @@ -155,7 +163,8 @@ export type AuthObject = | SignedInAuthObject | SignedOutAuthObject | AuthenticatedMachineObject - | UnauthenticatedMachineObject; + | UnauthenticatedMachineObject + | InvalidTokenAuthObject; const createDebug = (data: AuthObjectDebugData | undefined) => { return () => { @@ -349,6 +358,19 @@ export function unauthenticatedMachineObject( } } +/** + * @internal + */ +export function invalidTokenAuthObject(): InvalidTokenAuthObject { + return { + isAuthenticated: false, + tokenType: null, + getToken: () => Promise.resolve(null), + has: () => false, + debug: () => ({}), + }; +} + /** * Auth objects moving through the server -> client boundary need to be serializable * as we need to ensure that they can be transferred via the network as pure strings. @@ -424,10 +446,8 @@ export function getAuthObjectForAcceptedToken({ if (Array.isArray(acceptsToken)) { if (!isTokenTypeAccepted(authObject.tokenType, acceptsToken)) { - if (isMachineTokenType(authObject.tokenType)) { - return unauthenticatedMachineObject(authObject.tokenType, authObject.debug); - } - return signedOutAuthObject(authObject.debug); + // If the token is not in the accepted array, return invalidTokenAuthObject + return invalidTokenAuthObject(); } return authObject; } diff --git a/packages/backend/src/tokens/machine.ts b/packages/backend/src/tokens/machine.ts index 6be97d75103..17df764a429 100644 --- a/packages/backend/src/tokens/machine.ts +++ b/packages/backend/src/tokens/machine.ts @@ -52,14 +52,18 @@ export function getMachineTokenType(token: string): MachineTokenType { /** * Check if a token type is accepted given a requested token type or list of token types. * - * @param tokenType - The token type to check + * @param tokenType - The token type to check (can be null if the token is invalid) * @param acceptsToken - The requested token type or list of token types * @returns true if the token type is accepted */ export const isTokenTypeAccepted = ( - tokenType: TokenType, + tokenType: TokenType | null, acceptsToken: NonNullable, ): boolean => { + if (!tokenType) { + return false; + } + if (acceptsToken === 'any') { return true; } diff --git a/packages/backend/src/tokens/types.ts b/packages/backend/src/tokens/types.ts index badda7d18f3..2b95dfb6c23 100644 --- a/packages/backend/src/tokens/types.ts +++ b/packages/backend/src/tokens/types.ts @@ -5,6 +5,7 @@ import type { ApiClient, APIKey, IdPOAuthAccessToken, MachineToken } from '../ap import type { AuthenticatedMachineObject, AuthObject, + InvalidTokenAuthObject, SignedInAuthObject, SignedOutAuthObject, UnauthenticatedMachineObject, @@ -200,7 +201,8 @@ export interface GetAuthFn req: RequestType, options: AuthOptions & { acceptsToken: T }, ): MaybePromise< - InferAuthObjectFromTokenArray>>, + | InferAuthObjectFromTokenArray>> + | InvalidTokenAuthObject, ReturnsPromise >; diff --git a/packages/nextjs/src/app-router/server/auth.ts b/packages/nextjs/src/app-router/server/auth.ts index ec5b151f7ee..cd5ce02c81d 100644 --- a/packages/nextjs/src/app-router/server/auth.ts +++ b/packages/nextjs/src/app-router/server/auth.ts @@ -1,4 +1,4 @@ -import type { AuthObject, MachineAuthObject, SessionAuthObject } from '@clerk/backend'; +import type { AuthObject, InvalidTokenAuthObject, MachineAuthObject, SessionAuthObject } from '@clerk/backend'; import type { AuthenticateRequestOptions, InferAuthObjectFromToken, @@ -56,11 +56,12 @@ export interface AuthFn> { ( options: AuthOptions & { acceptsToken: T }, ): Promise< - InferAuthObjectFromTokenArray< - T, - SessionAuthWithRedirect, - MachineAuthObject> - > + | InferAuthObjectFromTokenArray< + T, + SessionAuthWithRedirect, + MachineAuthObject> + > + | InvalidTokenAuthObject >; /** diff --git a/packages/nextjs/src/server/protect.ts b/packages/nextjs/src/server/protect.ts index cfb0dea5628..f74918893a8 100644 --- a/packages/nextjs/src/server/protect.ts +++ b/packages/nextjs/src/server/protect.ts @@ -145,6 +145,10 @@ export function createProtect(opts: { return handleUnauthorized(); } + if (authObject.tokenType === null) { + return handleUnauthorized(); + } + if (authObject.tokenType !== TokenType.SessionToken) { // For machine tokens, we only check if they're authenticated // They don't have session status or organization permissions From fc025c0084ae19e64eb5f709f6b004f4581768f1 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 13:10:59 -0700 Subject: [PATCH 6/8] chore: adjustments --- packages/backend/src/tokens/authObjects.ts | 2 +- .../server/__tests__/getAuthDataFromRequest.test.ts | 6 +++--- .../nextjs/src/server/data/getAuthDataFromRequest.ts | 11 ++++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/tokens/authObjects.ts b/packages/backend/src/tokens/authObjects.ts index cadd4166476..a1ec6892b0e 100644 --- a/packages/backend/src/tokens/authObjects.ts +++ b/packages/backend/src/tokens/authObjects.ts @@ -446,7 +446,7 @@ export function getAuthObjectForAcceptedToken({ if (Array.isArray(acceptsToken)) { if (!isTokenTypeAccepted(authObject.tokenType, acceptsToken)) { - // If the token is not in the accepted array, return invalidTokenAuthObject + // If the token is not in the accepted array, return invalid token auth object return invalidTokenAuthObject(); } return authObject; diff --git a/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts b/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts index 09e80594c75..3f17613ba50 100644 --- a/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts +++ b/packages/nextjs/src/server/__tests__/getAuthDataFromRequest.test.ts @@ -46,7 +46,7 @@ describe('getAuthDataFromRequestAsync', () => { expect((auth as AuthenticatedMachineObject<'machine_token'>).machineId).toBeNull(); }); - it('returns unauthenticated machine object for the actual parsed machine token type when token type does not match any in acceptsToken array', async () => { + it('returns invalid token auth object when token type does not match any in acceptsToken array', async () => { const req = mockRequest({ url: '/api/protected', headers: new Headers({ @@ -58,8 +58,8 @@ describe('getAuthDataFromRequestAsync', () => { acceptsToken: ['machine_token', 'oauth_token', 'session_token'], }); - expect(auth.tokenType).toBe('api_key'); - expect((auth as AuthenticatedMachineObject<'api_key'>).userId).toBeNull(); + expect(auth.tokenType).toBeNull(); + expect(auth.isAuthenticated).toBe(false); }); it('returns authenticated api_key object when array contains only api_key and token is ak_xxx and verification passes', async () => { diff --git a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts index 771498245ee..7379a03a7cd 100644 --- a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts +++ b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts @@ -6,6 +6,7 @@ import { constants, getAuthObjectFromJwt, getMachineTokenType, + invalidTokenAuthObject, isMachineTokenByPrefix, isMachineTokenType, isTokenTypeAccepted, @@ -168,9 +169,13 @@ async function handleMachineToken({ acceptsToken: AuthenticateRequestOptions['acceptsToken']; options: Record; }) { - if (!tokenType) { - return signedOutAuthObject(options); + if (Array.isArray(acceptsToken)) { + // If the token is not in the accepted array, return invalid token auth object + if (!isTokenTypeAccepted(tokenType, acceptsToken)) { + return invalidTokenAuthObject(); + } } + if (!isTokenTypeAccepted(tokenType, acceptsToken ?? TokenType.SessionToken)) { return unauthenticatedMachineObject(tokenType, options); } @@ -206,7 +211,7 @@ async function handleIntentBased({ } if (!isTokenTypeAccepted(tokenType, acceptsToken ?? TokenType.SessionToken)) { if (intendedType && isMachineTokenType(intendedType)) { - return unauthenticatedMachineObject(intendedType as MachineTokenType, options); + return unauthenticatedMachineObject(intendedType, options); } return signedOutAuthObject(options); } From d01f40d8bcd9e6400b730c7f36a5e0a4e450228c Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 13:28:16 -0700 Subject: [PATCH 7/8] chore: adjustments --- .../src/server/data/getAuthDataFromRequest.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts index 7379a03a7cd..6e7bfb40814 100644 --- a/packages/nextjs/src/server/data/getAuthDataFromRequest.ts +++ b/packages/nextjs/src/server/data/getAuthDataFromRequest.ts @@ -109,12 +109,12 @@ export const getAuthDataFromRequestAsync = async ( if (Array.isArray(acceptsToken)) { if (isMachine) { - if (!tokenType) { - return signedOutAuthObject(options); - } - return handleMachineToken({ bearerToken, tokenType, acceptsToken, options }); - } else { - return signedOutAuthObject(options); + return handleMachineToken({ + bearerToken, + tokenType: tokenType as MachineTokenType, + acceptsToken, + options, + }); } } else { let intendedType: TokenType | undefined; @@ -135,6 +135,13 @@ export const getAuthDataFromRequestAsync = async ( } } + if (Array.isArray(acceptsToken)) { + if (!isTokenTypeAccepted(TokenType.SessionToken, acceptsToken)) { + return invalidTokenAuthObject(); + } + } + + // Fall through to session logic return getAuthDataFromRequestSync(req, opts); }; From e672cd3ac0869aedd4534b58a1cfde71f2111b83 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 13 Jun 2025 13:33:38 -0700 Subject: [PATCH 8/8] fix test types --- packages/express/src/__tests__/getAuth.test.ts | 7 +++++-- packages/fastify/src/__tests__/getAuth.test.ts | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/express/src/__tests__/getAuth.test.ts b/packages/express/src/__tests__/getAuth.test.ts index 382d5648312..750994777a4 100644 --- a/packages/express/src/__tests__/getAuth.test.ts +++ b/packages/express/src/__tests__/getAuth.test.ts @@ -1,3 +1,5 @@ +import type { AuthenticatedMachineObject } from '@clerk/backend/internal'; + import { getAuth } from '../getAuth'; import { mockRequest, mockRequestWithAuth } from './helpers'; @@ -33,8 +35,9 @@ describe('getAuth', () => { const req = mockRequestWithAuth({ tokenType: 'machine_token', id: 'm2m_1234' }); const result = getAuth(req, { acceptsToken: ['machine_token', 'api_key'] }); expect(result.tokenType).toBe('machine_token'); - expect(result.id).toBe('m2m_1234'); - expect(result.subject).toBeUndefined(); + + expect((result as AuthenticatedMachineObject<'machine_token'>).id).toBe('m2m_1234'); + expect((result as AuthenticatedMachineObject<'machine_token'>).subject).toBeUndefined(); }); it('returns an unauthenticated auth object when the tokenType does not match acceptsToken', () => { diff --git a/packages/fastify/src/__tests__/getAuth.test.ts b/packages/fastify/src/__tests__/getAuth.test.ts index 124ca033219..9d5dfe8f660 100644 --- a/packages/fastify/src/__tests__/getAuth.test.ts +++ b/packages/fastify/src/__tests__/getAuth.test.ts @@ -1,3 +1,4 @@ +import type { AuthenticatedMachineObject } from '@clerk/backend/internal'; import type { FastifyRequest } from 'fastify'; import { getAuth } from '../getAuth'; @@ -30,8 +31,8 @@ describe('getAuth(req)', () => { const req = { auth: { tokenType: 'machine_token', id: 'm2m_1234' } } as unknown as FastifyRequest; const result = getAuth(req, { acceptsToken: ['machine_token', 'api_key'] }); expect(result.tokenType).toBe('machine_token'); - expect(result.id).toBe('m2m_1234'); - expect(result.subject).toBeUndefined(); + expect((result as AuthenticatedMachineObject<'machine_token'>).id).toBe('m2m_1234'); + expect((result as AuthenticatedMachineObject<'machine_token'>).subject).toBeUndefined(); }); it('returns an unauthenticated auth object when the tokenType does not match acceptsToken', () => { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy