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.
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.
Method | Parameters | Overview |
---|---|---|
client.sessions.authenticateJwt | session_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.authenticate | session_jwt or session_token, session_duration_minutes (optional), session_custom_claims (optional) | Always communicates with the Stytch API to authenticate the session. |
client.sessions.authenticateJwtLocal | session_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:
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.
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.