User Sessions

Supabase Auth provides fine-grained control over your user's sessions.

Some security sensitive applications, or those that need to be SOC 2, HIPAA, PCI-DSS or ISO27000 compliant will require some sort of additional session controls to enforce timeouts or provide additional security guarantees. Supabase Auth makes it easy to build compliant applications.

What is a session?#

A session is created when a user signs in. By default it lasts indefinitely and a user can have an unlimited number of active sessions on as many devices.

A session is represented by the Supabase Auth access token in the form of a JWT, and a refresh token which is a unique string.

Access tokens are designed to be short lived, usually between 5 minutes and 1 hour. You can exchange a refresh token only once to get a new access and refresh token pair.

This process is called refreshing the session.

A session terminates, depending on configuration, when:

  • The user clicks sign out.
  • The user changes their password or performs a security sensitive action.
  • It times out due to inactivity.
  • It reaches its maximum lifetime.

Access token (JWT) claims#

Every access token contains a session_id claim, a UUID, uniquely identifying the session of the user. You can correlate this ID with the primary key of the auth.sessions table.

Sign out and scopes#

Supabase Auth allows you to specify three different scopes for when a user invokes the sign out API in your application:

  • global (default) when all sessions active for the user are terminated.
  • local which only terminates the current session for the user but keep sessions on other devices or browsers active.
  • others to terminate all but the current session for the user.

You can invoke these by providing the scope option:

await supabase.auth.signOut()
// provide the relevant scope, the default being
await supabase.auth.signOut({ scope: 'global' })

Upon sign out, all refresh tokens and potentially other database objects related to the affected sessions are destroyed.

Be aware that access tokens of revoked sessions remain valid until their expiry time, encoded in the exp claim.

Controlling the lifetime of a session#

Supabase Auth can be configured to limit the lifetime of a user's session. By default, all sessions are active until the user signs out or performs some other action that terminates a session.

In some applications, it's useful or required for security to ensure that users authenticate often, or that sessions are not left active on devices for too long.

There are two ways to limit the lifetime of a session:

  • By enabling time-boxed sessions, which terminate after a fixed amount of time.
  • By enabling inactivity timeout, which terminates sessions that haven't been refreshed for a while.

To make sure that users are required to re-authenticate periodically, you can set a positive value for the Time-box user sessions option in the Auth settings for your project.

To make sure that sessions expire after a period of inactivity, you can set a positive duration for the Inactivity timeout option in the Auth settings.

Sessions are not proactively destroyed when you change these settings, but rather the check is enforced whenever a session is refreshed next. Otherwise sessions are progressively cleaned up 24 hours after they expire, which prevents you from causing a high load on your database by accident and allows you some freedom to undo changes without adversely affecting all users.

Frequently asked questions#

Most applications should use the default expiration time of 1 hour. This can be customized in your project's Auth settings in the Advanced Settings section.

Setting a value over 1 hour is generally discouraged for security reasons, but it may make sense in certain situations.

Values below 5 minutes, and especially below 2 minutes, should not be used in most situations because:

  • The shorter the expiration time, the more frequently refresh tokens are used, which increases the load on the Auth server.
  • Time is not absolute. Servers can often be off sync for tens of seconds, but user devices like laptops, desktops or mobile devices can sometimes be off by minutes or even hours. Having too short expiration time can cause difficult-to-debug errors due to clock skew.
  • Supabase's client libraries always try to refresh the session ahead of time, which won't be possible if the expiration time is too short.
  • Access tokens should generally be valid for at least as long as the longest running request in your application. This helps you avoid issues where the access token becomes invalid midway through processing.

What is refresh token reuse detection and what does it protect from?#

As your users continue using your app, refresh tokens are being constantly exchanged for new access tokens.

