Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .changeset/five-cougars-throw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
'@clerk/react': major
'@clerk/nextjs': major
'@clerk/react-router': major
'@clerk/tanstack-react-start': minor
'@clerk/shared': patch
---

`useAuth().getToken` is no longer `undefined` during server-side rendering, it is a function and calling it will throw.

* If you are only using `getToken` in `useEffect`, event handlers or with non-suspenseful data fetching libraries, no change is necessary as these only trigger on the client.
* If you are using suspenseful data fetching libraries that do trigger during SSR, you likely have strategies in place to avoid calling `getToken` already, since this has never been possible.
* If you are using `getToken === undefined` checks to avoid calling it, know that it will now throw instead and you should catch and handle the error.

```tsx
async function doThingWithToken(getToken: GetToken) {
try {
const token = await getToken();

// Use token
} catch (error) {
if (isClerkRuntimeError(error) && error.code === 'clerk_runtime_not_browser') {
// Handle error
}
}
}
```

To access auth data server-side, see the [`Auth` object reference doc](https://clerk.com/docs/reference/backend/types/auth-object).
25 changes: 25 additions & 0 deletions packages/react/src/hooks/__tests__/useAuth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ describe('useAuth', () => {
});
});

describe('useAuth.getToken', () => {
test('throws an error if getToken is called in a non-browser environment', async () => {
const originalWindow = global.window;

const { result } = renderHook(() => useAuth(), {
wrapper: ({ children }) => (
<ClerkInstanceContext.Provider value={{ value: { addListener: vi.fn() } as unknown as LoadedClerk }}>
<InitialStateProvider initialState={{} as any}>{children}</InitialStateProvider>
</ClerkInstanceContext.Provider>
),
});

// Set window to undefined to simulate non-browser environment
global.window = undefined as any;

try {
await expect(result.current.getToken()).rejects.toThrow(
'useAuth().getToken() can only be used in browser environments',
);
} finally {
global.window = originalWindow;
}
});
});

describe('useDerivedAuth', () => {
beforeEach(() => {
vi.clearAllMocks();
Expand Down
12 changes: 12 additions & 0 deletions packages/react/src/hooks/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { inBrowser } from '@clerk/shared/browser';
import { ClerkRuntimeError } from '@clerk/shared/error';

import type { IsomorphicClerk } from '../isomorphicClerk';

/**
Expand All @@ -22,6 +25,15 @@ const clerkLoaded = (isomorphicClerk: IsomorphicClerk) => {
*/
export const createGetToken = (isomorphicClerk: IsomorphicClerk) => {
return async (options: any) => {
if (!inBrowser()) {
throw new ClerkRuntimeError(
'useAuth().getToken() can only be used in browser environments. To access auth data server-side, see the Auth object reference doc: https://clerk.com/docs/reference/backend/types/auth-object',
{
code: 'clerk_runtime_not_browser',
},
);
}

await clerkLoaded(isomorphicClerk);
if (!isomorphicClerk.session) {
return null;
Expand Down
9 changes: 6 additions & 3 deletions packages/shared/src/getToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ function getWindowClerk(): LoadedClerk | undefined {

async function waitForClerk(): Promise<LoadedClerk> {
if (!inBrowser()) {
throw new ClerkRuntimeError('getToken can only be used in browser environments.', {
code: 'clerk_runtime_not_browser',
});
throw new ClerkRuntimeError(
'getToken can only be used in browser environments. To access auth data server-side, see the Auth object reference doc: https://clerk.com/docs/reference/backend/types/auth-object',
{
code: 'clerk_runtime_not_browser',
},
);
}

const clerk = getWindowClerk();
Expand Down
5 changes: 5 additions & 0 deletions packages/shared/src/types/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ export type UseAuthReturn =
signOut: SignOut;
/**
* A function that retrieves the current user's session token or a custom JWT template. Returns a promise that resolves to the token. See the [reference doc](https://clerk.com/docs/reference/javascript/session#get-token).
*
* > [!NOTE]
* > To access auth data server-side, see the [`Auth` object reference doc](https://clerk.com/docs/reference/backend/types/auth-object).
*
* @throws {ClerkRuntimeError} When called in a non-browser environment (code: `clerk_runtime_not_browser`)
*/
getToken: GetToken;
}
Expand Down
Loading