From 37fac81ff29b40ae83ce66f2153508386adc9985 Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Wed, 10 Jan 2024 12:51:04 +0200 Subject: [PATCH] fix(backend): Fix `getToken()` from `getAuth()` return value in v5 --- .changeset/eight-cherries-tan.md | 38 +++++++++++++++++++ .../backend/src/api/endpoints/SessionApi.ts | 10 ++--- packages/backend/src/tokens/authObjects.ts | 32 +++++++++++++++- 3 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 .changeset/eight-cherries-tan.md diff --git a/.changeset/eight-cherries-tan.md b/.changeset/eight-cherries-tan.md new file mode 100644 index 00000000000..f6852764e72 --- /dev/null +++ b/.changeset/eight-cherries-tan.md @@ -0,0 +1,38 @@ +--- +'@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 fixes issues with `getToken()` in `@clerk/nextjs` / `@clerk/remix` / `@clerk/fastify` / `@clerk/sdk-node` / `gatsby-plugin-clerk`: + +Example: +```typescript +import { getAuth } from '@clerk/nextjs/server'; + +const { getToken } = await getAuth(...); +const jwtString = await getToken(...); +``` + +The change in `SessionApi.getToken()` return value is a breaking change, to keep the existing behavior use the following: +```typescript +import { ClerkAPIResponseError } from '@clerk/shared/error'; + +const response = await clerkClient.sessions.getToken(...); + +if (response.errors) { + const { status, statusText, clerkTraceId } = response; + const error = new ClerkAPIResponseError(statusText || '', { + data: [], + status: Number(status || ''), + clerkTraceId, + }); + error.errors = response.errors; + + throw error; +} + +// the value of the v4 `clerkClient.sessions.getToken(...)` +const jwtString = response.data.jwt; +``` 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 {