feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(clerk-js): Allow token refresh when Client failed to resolve - #5345

Merged
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails
Mar 18, 2025
Merged

feat(clerk-js): Allow token refresh when Client failed to resolve#5345
panteliselef merged 27 commits into
mainfrom
elef/sdki-915-support-token-refresh-when-client-fails

Conversation

@panteliselef

@panteliselefpanteliselef commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

Description

Until now we depended on the client to be successfully fetched in order to mark clerk as initialized. This is changing in this PR.

When /client fails for any reason, a 4xx error, a 5xx error, a network error (aka blocked), we are creating a "local" only representation of the client. If the user is already signed in, clerk-js leverages the existing JWT of the __session cookie to create the client based on the existing information (userId, sessionId, orgId etc.).

With this PR, (only for standard browsers) if /client fails, clerk-js always initiates a local-only client to allow for operations like Clerk.session?.getToken() to still operate as expected. That means Clerk.loaded will be true even when we don't have a proper client object in memory.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-botBot commented Mar 13, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14af574

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
NameType
@clerk/clerk-jsPatch
@clerk/typesPatch
@clerk/chrome-extensionPatch
@clerk/clerk-expoPatch
@clerk/agent-toolkitPatch
@clerk/astroPatch
@clerk/backendPatch
@clerk/elementsPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/localizationsPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/clerk-reactPatch
@clerk/remixPatch
@clerk/sharedPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/themesPatch
@clerk/vuePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Mar 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

NameStatusPreviewCommentsUpdated (UTC)
clerk-js-sandbox✅ Ready (Inspect)Visit Preview💬 Add feedbackMar 18, 2025 8:49am

Comment on lines -186 to -191

if (isNetworkError(e)) {
return;
}

clerkCoreErrorTokenRefreshFailed(e.toString());

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to stop throwing errors on status 5xx here. These errors are not useful to us, and developers cannot catch them and handle them.

In case of an FAPI outage, the poller would continue to throw errors, that would pollute a customer's Sentry logs

export interface ClientJSON extends ClerkResourceJSON {
object: 'client';
id: string;
status: any;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is safe to remove

Comment threadpackages/types/src/jwt.ts Outdated
Comment on lines +2059 to +2075
.then(res => this.updateClient(res))
.catch(async e => {
if (isClerkAPIResponseError(e) && e.errors[0].code === 'requires_captcha') {
throw e;
}

const jwtInCookie = this.#authService?.getSessionCookie();
const localClient = createClientFromJwt(jwtInCookie);

this.updateClient(localClient);

// Always grab a fresh token
await this.session?.getToken({ skipCache: true });

// Allows for Clerk to be marked as loaded with the client and session created from the JWT.
return null;
});

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't focus too much on where this code is located, we might refactor once both this and #5287 are merged.

Comment on lines +243 to +246
external_id: string | null;
primary_email_address_id: string | null;
primary_phone_number_id: string | null;
primary_web3_wallet_id: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

image_url: string;
has_image: boolean;
username: string;
username: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

Comment on lines +264 to +265
first_name: string | null;
last_name: string | null;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

}

export interface PublicUserDataJSON extends ClerkResourceJSON {
export interface PublicUserDataJSON {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our fapi docs

…ent-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
#	packages/types/src/jwt.ts
Comment threadpackages/clerk-js/src/core/auth/cookies/session.ts Outdated
Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated
{
object: 'session',
id: sid,
status: 'active',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added in a later PR, but we now have a new claim in the JWT: sts - it's only added when a session is in a pending status

We should extract it here and default to active: sts ?? 'active' -> happy to open up another PR later to update the JWT types and apply it here as well!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in another PR sounds proper. TBH i don't think it is that important, because it might force redirect you to the appropriate page, but clerk will be down so you will not be able to proceed.

Comment threadpackages/clerk-js/src/core/jwt-client.ts Outdated

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

…sh-when-client-fails
# Conflicts:
#	packages/clerk-js/bundlewatch.config.json
@panteliselef
panteliselef merged commit 3dddcda into mainMar 18, 2025
@panteliselef
panteliselef deleted the elef/sdki-915-support-token-refresh-when-client-fails branch March 18, 2025 09:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@panteliselef@jacekradko@LauraBeatris@clerk-cookie