feat(clerk-js): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@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): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@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): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@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): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@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): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@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): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@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): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@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): Handle new session pending status as authenticated state - #5136

Merged
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated
Feb 18, 2025
Merged

feat(clerk-js): Handle new session pending status as authenticated state#5136
LauraBeatris merged 17 commits into
mainfrom
laura/orgs-544-sdk-handle-pending-session-status-as-authenticated

Conversation

@LauraBeatris

@LauraBeatrisLauraBeatris commented Feb 11, 2025

Copy link
Copy Markdown
Contributor

Description

Context

Introducing a new FAPI session status: pending. It builds a fundamental layer for the after-auth project and the future concept of tasks, eg: Forcing to select an organization after sign-in.

Previously, only active sessions were considered to be in an authenticated state. Now, we're introducing a new status assigned to the user session after a successful authentication process but when there are pending tasks.

Next steps

These changes do not introduce new behavior on helpers / AIO components, neither breaking changes, as it handles pending as an authenticated state, pairing the same functionality as active

Next PRs will start introducing the concept of pending tasks and enforcing resolution upon after-auth.

Developer-facing changes

Once these clerk-js get served, developers shouldn't have to worry about changing their app logic or breaking changes. However, some interface changes are preparing the DX for the next steps mentioned above:

Unifying an signed-in state check based on the session status with Clerk.isSignedIn or Clerk.client.isSignedIn

For custom flows, Clerk.user shouldn't be used to determine if the user has fully authenticated or not, and also, it shouldn't be necessary for developers to explicitly check against the session as it's prone for breaking changes, therefore we're abstracting this behind a new property.