The general rule is that a refresh token can only be used once. However, strictly enforcing this rule can cause certain issues to arise. There are two exceptions to this designed to prevent the early and unexpected termination of user's sessions:

  • A refresh token can be used twice or more within a defined reuse interval. By default this is 10 seconds and we do not recommend changing this value. This exception is granted for legitimate situations such as:
    • Using server-side rendering where the same refresh token needs to be reused on the server and soon after on the client
    • To allow some leeway for bugs or issues with serializing access to the refresh token request
  • If the parent of the currently active refresh token for the user's session is being used, the active token will be returned. This exception solves an important and often occurring situation:
    • All clients such as browsers, mobile or desktop apps, and even some servers are inherently unreliable. Just because they sent a request, does not mean they received a response or even processed the response they received.
    • If a refresh token is used only once, and the client did not receive and process the response, when the client comes back it will be using a refresh token that was already used and falls out of the reuse interval, causing sudden and unexpected session termination.

Should the reuse attempt not fall under these two exceptions, the whole session is regarded as terminated and all refresh tokens belonging to it are marked as revoked. You can switch off this behavior in the Advanced Settings of the Auth settings page, though it is generally not recommended.

The purpose of this mechanism is to guard against potential security issues where a refresh token could have been stolen from the user, for example by exposing it accidentally in logs that leak (like logging cookies, request bodies or URL params) or via vulnerable third-party servers. It does not guard against the case where a user's session is stolen from their device.

What are the benefits of using access and refresh tokens instead of traditional sessions?#

Traditionally user sessions were implemented by using a unique string stored in cookies that identified the authorization that the user had on a specific browser. Applications would use this unique string to constantly fetch the attached user information on every API call.

This approach has some significant technical drawbacks, as it means that the authentication server and database are central and inseparable from the entirety of the application:

  • Requirement to be always available and performant. If the authentication server or its database crashes or is unavailable for even a few seconds, the whole application goes down. Scheduling maintenance or dealing with transient errors becomes very difficult.
  • A failing authentication server can cause a chain of failures across other systems and APIs, paralyzing the whole application system.
  • It is generally very hard to globally distribute workloads for authentication, so this causes a situation where all requests experience the latency to communicate with this primary region.

Access and refresh tokens are therefore a better approach because the session information is encoded in a short-lived token -- the access token -- which can be transferred across APIs and systems without relying on the availability or performance of a central server. An application can thus tolerate transient failures or performance issues a lot better. By trying to refresh the access token ahead of time, your application can safely continue to function even with larger outages.

It's better for cost optimization and scaling as well, as the authentication system's servers and database only handle traffic for this use case.

How do I make sure that an access token (JWT) cannot be used after a user clicks sign out?#

Most applications rarely need such strong guarantees. Consider adjusting the JWT expiry time to an acceptable value. If this is still necessary, you should try to use this validation logic only for the most sensitive actions within your application.

When a user signs out, the sessions affected by the logout are removed from the database entirely. You can check that the session_id claim in the JWT corresponds to a row in the auth.sessions table. If such a row does not exist, it means that the user has logged out.

Note that sessions are not proactively terminated when their maximum lifetime (time-box) or inactivity timeout are reached. These sessions are cleaned up progressively 24 hours after reaching that status. This allows you to tweak the values or roll back changes without causing unintended user friction.

Can I use HTTP-Only cookies to store the access and refresh tokens?#

This is possible, but only for apps that use the traditional server-only web app approach where all of the application logic is implemented on the server and it returns rendered HTML only.

If your app uses any client side JavaScript to build a rich user experience, using HTTP-Only cookies is not possible since only your server will be able to see and refresh the session of the user. The browser would not have access to the access and refresh token.

Because of this, the Supabase JavaScript libraries provide only limited support. You can override the storage option when creating the Supabase client on the server to store the values in cookies or other adequate storage, like so:

import { createClient } from '@supabase/supabase-js'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
auth: {
storage: customStorageObject,

The customStorageObject should implement the localStorage API interface.

When using cookies to store access and refresh tokens, make sure that the Expires or Max-Age attributes of the cookies is set to a timestamp very far into the future. Browsers will clear the cookies, but the session will remain active in Supabase Auth. Therefore it's best to let Supabase Auth control the validity of these tokens and instruct the browser to always store the cookies indefinitely.