diff --git a/.changeset/eight-cherries-tan.md b/.changeset/eight-cherries-tan.md new file mode 100644 index 00000000000..2b23d95bc04 --- /dev/null +++ b/.changeset/eight-cherries-tan.md @@ -0,0 +1,16 @@ +--- +'@clerk/backend': major +--- + +Change `SessionApi.getToken()` to return consistent `{ data, errors }` return value +and fix the `getToken()` from requestState to have the same return behavior as v4 +(return Promise or throw error). +This change will fix issues in `@clerk/nextjs` / `@clerk/remix` with `getToken()`: + +Example: +```typescript +import { getAuth } from '@clerk/remix'; + +const { getToken } = getAuth(...); +const jwtString = await getToken(); +``` diff --git a/packages/backend/src/api/endpoints/SessionApi.ts b/packages/backend/src/api/endpoints/SessionApi.ts index 770415d79e5..0daaa0f84e5 100644 --- a/packages/backend/src/api/endpoints/SessionApi.ts +++ b/packages/backend/src/api/endpoints/SessionApi.ts @@ -49,11 +49,9 @@ export class SessionAPI extends AbstractAPI { public async getToken(sessionId: string, template: string) { this.requireId(sessionId); - return ( - (await this.request({ - method: 'POST', - path: joinPaths(basePath, sessionId, 'tokens', template || ''), - })) as any - ).jwt; + return this.request({ + method: 'POST', + path: joinPaths(basePath, sessionId, 'tokens', template || ''), + }); } } diff --git a/packages/backend/src/tokens/authObjects.ts b/packages/backend/src/tokens/authObjects.ts index 549b29601d2..d2a4daffe9e 100644 --- a/packages/backend/src/tokens/authObjects.ts +++ b/packages/backend/src/tokens/authObjects.ts @@ -1,3 +1,4 @@ +import { ClerkAPIResponseError } from '@clerk/shared/error'; import type { ActClaim, CheckAuthorizationWithCustomPermissions, @@ -10,6 +11,7 @@ import type { import type { CreateBackendApiOptions } from '../api'; import { createBackendApiClient } from '../api'; +import type { ClerkBackendApiResponse } from '../api/request'; import type { AuthenticateContext } from './authenticateContext'; type AuthObjectDebugData = Record; @@ -73,6 +75,27 @@ const createDebug = (data: AuthObjectDebugData | undefined) => { }; }; +// This helper is introduced as compat layer between the v4 and v5 implementations to keep the +// exposed top-level getToken API the same since it's critical and there are already a lot of +// breaking changes. +// TODO: Revamp AuthObject `getToken()` to return { data, errors } in next major version +const throwResponseErrors = (response: ClerkBackendApiResponse): never => { + // used to by-pass type-safety for the `{ status, statusText, clerkTraceId } = response` line below + if (!response.errors) { + throw new Error('no error to throw'); + } + + const { status, statusText, clerkTraceId } = response; + const error = new ClerkAPIResponseError(statusText || '', { + data: [], + status: Number(status || ''), + clerkTraceId, + }); + error.errors = response.errors; + + throw error; +}; + /** * @internal */ @@ -93,7 +116,14 @@ export function signedInAuthObject( const getToken = createGetToken({ sessionId, sessionToken: authenticateContext.sessionToken || '', - fetcher: (...args) => apiClient.sessions.getToken(...args), + fetcher: async (...args) => { + const response = await apiClient.sessions.getToken(...args); + if (response.errors) { + return throwResponseErrors(response); + } + + return response.data.jwt; + }, }); return {