Frontend Sessions guide
In this guide, we'll walk you through how to create and manage B2B Stytch Sessions via using our frontend JavaScript SDK. This guide is relevant both to integrations that use our pre-built UI components and integrations that use our headless frontend methods.
Overview

When a user successfully authenticates, our frontend JavaScript SDK will automatically store the user's session token and JWT as browser cookies. The user's session token and JWT will then be automatically included in request headers by the browser in all requests to your backend, as long as your frontend and backend share a domain.
Your backend can use the session token or JWT to authenticate the session and determine whether or not it should proceed with the request. See our Backend sessions guide for additional information about backend session authentication.
The session token will also be included in all frontend SDK requests to Stytch's servers, so that the SDK can make requests on behalf of the authenticated user.
Background session authentication
The frontend SDK makes background session authentication calls to Stytch servers roughly every three minutes while the user is actively using your application. After each background request, the frontend SDK will replace the old session JWT with a new one, ensuring that the JWT remains unexpired.
The SDK will also update its cached User and Session data after its background session authentication calls to reflect any changes in the session data, and will clear the session cookies if the session is no longer valid.
Session cookie configuration
When using the Stytch frontend SDK for session management, you may configure certain values such as the session cookie names and which HTTP paths, domain, and subdomains the cookie should be accessible to.
For additional information, see our Cookies & session management SDK resource.
HttpOnly vs. non-HttpOnly session cookies
By default, session cookies set by the Stytch frontend JavaScript SDK are not marked as HttpOnly. For enhanced security, you can optionally enable the use of HttpOnly cookies for your project.
When HttpOnly cookies are enabled, session handling behavior will remain largely the same, with a few minor differences. Check out our HttpOnly Cookies overview for additional information.
Handling intermediate session tokens
The Stytch API returns intermediate_session_tokens instead of full-fledged Member session_tokens during authentication flows that require an intermediate step before the user is fully authenticated. The frontend JavaScript SDK will automatically store any intermediate_session_tokens returned by the Stytch API and will include them in subsequent authentication requests.
There are three flows in which intermediate_session_tokens are used:
- The Discovery flow, where an intermediate_session_token is returned after the user authenticates but before they select or create an Organization.
- The MFA flow, where an intermediate_session_token is returned after the user completes their first authentication factor but before they complete their second factor.
- The step-up flow, where an intermediate_session_token is returned after the user completes one form of authentication but must complete additional authentication to verify their email address or to comply with an Organization's allowed authentication methods before they can be granted a session.
Our frontend pre-built UI components handle all three of the above flows out of the box. If you're using our headless methods with your own UI, you'll need to implement some of the logic yourself.
Discovery flow
An intermediate_session_token will be returned in response to any of the Discovery authenticate methods (like Authenticate discovery magic link or Authenticate discovery OAuth, for example).
In the Discovery authenticate response, you'll receive a Discovered Organizations object that you can use to inform the subsequent UI. Here's an example Discovery authenticate response:
{
"discovered_organizations": [
{
"member_authenticated": false,
"membership": {
"details": null,
"member": {
// Full Member object
},
"type": "active_member"
},
"mfa_required": {
"member_options": {
"mfa_phone_number": "XXXXXXX1234",
"totp_registration_id": ""
},
"secondary_auth_initiated": null
},
"organization": {
// Full Organization object (fields omitted for brevity)
...
"organization_id": "organization-...",
"organization_logo_url": "",
"organization_name": "Example Organization One",
"organization_slug": "example-organization-one",
},
"primary_required": null
},
{
"member_authenticated": true,
"membership": {
"details": null,
"member": {
// Full Member object
},
"type": "pending_member"
},
"mfa_required": null,
"organization": {
// Full Organization object (fields omitted for brevity)
...
"organization_id": "organization-...",
"organization_logo_url": "",
"organization_name": "Example Organization Two",
"organization_slug": "example-organization-two",
},
"primary_required": null
},
],
"email_address": "example@stytch.com",
"intermediate_session_token": "Is9gF...",
"request_id": "request-id-...",
"status_code": 200
}
At this point, you can either allow the user to start a session in an Organization that they have access to using the Exchange intermediate session method, or you can allow them to create a new Organization using the Create Organization via discovery method. If you'd like to allow Organization creation via frontend calls, make sure that the Create Organizations setting is enabled for your project in the Frontend SDKs tab in the Stytch Dashboard.
Note that the SDK will automatically save the intermediate_session_token in browser cookies, so there's no need to explicitly pass it into these methods.
export const exchangeDiscoveryIntermediateSessionToken = (organization_id) => {
stytch.discovery.intermediateSessions.exchange({
organization_id,
session_duration_minutes: DESIRED_SESSION_LENGTH,
});
}
export const createOrganization = (organization_name, organization_slug) => {
stytch.discovery.organizations.create({
organization_name,
organization_slug,
session_duration_minutes: DESIRED_SESSION_LENGTH,
});
}
After calling the Exchange intermediate session method, you may still need to trigger the MFA flow or primary step-up authentication flow based on the authentication requirements for the Organization that the user selects. See below for additional information.
Multi-factor authentication (MFA) flow
If a user is required to complete MFA, the following will occur:
- An intermediate_session_token will be returned instead of a session_token in response to whichever authentication method is being called (for example, Email Magic Links authenticate, Passwords authenticate, or Exchange intermediate session).
- The member_authenticated value in the authentication response will be false.
- The primary_required value in the authentication response will be null.
- The mfa_required value in the authentication response will be non-null and will indicate which form of MFA the user is required to complete.
- If the user is enrolled in SMS OTP MFA, an OTP code will automatically be sent to their phone number.
The following is an example Email Magic Links authenticate response where SMS OTP MFA is required as a next step:
{
"intermediate_session_token": "Fxq0E...",
"member": {
// Full Member object
},
"member_authenticated": false,
"member_id": "member-...",
"member_session": null,
"method_id": "member-email-...",
"mfa_required": {
"member_options": {
"mfa_phone_number": "XXXXXXX1234",
"totp_registration_id": ""
},
"secondary_auth_initiated": "sms_otp"
},
"organization": {
// Full Organization object
},
"organization_id": "organization-...",
"request_id": "request-id-...",
"reset_sessions": false,
"session_jwt": "",
"session_token": "",
"status_code": 200
}
In this case, you will need to prompt the user to complete the appropriate MFA flow. See our Headless frontend MFA guide for more information and implementation steps.
Primary step-up flow
If a user must complete an additional form of primary authentication, the following will occur:
- An intermediate_session_token will be returned instead of a session_token in response to whichever authentication method is being called (for example, OAuth authenticate, Passwords authenticate or Exchange intermediate session).
- The member_authenticated value in the authentication response will be false.
- The primary_required value in the authentication response will be non-null and will detail the action(s) that the user must take in order to meet the primary step-up requirement for the Organization in question.
The following is an example OAuth authenticate response where primary step-up authentication is required as a next step:
{
"intermediate_session_token": "fTwWh...",
"member": {
// Full Member object
},
"member_authenticated": false,
"member_id": "member-...",
"member_session": null,
"mfa_required": null,
"organization": {
// Full Organization object
},
"organization_id": "organization-...",
"primary_required": {
"allowed_auth_methods": [
"sso",
"microsoft_oauth",
"email_otp",
"magic_link",
"google_oauth"
]
},
"provider_subject": "U08...",
"provider_type": "Slack",
"provider_values": {
"access_token": "x2f...",
"expires_at": "2024-12-10T20:11:53Z",
"id_token": null,
"refresh_token": null,
"scopes": [
"users:read",
"users:read.email"
]
},
"request_id": "request-id-...",
"reset_sessions": false,
"session_jwt": "",
"session_token": "",
"status_code": 200
}
At this point, you'll need to prompt the user to complete one of the authentication methods present in the primary_required.allowed_auth_methods array before they can be granted a full Member session.
Determining whether there is an active session
You can leverage the frontend SDK's session data to determine whether or not there is an active session for frontend UI and navigation purposes. Choose your frontend framework for additional usage details.
When using our React SDK, you can use the useStytchMemberSession() hook to retrieve the user's session data and listen for changes. The useStytchMemberSession() hook returns two values:
- session: The user's Stytch Session object, if there is an active session. If there's no active session, the session value will be null.
- fromCache: true if the SDK hasn't yet completed a session authentication call after loading and is returning cached session data; false if the session authentication call has completed.
For many use cases, like deciding whether or not to render certain frontend content, it's generally fine to rely on the session value. However, for use cases where you'd prefer to wait until the session data finishes updating instead of relying on cached data, you can verify that fromCache is false before checking the session value. See our SWR & caching resource for additional information.
import { useStytchMemberSession } from '@stytch/react/b2b';
export const Home = () => {
const { session } = useStytchMemberSession();
return session ? <p>Active Session</p> : <p>No Active Session</p>;
};
When using our NextJS SDK, you can use the useStytchMemberSession() hook to retrieve the user's session data and listen for changes. The NextJS useStytchMemberSession() hook returns three values:
- isInitialized: Whether or not the SDK has finished initializing. Ensure this is true before checking for an active session.
- session: The user's Stytch Session object, if there is an active session. If there's no active session, the session value will be null.
- fromCache: true if the SDK hasn't yet completed a session authentication call after loading and is returning cached session data, false if the session authentication call has completed.
For many use cases, like deciding whether or not to render certain frontend content, it's generally fine to rely on the session value as long as isInitialized is true. However, for use cases where you'd prefer to wait until the session data finishes updating instead of relying on cached data, you can verify that fromCache is false before checking the session value. See our SWR & caching resource for additional information.
import { useStytchMemberSession } from '@stytch/nextjs/b2b';
export const Home = () => {
const { session, isInitialized } = useStytchMemberSession();
if (!isInitialized) {
return <p>Loading</p>
}
return session ? <p>Active Session</p> : <p>No Active Session</p>;
};
When using our Vanilla JavaScript SDK, you can use the stytch.session.getInfo method to retrieve the user's session data. The stytch.session.getInfo method returns two values:
- session: The user's Stytch Session object, if there is an active session. If there's no active session, the session value will be null.
- fromCache: true if the SDK hasn't yet completed a session authentication call after loading and is returning cached session data; false if the session authentication call has completed.
import { StytchB2BHeadlessClient } from '@stytch/vanilla-js/b2b/headless';
const stytch = new StytchB2BHeadlessClient('PUBLIC_TOKEN');
let { session, fromCache } = stytch.session.getInfo();
if (session && fromCache) {
// There is a cached session and a session authentication is in progress
} else if (session) {
// Session authentication has completed and there is an active session
} else {
// There is no active session
}
You can also use the stytch.session.onChange method to listen for changes in the user's session. The session.onChange method takes in a callback that gets called whenever the session object changes, and it returns an unsubscribe method for you to call when you no longer want to listen for changes.
// Listen for session changes
const unsubscribeSession = stytch.session.onChange((newSession) => {
session = newSession;
// Trigger any relevant actions in your application based on the session update
handleSessionChange();
});
window.addEventListener('beforeunload', () => {
unsubscribeSession && unsubscribeSession();
});
Extending the lifetime of a session
Once a session has been created, the frontend SDK will keep the session JWT updated by making background session authentication calls, but it will not automatically extend the lifetime of the underlying session. For example, if you set an initial session_duration_minutes of 60 minutes and take no further action, the session will expire after 60 minutes regardless of user activity.
If you would like to extend the lifetime of an active session, you can specify the new desired session length by passing a session_duration_minutes value to the Authenticate session method, which will set the session's lifetime to that many minutes from the present. Note that once a session has expired, it can't be reactivated – in that case, the user will need to log in again and create a new session.
One technique is to set an interval where you extend the session's lifetime so that the session is periodically extended while the user keeps your application open:
const extendSession = () => {
if (stytch.session.getSync()) {
// Set the session's expiration to 60 minutes from the present
stytch.session.authenticate({
session_duration_minutes: 60,
});
}
};
// Extend the session's lifetime every 10 minutes
let interval = setInterval(extendSession, 600000);
window.addEventListener('beforeunload', () => {
clearInterval(interval);
});
Exchanging a session
Even if a user has access to more than one Organization, Stytch session_tokens are valid for one Organization at a time. However, as long as the user's current session meets the authentication requirements for another Organization that they have access to, you may exchange their session for a session in the other Organization. See the Exchange session method documentation for additional information regarding session exchange requirements.
First, you can use the List discovered Organizations method to retrieve a list of Organizations that the active user has access to.
export const listOrganizations = async () => {
const { discovered_organizations } = await stytch.discovery.organizations.list();
return discovered_organizations;
};
Here's an example List discovered Organizations response:
{
"discovered_organizations": [
{
"member_authenticated": false,
"membership": {
"details": null,
"member": {
// Full Member object
},
"type": "active_member"
},
"mfa_required": {
"member_options": {
"mfa_phone_number": "XXXXXXX1234",
"totp_registration_id": ""
},
"secondary_auth_initiated": null
},
"organization": {
// Full Organization object (fields omitted for brevity)
...
"organization_id": "organization-...",
"organization_logo_url": "",
"organization_name": "Example Organization One",
"organization_slug": "example-organization-one",
},
"primary_required": null
},
{
"member_authenticated": true,
"membership": {
"details": null,
"member": {
// Full Member object
},
"type": "active_member"
},
"mfa_required": null,
"organization": {
// Full Organization object (fields omitted for brevity)
...
"organization_id": "organization-...",
"organization_logo_url": "",
"organization_name": "Example Organization Two",
"organization_slug": "example-organization-two",
},
"primary_required": null
},
],
"email_address": "example@stytch.com",
"organization_id_hint": null,
"request_id": "request-id-...",
"status_code": 200
}
Once the user selects the Organization that they'd like to access, you can exchange their active session for a session in that Organization using the Exchange session method documentation. If the session is exchanged successfully, the frontend SDK will automatically be populated with the new Session, Member, and Organization data.
export const exchange = (target_organization_id) => {
stytch.session.exchange({
organization_id: target_organization_id,
session_duration_minutes: DESIRED_SESSION_LENGTH,
});
};
Check out our B2B SDK example app for an example of an Organization switcher component built on top of our frontend SDK endpoints.
Note that if additional authentication is required before the user can be granted a Member Session in the new Organization, the Exchange session method will return an intermediate_session_token instead. See the [Handling intermediate_session_tokens section](TODO: Link to section) for additional details.
Hydrating a session
If necessary, you can hydrate the frontend SDK with an active session that was created elsewhere. This is useful if your application uses any authentication flows where a session is created or updated on the backend and you'd like the frontend SDK to pick up the most recent session data.
You can do so by providing both the active session_token and the session_jwt to the Update session method, followed by a call to the Authenticate session method.
export const hydrateSession = () => {
stytch.session.updateSession({
session_token: 'ACTIVE_SESSION_TOKEN',
session_jwt: 'ACTIVE_SESSION_JWT',
});
stytch.session.authenticate();
};
Note that when HttpOnly cookies are enabled for your Stytch project, the Update session method will only work if there isn't already an existing session token set using an HttpOnly cookie. If you do use the Update session method when HttpOnly cookies are enabled, make sure your Stytch SDK client's cookieOptions are configured to match the domain and cookie names used by Stytch's backend.
Alternatively, you can override the session cookies via response headers from your backend. After doing so, be sure to make a frontend call to the Authenticate session method so that the SDK picks up the new session data.
Revoking a session
The Revoke session method makes a call to Stytch's servers to revoke the active session and clears the session cookies from the user's browser.
export const logout = () => {
stytch.session.revoke();
};