/
Contact usSee pricingStart building
Node
​

    About Stytch

    Introduction
    Integration Approaches
      Full-stack overview
      Frontend (pre-built UI)
      Frontend (headless)
      Backend
    Migrations
      Migration overview
      Migrating users statically
      Migrating users dynamically
      Additional migration considerations
      Zero-downtime deployment
      Defining external IDs for users
      Exporting from Stytch
    Custom Domains
      Overview

    Authentication

    DFP Protected Auth
      Overview
      Setting up DFP Protected Auth
      Handling challenges
    Magic Links
    • Email Magic Links

      • Getting started with the API
        Getting started with the SDK
        Replacing your password reset flow
        Building an invite user flow
        Add magic links to an existing auth flow
        Adding PKCE to a Magic Link flow
        Magic Link redirect routing
    • Embeddable Magic Links

      • Getting started with the API
    MFA
      Overview
      Backend integration
      Frontend integration
    Mobile Biometrics
      Overview
    M2M Authentication
      Authenticate an M2M Client
      Rotate client secrets
      Import M2M Clients from Auth0
    OAuth
    • Identity providers

      • Overview
        Provider setup
      Getting started with the API (Google)
      Add Google One Tap via the SDK
      Email address behavior
      Adding PKCE to an OAuth flow
    Connected AppsBeta
      Setting up Connected Apps
      Client types
    • Integration Guides

      • Integrate with AI agents
        Integrate with MCP servers
        Integrate with CLI Apps
    • Resources

      • About Remote MCP Servers
        Consent Management
    Passcodes
      Getting started with the API
      Getting started with the SDK
    • Toll fraud

      • What is SMS toll fraud?
        How you can prevent toll fraud
      Unsupported countries
    Passkeys & WebAuthn
    • Passkeys

      • Passkeys overview
        Set up Passkeys with the frontend SDK
    • WebAuthn

      • Getting started with the API
        Getting started with the SDK
    Passwords
      Getting started with the API
      Getting started with the SDK
      Password strength policy
    • Email verification

      • Overview
        Email verification before password creation
        Email verification after password creation
    Sessions
      How to use sessions
      Backend integrations
      Frontend integrations
      Custom claims
      Custom claim templates
      Session tokens vs JWTs
      How to use Stytch JWTs
    TOTP
      Getting started with the API
      Getting started with the SDK
    Web3
      Getting started with the API
      Getting started with the SDK

    Authorization

    Implement RBAC with metadata

    3rd Party Integrations

    Planetscale
    Supabase
    Feathery
    Unit

    Testing

    E2E testing
    Sandbox values
Get support on SlackVisit our developer forum

Contact us

Consumer Authentication

/

Guides

/

Authentication

/

Sessions

/

How to use Stytch JWTs

How to use Stytch Session JWTs

Stytch Session JWTs can be a great tool to use if you'd like to minimize latency during session authentication. To determine whether or not JWTs are a good fit for your application, we'd first recommend checking out our Session tokens vs. JWTs resource, as well as our JWTs vs. sessions: which authentication approach is right for you? blog post.

In this guide, we'll help you understand the following:

  • How to use Stytch JWTs on the backend
  • Handling and rotating your JSON Web Key Set (JWKS)
  • How to store and refresh JWTs on the frontend

We recommend reviewing the information in this guide before incorporating Stytch Session JWTs into your application.

Authenticating JWTs on the backend

When using Stytch JWTs to authenticate sessions, you'll typically pass the JWT to your backend endpoints to gate protected actions or resources. In order to do so, you'll need to validate the JWT before proceeding.

