Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 470
chore(clerk-js): Improve session refresh retry logic#5397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
28880dc64b4fdf5a4f823be509e9742e7171dbce8b49200e105fb33e24c37358a4b7bfa295c0928fe0685b382a3File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| '@clerk/clerk-js': minor | ||
| --- | ||
| Improve session refresh logic. | ||
| - Switched from interval-based polling to timeout-based polling, ensuring retries for a `getToken()` call complete before the next poll begins. | ||
| - `Clerk.handleUnauthenticated()` now sets the session to null when a `/client` request returns a `500` status code, preventing infinite request loops. | ||
| - Improved error handling: If the `/client` request fails during initialization, the poller stops, a dummy client is created, a manual request to `/tokens` is attempted, and polling resumes. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@clerk/shared': minor | ||
| --- | ||
| Set `retryImmediately: false` as the default for `retry()`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1745,8 +1745,11 @@ export class Clerk implements ClerkInterface { | ||
| } | ||
| return this.setActive({ session: null }); | ||
| } catch (err) { | ||
| // Handle the 403 Forbidden | ||
| if (err.status === 403) { | ||
| // `/client` can fail with either a 401, a 403, 500 or network errors. | ||
| // 401 is already handled internally in our fetcher. | ||
| // 403 means that the client is blocked, signing out the user is the only option. | ||
| // 500 means that the client is not working, signing out the user is the only option, since the intention was to sign out the user. | ||
| if (isClerkAPIResponseError(err) && [403, 500].includes(err.status)) { | ||
Comment on lines
+1748
to
+1752
ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is the needed ? At this point the user cannot really do anything to resolve this, and clerk.js cannot auto recover. We set | ||
| return this.setActive({ session: null }); | ||
| } else { | ||
| throw err; | ||
| @@ -2148,8 +2151,20 @@ export class Clerk implements ClerkInterface { | ||
| this.updateClient(localClient); | ||
| // Always grab a fresh token | ||
| await this.session?.getToken({ skipCache: true }); | ||
| /** | ||
| * In most scenarios we want the poller to stop while we are fetching a fresh token during an outage. | ||
| * We want to avoid having the below `getToken()` retrying at the same time as the poller. | ||
| */ | ||
| this.#authService?.stopPollingForToken(); | ||
| // Attempt to grab a fresh token | ||
| await this.session | ||
| ?.getToken({ skipCache: true }) | ||
| // If the token fetch fails, let Clerk be marked as loaded and leave it up to the poller. | ||
| .catch(() => null) | ||
| .finally(() => { | ||
| this.#authService?.startPollingForToken(); | ||
| }); | ||
Comment on lines
+2154
to
+2167
ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid parallel retries from this explicit invocation and the poller. | ||
| // Allows for Clerk to be marked as loaded with the client and session created from the JWT. | ||
| return null; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -29,7 +29,7 @@ type RetryOptions = Partial<{ | ||
| /** | ||
| * Controls whether the helper should retry the operation immediately once before applying exponential backoff. | ||
| * The delay for the immediate retry is 100ms. | ||
| * @default true | ||
| * @default false | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how come we changed the default here? ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We settled on it, as the best default to have. | ||
| */ | ||
| retryImmediately: boolean; | ||
| /** | ||
| @@ -44,7 +44,7 @@ const defaultOptions: Required<RetryOptions> = { | ||
| maxDelayBetweenRetries: 0, | ||
| factor: 2, | ||
| shouldRetry: (_: unknown, iteration: number) => iteration < 5, | ||
| retryImmediately: true, | ||
| retryImmediately: false, | ||
| jitter: true, | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Credits to @nikosdouvlis