- if (Clerk.session.status === 'active') {+ if (Clerk.isSignedIn) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
const userbuttonDiv = document.getElementById('user-button')
clerk.mountUserButton(userbuttonDiv)
} else {

Deprecating activeSessions in favor of signedInSessions

Deprecating explicit checks against "active" sessions in favor of a generic property that expresses the "signed-in" state instead, since in the future, we might add other types of session statuses for different levels of user verification as we're doing now with after-auth.

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 Feb 11, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e69c175

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

This PR includes changesets to release 23 packages
NameType
@clerk/elementsMinor
@clerk/sharedMinor
@clerk/astroMinor
@clerk/clerk-reactMinor
@clerk/typesMinor
@clerk/clerk-expoMinor
@clerk/vueMinor
@clerk/clerk-jsMinor
@clerk/uiPatch
@clerk/agent-toolkitPatch
@clerk/backendPatch
@clerk/chrome-extensionPatch
@clerk/expo-passkeysPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/remixPatch
@clerk/tanstack-startPatch
@clerk/testingPatch
@clerk/localizationsPatch
@clerk/themesPatch

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 Feb 11, 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 feedbackFeb 18, 2025 7:07pm

@LauraBeatrisLauraBeatris changed the title [wip] Handle new pending session status as authenticated user[wip] Introduce new pending statusFeb 11, 2025
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 4b907c1 to cdee2c9CompareFebruary 11, 2025 20:06
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from cdee2c9 to a9143d3CompareFebruary 11, 2025 21:11
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from a9143d3 to f21d6e7CompareFebruary 11, 2025 21:13
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f21d6e7 to b3044d2CompareFebruary 11, 2025 21:23
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from b3044d2 to 96a7629CompareFebruary 11, 2025 21:36
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 96a7629 to f249b85CompareFebruary 11, 2025 21:46
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from f249b85 to 37f3c34CompareFebruary 11, 2025 21:49
@LauraBeatris
LauraBeatrisforce-pushed the laura/orgs-544-sdk-handle-pending-session-status-as-authenticated branch from 37f3c34 to f51ee21CompareFebruary 11, 2025 21:56
Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

I propose Clerk.isAuthenticated, i don't like having "client" in the name, since as a developer using Clerk that term is unfamiliar to me (compared to user and session).

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 agree! I tried that out first but was on the fence about whether to mention "client"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use isSignedIn elsewhere, I would say we stick with that unless we feel strongly that isAuthenticated() is a better representation.

@LauraBeatrisLauraBeatrisFeb 14, 2025

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 quite like that! Here's a comparison of how it'd play out with the current SignedIn control component vs a custom flow

<SignedIn><p> I have successfully signed up but I still have pending tasks </p></SignedIn>
if(!Clerk.isSignedIn){mountSignIn()}else{if(Clerk.session.tasks){// ...}}

I think the same nomenclature should be extended to the session arrays, so would be something like: signedInSessions

return this.sessions.filter(s => s.status === 'active') as ActiveSessionResource[];
}

get authenticatedSessions(): AuthenticatedSessionResource[] {

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.

Curious, would authenticatedSessions be in conflict with sessions from an anonymous login ? Are you authenticated when you use anonymous login ? (thinking out loud here, you can ignore)

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.

Are you authenticated when you use anonymous login ?

I'd expect not, cause the user haven't gone through "identity verification" in a way

Comment thread.changeset/proud-cycles-roll.md Outdated

```diff
- if (Clerk.user) {
+ if (clerk.hasAuthenticatedClient) {

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.

Suggested change
+ if (clerk.hasAuthenticatedClient) {
+ if (Clerk.hasAuthenticatedClient) {

Comment threadpackages/clerk-js/src/core/clerk.ts Outdated
return this.#options[key];
}

get hasAuthenticatedClient(): boolean {

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.

The goal of this property is to deprecate/do not recommend the use of Clerk.user for custom flows to determine the authenticate state

Is this custom flows only or in their codebase overall ?

return this._basePostBypass({ body: params, path: this.path() + '/verify' });
}

get hasAuthenticated() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would recommend present tense instead of past-tense:

Suggested change
gethasAuthenticated(){
getisAuthenticated(){

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.

I agree with Bryce here, present tense seems a bit better

Comment on lines +126 to +127
const currentSessionToken = client.authenticatedSessions.find(s => s.id === client.lastActiveSessionId);
const token = currentSessionToken?.lastActiveToken?.getRawString();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
constcurrentSessionToken=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSessionToken?.lastActiveToken?.getRawString();
constcurrentSession=client.authenticatedSessions.find(s=>s.id===client.lastActiveSessionId);
consttoken=currentSession?.lastActiveToken?.getRawString();

const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
const [ClientContext, useClientContext] = createContextAndHook<ClientResource | null | undefined>('ClientContext');
const [SessionContext, useSessionContext] = createContextAndHook<ActiveSessionResource | null | undefined>(
const [SessionContext, useSessionContext] = createContextAndHook<AuthenticatedSessionResource | null | undefined>(

@brkalowbrkalowFeb 14, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be a breaking change, if people are importing ActiveSessionResource from @clerk/types directly and depending on the return type of useSession().

EDIT: nevermind! It looks like AuthenticatedSessionResource is inclusive of ActiveSessionResource, so we should be safe 👀

const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();

const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
const handleSignOutSessionClicked = (session: SignedInSessionResource) => () => {

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.

It's much more idiomatic now! Thanks for the feedback @brkalow 🫡

@panteliselefpanteliselef 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.

Nice work

Comment on lines +9 to +11
- if (Clerk.user) {
+ if (Clerk.isSignedIn) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The isSignedIn property makes sense to me!

In the PR description, you mention that:

Clerk.user shouldn't be used to determine if the user has fully authenticated or not

But the logic in the Client resource tells me that isSignedIn is going to be true if the current session is active or pending, meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user?

@LauraBeatrisLauraBeatrisFeb 18, 2025

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.

meaning that currently, isSignedIn cannot be used to check if the user is fully authenticated or not.

The user has fully authenticated even with a pending status. Still, they have pending tasks to complete, eg: After sign-in, complete all factors but FAPI returns a pending session for tasks such as having to select an org.

Is this intentional? If yes, could you please provide an example where Clerk.isSignedIn !== !!Clerk.user

Currently, they are the same. This PR treats pending exactly like active to maintain current functionality and to incrementally add protections for it since the feature is gated on FAPI and toggled via Dashboard.

That property was added to avoid relying only on the user object, or Clerk.activeSessions.length > 0 for our internal "signed-in" state checks.

Once we introduce tasks (#5170) -> I'd add another property that specifically checks if the user has a session that resolved all pending tasks, something like:

// Would resolve to `false` for session.status === 'pending' // Would resolve to `true` for session.status === 'active'if(Clerk.hasValidSession){clerk.mountUserButton(userbuttonDiv)}else{clerk.mountSignIn(signInDiv)}

As a syntax sugar so that developers don't have to manually check for the session statuses on custom flows as well.

@LauraBeatrisLauraBeatrisFeb 18, 2025

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 actually acknowledged that I misplaced "Clerk.user shouldn't be used to determine if the user has fully authenticated or not" statement and updated the changeset here

isSignedIn doesn't necessarily replace Clerk.user, but it does act like a syntax sugar to deprecate any manual references to Clerk.client.activeSessions.length > 0, !!Clerk.user or !!Clerk.session

@144mdgross

Copy link
Copy Markdown

Hello! Sorry if this is the wrong place to ask but I wasn't able to get my discord account working. I begin getting type errors (error TS2322) when updating to version 4.47.0 of types. Is this expected and is it resolved by an update to the corresponding clerk packages? We are using "@clerk/remix": "4.1.1", in conjunction with @clerk/types": "4.6.0 currently without issue.

error TS2322: Type 'SignedInSessionResource | null | undefined' is not assignable to type 'ActiveSessionResource | null | undefined'.
Type 'PendingSessionResource' is not assignable to type 'ActiveSessionResource'.
Types of property 'status' are incompatible.
Type '"pending"' is not assignable to type '"active"'.```

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.

8 participants

@LauraBeatris@144mdgross@brkalow@nikosdouvlis@octoper@izaaklauer@panteliselef@clerk-cookie