The main benefit of using JWTs over opaque session tokens is that you can validate them locally (as long as they're unexpired) entirely within your backend service, which is significantly faster than communicating with the Stytch API to authenticate the user's session on every session validation check. You can validate Stytch Session JWTs locally whether you're using Stytch's backend SDKs or other, standard JWT validation libraries.

Note that an expired Stytch JWT can be exchanged for a new, unexpired JWT as long as the underlying Stytch Session is still active. This is only possible when communicating with the Stytch API to authenticate the user's session; you cannot retrieve a new JWT via local JWT validation.

JWT validation diagram

Using Stytch backend SDKs

Upon initialization, backend Stytch SDK clients will automatically retrieve and cache your JWKS for use in local JWT validation. Because of this, it's important to persist the Stytch SDK client instead of reinitializing it between requests, so your JWKS is only fetched once.

There are several different backend SDK methods that can be used to validate Stytch JWTs, and the below table and following usage notes explain how each method works. Note that method names and parameters vary by SDK, so we'd also recommend checking out the source code for the Stytch backend SDK that you're planning to use.

MethodParametersOverview
client.sessions.authenticateJwtsession_jwt, max_token_age_seconds (optional)Either validates the JWT locally or communicates with the Stytch API to authenticate the session if the JWT is expired (see usage notes below for additional information).
client.sessions.authenticatesession_jwt or session_token, session_duration_minutes (optional), session_custom_claims (optional)Always communicates with the Stytch API to authenticate the session.
client.sessions.authenticateJwtLocalsession_jwt, max_token_age_seconds (optional)Always validates the JWT locally.

Usage notes

If the JWT is older than max_token_age_seconds or if the JWT is expired, this method will communicate with the Stytch API to authenticate the session. Otherwise, the JWT will be validated locally.

If you do not provide a max_token_age_seconds parameter, then the authenticateJwt method will only communicate with the Stytch API if the JWT is expired (Stytch JWTs have an exp of five minutes). Specifying a max_token_age_seconds parameter of less than five minutes is one way to reduce security risks inherent to local JWT validation by forcing communication with the Stytch API more frequently.

We recommend relying primarily on this method, as it handles the local JWT validation vs. remote session authentication logic for you, improving latency when the JWT is less than max_token_age_seconds old and authenticating the underlying session with Stytch when necessary.

This method will always communicate with the Stytch API to authenticate the session, even if a session_jwt is provided, and will never validate JWTs locally.

If you provide a session_duration_minutes parameter, the session's lifetime will be set to that many minutes from the time of the call.

We recommend using this method in situations where you'd prefer to force communication with the Stytch API in order to authenticate the session; for example, before taking particularly sensitive actions or returning particularly sensitive data, or when you need to update the session's custom claims (via the session_custom_claims parameter).

This method will always validate the JWT locally. If the JWT is expired or if it's older than the specified max_token_age_seconds, an expiration error will be returned.We'd recommend using this method in situations where you know you never want to make a request to the Stytch API in order to authenticate the session.


Retrieving user data

Note that when local JWT validation occurs, the client.sessions.authenticateJwt and client.sessions.authenticateJwtLocal methods will not return the full Stytch User object. If you need data from the Stytch User object during local JWT validation in order to complete additional authorization checks or perform additional actions while authenticating the user's session, we'd recommend including that data in the Session's custom claims so that it's added to the JWT itself.

If there is certain User data that you'd always like to include in Session custom claims, we'd recommend setting up a Custom claim template so that the data is included automatically in all Sessions minted for your Stytch Project.

Using custom backend JWT validation methods

You may use other standard JWT validation libraries to validate Stytch JWTs locally using your Stytch project's JWKS, rather than using Stytch's backend SDKs. When doing so, you'll need to implement logic to fall back to Stytch's Authenticate Session endpoint when the JWT is expired (see the client.sessions.authenticateJwt method description above for an example of how that logic might work).

See below for a non-exhaustive list of some common libraries and services that Stytch JWTs are compatible with:

  • jose (Node)
  • jwt (Go)
  • Hasura
  • NGINX
  • Apollo

JSON Web Key Set (JWKS) rotation

Stytch will automatically rotate your JWKS every six months for security purposes. Upon rotation, newly minted JWTs will be signed using the new key set, and both key sets will be returned by the Get JWKS endpoint for a period of one month.

JWTs have a set lifetime of five minutes, so there will be a five minute period where some JWTs will be signed by the old JWKS, and some JWTs will be signed by the new JWKS. The correct JWKS to use for validation is determined by matching the kid value of the JWT and JWKS.

If you're using one of our backend SDKs, the JWKS rotation will be handled for you, and you shouldn't need to take any additional action.

If you're using your own JWT validation logic, many JWT validation libraries have built-in support for JWKS rotation, and you'll just need to supply the Stytch Get JWKS endpoint. Otherwise, your application should decide which JWKS to use for validation by inspecting the JWT's kid value.

If you issue sessions that exceed one month in duration, we recommend storing Stytch session tokens alongside your Stytch JWTs. That way, you can fall back to session token authentication in the case where a JWT is minted using one JWKS, is not updated at all during the one month period when the old and new JWKS are returned, and then the session needs to be authenticated after that period is over (and only the new JWKS is available, which cannot be used to validate the JWT).

Storing JWTs on the frontend

In order to persist your user's session, you'll need to store their session JWT in their browser or on their device. You'll also need to keep the stored JWT current by refreshing it periodically in order to take advantage of the performance gains that unexpired Stytch JWTs provide.

Using Stytch frontend SDKs

Stytch frontend SDKs automatically store and refresh session JWTs for the logged-in user. No additional code is necessary.

When a user successfully logs in, the SDK will store the newly minted JWT as well as an opaque session token as browser cookies, which will then be automatically included in requests to your backend (as long as it shares a domain with your frontend). See our Cookies and session management resource for more information about how our frontend JavaScript SDK stores session JWTs.

The SDK will also automatically refresh the JWT in the background every 3 minutes and will replace the older JWT stored as a browser cookie with the new one. That way, the JWTs included in requests to your backend will be unexpired.

Using custom frontend JWT management

If you've implemented your own custom frontend session management, you'll need to store the session JWTs for logged-in users yourself. We generally recommend storing session JWTs as browser cookies, though this may vary depending on your use case.

Session JWTs are sensitive values, so make sure that you store them securely.

You should also frequently refresh the JWT that you store to avoid sending expired JWTs to your backend, which negates the JWT performance gain. You can retrieve a new JWT by calling the backend Authenticate Session endpoint (you can read more about this in the Authenticating JWTs on the backend section).

You can either run a background job to periodically refresh the JWT (retrieving a new JWT from Stytch and replacing the JWT you have stored on the frontend at a set cadence), or you can simply replace the stored JWT with a new one whenever you receive a new JWT during backend session authentication.

Authenticating JWTs on the backend

Using Stytch backend SDKs

Usage notes

Retrieving user data

Using custom backend JWT validation methods

JSON Web Key Set (JWKS) rotation

Storing JWTs on the frontend

Using Stytch frontend SDKs

Using custom frontend JWT management