-
Notifications
You must be signed in to change notification settings - Fork 418
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
Changes from all commits
28880dc
64b4fdf
5a4f823
be509e9
742e717
1dbce8b
49200e1
05fb33e
24c3735
8a4b7bf
a295c09
28fe068
5b382a3
File filter
Filter by extension
Conversations
Jump to
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
Contributor
Author
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
Contributor
Author
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?
Contributor
Author
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