Session Management

Improve security and user experience by enabling refresh tokens.

Passage supports a variety of session management capabilities that improve security and end user experience by enabling long-lived sessions (for mobile apps and PWAs) and token revocation support.

Stateless session management

This is the default setting for new applications in Passage. For stateless sessions, there is only a single configuration option available - the token expiration time. To change this setting, visit Authentication --> Session Settings in the Passage Console.

Stateless session management means that the session token is not stored anywhere on the server (the Passage server or your application server). This is done in the form of JSON Web Tokens (opens in a new tab) (JWTs), which are JSON-formatted tokens that are cryptographically signed by Passage to ensure they have not been tampered with. To verify a session, an application can use the public key provided by Passage to check the signature on the token. If it passes, the data in the JWT can be trusted.

JWTs provide great session security with low latency because they can be verified without contacting the Passage API. The downside to stateless sessions is that revocation and long lived sessions (common in mobile applications) can be difficult, if not impossible, to implement.

Refresh tokens

Passage also supports a hybrid session management solution that provide the low latency of stateless JWT-based sessions, with the revocation capabilities of stateful sessions.

Here’s how it works: When a user logs into your application with Passage, they are issued an auth token and a refresh token. The auth token is a stateless JWT that can be verified using a public key from Passage and grants users access to your application. It is designed to be short-lived. The refresh token is a long-lived stateful token that can be used to get a new auth token when one has expired.

Behind the scenes, Passage isn’t storing these sensitive tokens in our database. We store a cryptographic hash (HMAC) that allows us to validate an authentic data signature without holding any additional data linked to your customer accounts.

Refresh token rotation

To provide additional security out of the box, Passage has also introduced refresh token rotation that is enabled by default whenever you use refresh tokens. Rotation means that every time a refresh token is used to get a new auth token, a new refresh token is also issued and the original refresh token is invalidated.

The main reason for this is to prevent replay attacks - someone gaining access to the original refresh token and trying to use it to get an auth token. Passage provides an additional layer of security by detecting when an old refresh token has been compromised, invalidating all tokens related to the compromised refresh token, and requiring the user to re-authenticate.

Suggested token lifetimes

Here are some suggestions based on the type of application:

  • For most consumer applications, you can probably use our defaults: 1 hour auth token lifetime, 30 day absolute lifetime, and 5 day inactivity timeout.
  • For applications requiring higher security due to sensitive data or transactions, consider lowering the thresholds to 5 minute auth token lifetime, 24 hour absolute timeout, 30 minute inactivity timeout.
  • For long lived applications like mobile, we recommend a 1 hour auth token lifetime, 180 day absolute lifetime, and no inactivity timeout. These are typically apps where users may not log in often, but you don't want to make them re-authenticate.

Configuring refresh tokens

Refresh tokens are disabled by default. To enable this functionality visit Authentication → Session Management in the Passage Console. From there, choose your session timeouts.

  • The auth token lifetime should be short when enabling refresh tokens, from seconds to about 10 minutes.
  • The absolute expiration corresponds to the expiration of a refresh token and should be the maximum session length that you want to enforce.
  • The inactivity timeout will expire refresh tokens of users who have been inactive on your site for the specified period of time, so users will be required to re-authenticate on next visit.

Using refresh tokens in your frontend

Once refresh tokens are enabled, future login requests will include a refresh_token and refresh_token_expiration parameters in the AuthResult. If you are using a Passage Element, this will be transparent to you and Passage will automatically store the refresh token in local storage to be used when needed. You can also use the onSuccess callback to store the refresh token in a custom location. Learn more about callbacks

You only want to use refresh tokens when an auth token is expired. You should NOT refresh the token on every API request. You should use the getAuthToken() method on the PassageSession class in Passage-JS to get the correct auth token to send to your application. The method checks if the auth token is expired, & tries to refresh it silently as needed. It's a promise because the potential refresh is a network request.

import axios from 'axios';
import { Passage } from '@passageidentity/passage-js';
 
const passage = new Passage('YOUR_APP_ID');
 
passage.session.getAuthToken().then((authToken) =>
    axios.get(`${API_URL}/${PATH}`, {
        headers: {
            Authorization: `Bearer ${authToken}`,
        },
    }),
);

When a user logs out of your application, call user.signOut() to revoke the refresh token for the current session.

Backend SDK support

All of our backend SDKs have support for revoking user sessions. This can be helpful in the event that you think a user's account may have been compromised or if they choose to close their account with you. This function will revoke ALL refresh tokens for a user.

JavaScript

user.SignOut();

Go

  psg, err := passage.New(PassageAppID, &passage.Config{
      APIKey: PassageApiKey,
  })
  psg.SignOut(userID)

Python

  psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
  success = psg.revokeUserRefreshTokens(user_id)

Ruby

require 'passageidentity'
 
class ApplicationController < ActionController::Base
    PassageClient = Passage::Client.new(app_id: PASSAGE_APP_ID, api_key: PASSAGE_API_KEY)
 
  def authorize!
    begin
      revoke = PassageClient.auth.revoke_user_refresh_tokens(USER_ID)
    rescue Exception => e
      # handle exception (user is not authorized)
      # unauthorized
      redirect_to "/unauthorized"
    end
  end
end

Deactivating a user via the Passage Console or the backend SDKs with also revoke all refresh tokens for the user.