# How Clerk works

This guide provides a deep dive into Clerk's architecture and internal workings. For developers who are simply looking to add authentication to their app, see the [quickstart guides](https://clerk.com/docs/getting-started/quickstart/overview.md).

## The frontend API

When you create a new application through the [Clerk Dashboard](https://dashboard.clerk.com/), Clerk provisions a dedicated frontend API (FAPI) instance for your application. It is hosted at `https://<slug>.clerk.accounts.dev` in development environments, where `<slug>` is a unique identifier generated for your application. You can find your application's FAPI URL in the [**Domains**](https://dashboard.clerk.com/~/domains) page of the Clerk Dashboard.

When configuring your Clerk app, you must provide a Publishable Key. The Publishable Key tells your app what your FAPI URL is, enabling your app to locate and communicate with your dedicated FAPI instance.

The Clerk Publishable Key follows a specific format: it consists of your FAPI URL encoded in base64, prefixed with an environment identifier (e.g. `pk_test_` for development environments, `pk_live_` for production environments), and suffixed with a `$` delimiter for future extensibility. The base64-encoded URL enables your application to locate and communicate with your dedicated FAPI instance. You can verify this structure by decoding the key yourself:

```js
const publishableKey = 'pk_test_ZXhhbXBsZS5hY2NvdW50cy5kZXYk'
const keyWithoutPrefix = publishableKey.replace('pk_test_', '')

atob(keyWithoutPrefix) // => example.accounts.dev$
```

> In previous versions of Clerk, the FAPI URL was exposed directly rather than being encoded within a Publishable Key. This was a source of confusion for users, so we transitioned to encoding it as base64 and making it a key.

FAPI manages authentication flows on a per-user basis. For instance, it handles flows for signing up a user, retrieving a user's active sessions, creating an Organization on behalf of a user, or fetching a user's Organization invites. You can find the complete FAPI documentation [here](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }}.

FAPI _does not_ handle administrative actions that impact multiple users, such as listing all users, banning users, or impersonating a user. These types of tasks are handled by [the backend API](#the-backend-api).

Some tasks, such as [signing up a user](https://clerk.com/docs/reference/frontend-api/tag/sign-ups/post/v1/client/sign_ups){{ target: '_blank' }}, don't require authentication, as that would defeat the purpose of the endpoint. However, endpoints designed for authenticated users, like [updating a user's details](https://clerk.com/docs/reference/frontend-api/tag/user/patch/v1/me){{ target: '_blank' }}, require FAPI to first identify the user making the request and then verify their authorization. This ensures that users cannot modify another user's details. Typically, this is achieved by sending a signed token with the request, either as a cookie or a header. You can [learn more about Clerk's authentication tokens later in this guide](#stateful-authentication).

While it's possible to build complete authentication flows directly on top of the frontend API, it involves significantly more work. Most users prefer our frontend SDKs, which provide higher-level abstractions like the [mountSignIn()](https://clerk.com/docs/reference/components/authentication/sign-in.md#mount-sign-in) method or the [<SignIn />](https://clerk.com/docs/reference/components/authentication/sign-in.md) component (for React-based SDKs). These abstractions offer a well-tested, thoughtfully designed, a11y-optimized, and customizable UI for authentication flows, handling all possible configurations of your authentication preferences out-of-the-box.

## Levels of abstraction

FAPI is the lowest level of abstraction that authentication flows can be built on with Clerk. However, there are several other abstraction layers that offer less work and more convenience.

### Clerk's prebuilt components

Clerk's [prebuilt UI components](https://clerk.com/docs/reference/components/overview.md) are Clerk's highest level of abstraction. They are "all in one" components, offering the most complete implementation of authentication with the least amount of effort. While it's strongly recommended to use these components, due to the amount of research we have put into delivering an optimal experience, it's not the only option if you feel that you need more control over your authentication flows.

> **Customizability:** You can [modify the CSS for any part of the prebuilt components](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview.md), but not the HTML structure or the logic/ordering of how the authentication flow works.

### Custom flows

Finally, if you need complete control over the authentication flow, Clerk provides low-level primitives that directly wrap our API endpoints. These primitives enable you to build fully custom authentication flows from scratch. Clerk refers to these as ["custom flows"](https://clerk.com/docs/guides/development/custom-flows/overview.md). While this approach offers maximum flexibility, it also requires significant development effort to implement and maintain. Custom flows should only be considered when you have specific requirements that cannot be met using the prebuilt components or Clerk Elements, as you'll need to handle all authentication logic, error states, and edge cases yourself.

> **Customizability:** You have full control over every part of the authentication flow, including HTML structure, CSS, and the logic/ordering of how the authentication flow works.

## The backend API

The frontend API (FAPI) is designed for use primarily from the frontend of your application. Its methods focus on signing in users and managing user-related resources and data once they are authenticated. However, as an application developer, you might also need to perform administrative tasks, such as modifying multiple user or Organization details, retrieving a list of all users, banning or impersonating a user, and more.

To maintain security, these administrative tasks should only be executed on the server side using a secret key inaccessible to your users or the browser. These operations are handled by a separate API known as the backend API (BAPI). You can find the BAPI documentation [here](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}.

Although the administrative features of BAPI are useful for many applications, it's most commonly used to verify a user's authentication state when processing requests from your app's frontend. For instance, if a user submits a request to update some data associated with their account, **your server must ensure the user is authenticated and authorized to make this change.** Without proper validation, malicious actors could potentially take over user accounts.

Like FAPI, while you can interact directly with BAPI, most developers opt to use Clerk's SDKs for smoother integration with their preferred language or framework. Documentation for Clerk's SDKs is available in [the left sidenav of the docs](https://clerk.com/docs.md). That being said, FAPI is a much more complex and nuanced API that relies on more custom logic outside of its endpoints to create a functional SDK on top of it. As such, **interacting directly with FAPI is not recommended**, whereas interacting directly with BAPI is generally reasonable.

## Stateful authentication

To understand how authentication works in Clerk, it's important to first understand how the most common implementation of authentication logic works: the traditional "stateful authentication" model, also known as "session token authentication".

A user's process of signing in would work as follows. This example assumes that the user already signed up and their credentials are stored in a database.

1. The user initiates authentication by navigating to `example.com/sign-in`, entering their credentials (e.g. username/password), and submitting the form, usually by clicking a "submit" button. This makes a request to the server with the credentials.
2. The server validates the credentials against a database. This is normally done by [hashing](https://clerk.com/glossary.md#hash) the password and comparing it with a stored password hash. Upon successful validation, it creates a new [session](https://clerk.com/glossary.md#session) in the database associated with the user.
3. The server responds to the browser's request by setting the session ID in a [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) header in the response, which sets a cookie with this value in the browser. This cookie will be automatically included in future requests from the browser in order to authenticate the user.

   [View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/stateful-auth.mp4)
4. The next time the browser sends a request to the server, it [automatically includes](https://clerk.com/docs/guides/how-clerk-works/cookies.md) the session cookie. The server checks the database for the session ID and retrieves the associated user ID and session metadata. If the session is valid and active, the server has verified that the user has authenticated, and can then use the user ID to fetch any required user data and process the request.

   [View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/stateful-auth-2.mp4)

> What happens if an attacker gets their hands on your session ID? Generally, the answer here is that you're in trouble. If an attacker gets your session ID, they can sign in as you. Therefore, it's best practice to use [HTTPS](https://developer.mozilla.org/en-US/docs/Glossary/HTTPS) (ensures attacker can't [sniff it](https://en.wikipedia.org/wiki/Sniffing_attack)) and ensure the cookie is [set as `HttpOnly`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#httponly) (ensures attacker can't get it via remote JavaScript execution).

This is a perfectly reasonable authentication model and works great for most apps as it's straightforward to understand and implement. Additionally, since it checks the database for _every request_ that requires authentication, sessions can be instantly revoked if needed (by setting the state to `revoked` and adding a check in the server logic). However, because it requires every request to query the database, it introduces additional latency and can be difficult to scale as you start to shard out your database.

## Stateless authentication

An alternative approach is "stateless" authentication, which addresses the scalability and latency challenges of stateful authentication while introducing different tradeoffs.

The stateless authentication flow operates as follows. This example assumes that the user already signed up and their credentials are stored in a database.

1. The user initiates authentication by navigating to `example.com/sign-in`, entering their credentials (e.g. username/password), and submitting the form, usually by clicking a "submit" button. This makes a request to the server with the credentials.
2. The server validates the credentials against a database. Upon successful validation, it generates a [cryptographically signed token](https://clerk.com/docs/guides/how-clerk-works/tokens-and-signatures.md) containing essential user data like the user ID and any relevant [claims](https://clerk.com/glossary.md#claim). JSON Web Tokens (JWTs) are by far the most common form of signed tokens in the modern web.
3. The server responds to the browser's request by sending the token in a [`Set-Cookie` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) in the response. This token serves as a self-contained proof of authentication, and will be included in future requests from the browser in order to authenticate the user.

   [View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/stateless-auth.mp4)
4. The next time the browser sends a request to the server, it sends the cookie containing the token. The server verifies the signature of the token to ensure that it's valid, and then uses the user ID within the token to fetch any required user data and process the request.

   [View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/stateless-auth-2.mp4)

While more complex to implement, this stateless model offers significant advantages. Because verifying the JWT doesn't require interacting with a database, the latency overhead and scaling challenges caused by database lookups are eliminated, leading to faster request processing.

> [!QUIZ]
> How exactly does stateless authentication mitigate database scaling challenges?
>
> ***
>
> When you are first building an application, a single database instance is often sufficient to handle your traffic. As your application grows, you'll need to scale your database to manage increased load. This typically involves creating multiple database copies or splitting the database into multiple instances through a process called [sharding](https://en.wikipedia.org/wiki/Shard_(database_architecture)).
>
> Sharding involves dividing a database into smaller, more manageable databases (called shards), each handling a subset of the total data. To manage this complexity, a [load balancer](https://en.wikipedia.org/wiki/Load_balancing_(computing)) often serves as an entry point that directs traffic to ensure no single database instance becomes overwhelmed.
>
> Keeping multiple database instances synchronized is a challenging problem that software engineers have grappled with for decades. The potential consequences of unsynchronized instances can be significant. For example, if a user signs in on one database instance and a subsequent request for that user's data is routed to an unsynchronized instance, the user might encounter a confusing authentication error.
>
> Stateless authentication offers an elegant solution. By using [signed tokens](https://clerk.com/docs/guides/how-clerk-works/tokens-and-signatures.md) that contain all necessary authentication information, the server can verify a user's credentials **without direct database interaction**, effectively bypassing the synchronization complexities that arise in traditional, stateful authentication methods.

However, this approach also comes with important technical tradeoffs. The most significant limitation is that **JWTs cannot be revoked** due to their self-contained nature. Since JWT validation happens locally without consulting a central authority (i.e., they never "phone home"), there's no direct mechanism to invalidate them before their natural expiration.

This creates challenges for session management. To forcibly terminate a user's session, you have two suboptimal choices:

1. Wait for the JWT to expire naturally.
2. Rotate the signing keys, which invalidates all active sessions across your entire user base, signing out all of your users.

Furthermore, even after rotating keys, the revocation may be delayed if your application caches the public key used for verification - a common practice for performance optimization. The cached key would continue to validate the old tokens until the cache expires.

This limitation poses significant security risks, as it hampers your ability to quickly respond to security incidents that require immediate session termination for specific users.

## Clerk's authentication model

Clerk implements a hybrid authentication model that combines the benefits of both stateful and stateless approaches while mitigating their respective drawbacks, at the cost of adding a substantial amount of complexity to the implementation on Clerk's side. However, for a developer implementing Clerk, like you, this is all upside since the complexity is handled internally by Clerk.

The hybrid model incorporates the same flow when signing in as the stateless flow, but with a key change: **the session token's expiration is decoupled from the session lifetime**. See the following section for more details.

### Authentication flow

This example assumes that the user already signed up and their credentials are stored in a database.

1. The user initiates authentication by navigating to `example.com/sign-in`, entering their credentials (e.g. username/password), and submitting the form, usually by clicking a "submit" button. This makes a request to the server with the credentials.
2. The server validates the credentials against a database and, upon successful validation:
   - Creates a session record in the database (stateful component).
   - Generates a [signed JWT](https://clerk.com/docs/guides/how-clerk-works/tokens-and-signatures.md) with its expiration set to the session lifetime (stateless component) - this JWT is stored as a cookie on the FAPI domain, and is not accessible to the application. Clerk calls this the **client token**.
   - Generates a **second** signed JWT that expires after 60 seconds which is returned directly to the application and contains user data like the user ID and other claims. Clerk calls this the **session token**.
3. The server responds to the browser's request by sending the **client token** in a [`Set-Cookie` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) in the response, which is set on the FAPI domain. Clerk's client-side SDK then makes a request to FAPI to get a **session token** and sets it on your app's domain.

   > [!QUIZ]
   > Why doesn't Clerk set a **session token** using the `Set-Cookie` header when its setting the **client token**?
   >
   > ***
   >
   > This is a great test of your mastery of [how cookies work](https://clerk.com/docs/guides/how-clerk-works/cookies.md)!
   >
   > Remember, **the domain of a cookie can only be set as the domain of the server that set the cookie**. In this case, the server returning the request to your app is FAPI. For the **`__client` cookie**, this is ok, since the **`__client` cookie** needs to be set on FAPI. However, the **`__session` cookie** needs to be set on your app's domain, not on FAPI. So, FAPI returns the **JWT value** of the `__session` cookie in its response, and when the Clerk client-side SDK integrated in your app receives the response, it gets the **JWT value** and uses JavaScript to set the **`__session` cookie** on your app directly, since the JavaScript is running on your app's domain.

   [View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/hybrid-auth.mp4)
4. And just like stateless auth, the next time the browser sends a request to the server, it sends the cookie containing the **session token**. The server verifies the signature of the token to ensure that it's valid, and then uses the user ID within the token to fetch any required user data and process the request.

   [View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/hybrid-auth-2.mp4)

So far, this is the same as stateless auth, with one key distinction: the session token's expiration time. This is because normally, in stateless authentication implementations, the token's expiration is set to match the intended session duration - commonly ranging from one week to one month. But since JWTs can't be revoked, if a token is compromised, the attacker has the entirety of the session lifetime to be able to take over the user's account. This will be several days at least on average, if not several weeks.

Clerk's model mitigates this issue by setting an extremely short session token lifetime of 60 seconds. Normally, a Clerk token will have already expired before an attacker gets the chance to even try to use it. This is great for security, but it does create a complication in the authentication flow, as signing users out every 60 seconds would not be an acceptable user experience. So, for this architecture to work, **it must decouple token expiration from session lifetime**. To make this happen, Clerk introduces a new "token refresh" mechanism that runs in the background and is responsible for refreshing the token every minute.

### Token refresh mechanism

To maintain session continuity despite the 60-second token lifetime, Clerk's frontend SDKs implement an automatic refresh mechanism that:

- Runs on a 50-second interval (allowing 10 seconds for network latency).
- Makes requests to the [`/client/sessions/<id>/tokens` endpoint](https://clerk.com/docs/reference/frontend-api/tag/sessions/post/v1/client/sessions/%7Bsession_id%7D/tokens){{ target: '_blank' }}.
- Updates the session token before expiration.

If you open the network tab in your browser's developer tools on a Clerk application, you will see this request being sent, and a session token being returned in the response.

This approach provides several security benefits:

- Minimizes the window of opportunity for token misuse
- Maintains the ability to revoke sessions quickly
- Preserves the performance benefits of stateless authentication

[View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/renewal.mp4)

<br />

[View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/invalidation.mp4)

## Clerk's cookies & tokens in detail

> To understand Clerk's architecture, it's important to have a solid foundational understanding of how browser cookies work at a detailed level. If you need a refresher on cookie fundamentals, including domain scoping, `SameSite` policies, and `HttpOnly` flags, see the [guide on cookies](https://clerk.com/docs/guides/how-clerk-works/cookies.md).

If the token lifetime is 60 seconds, how does Clerk know how long your entire session lifetime is?

Clerk's authentication model relies on two distinct tokens - a "client token" and a "session token". Let's break down each of these tokens and how they are configured as cookies.

### Client token

The client token is a long-lived token that is stored in the `__client` cookie, which is set on your FAPI domain.

- **Cookie name:** `__client`
- **Contents:** A Clerk-signed JWT containing:
  - `id`: Unique client identifier
  - `rotating_token`: [Anti-session-fixation](https://clerk.com/docs/guides/secure/best-practices/fixation-protection.md) token that rotates on each sign-in across any device
- **Domain:** Set on your FAPI domain (`clerk.example.com`), rather than on your app domain (`example.com`). It's set on the FAPI domain as a security measure - for example, if your app logs are leaked, they wouldn't contain client token values, since it's scoped to a different domain.
- **Expiration:** Browser dependent. The `Expires/Max-Age` of this cookie is limited by the browser.
- **HttpOnly:** Yes
- **SameSite:** Lax

The client token serves as the source of truth for authentication state. When a user signs in, Clerk either creates a new client token or rotates the existing token's `rotating_token` value. A valid client token enables Clerk to generate short-lived session tokens for the application.

**The client token is only used in production environments**.

In production, Clerk uses a [client token](https://clerk.com/docs/guides/how-clerk-works/overview.md#client-token) to represent the user's session. This client token is stored as an `HttpOnly` cookie (`__client`) on the [Clerk FAPI](https://clerk.com/docs/guides/how-clerk-works/overview.md#the-frontend-api) domain. Because, in production, FAPI is hosted on a subdomain of your application (e.g., `clerk.example.com`) via a CNAME record, your app's frontend and FAPI are on the same site. This allows the client token to be securely and reliably sent with each request from your frontend to FAPI using **same-site** cookies.

However, in development, your app's frontend typically runs on a `localhost` domain, while FAPI for development instances is hosted on a domain ending with `accounts.dev`. As a result, requests from your app's frontend to FAPI are **cross-site**, and the client token cannot be securely and reliably stored or transmitted using cookies.

> [!QUIZ]
> Why can't the client token be securely and reliably stored and transmitted in a cookie when cross-site requests are made?
>
> ***
>
> In order for cookies to be sent cross-site, the cookie would need to be set with [`SameSite=None`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#none). In theory this would resolve the issue, but in reality, modern browsers are increasingly restrictive on cross-site cookie behavior due to the abuse of cookies for tracking and advertising. Safari specifically [does not support `SameSite=None` for cookies at all](https://support.apple.com/guide/iphone/browse-the-web-privately-iphb01fc3c85/ios) unless a browser flag is enabled. Other browsers also have restrictions around cross-site cookie behavior, which leads to a frustrating and unreliable experience for local development.

To address these limitations, development instances use a different approach to maintain session state. An object called the "dev browser", which is linked directly to the client token within Clerk's internals, is used to maintain session state across the session lifetime, and is transmitted via querystring (`__clerk_db_jwt`) rather than via cookie. This strategy allows for a more reliable experience for local development, as it does not require the use of cross-site cookies.

While this enables smooth local development workflows, it is not secure enough for production use. Including a sensitive value like the client token in a querystring is not a strong security practice, as it can be seen directly in server logs, browser history, internet providers' logs, and could be potentially intercepted by third-parties via malicious browser extensions or network interceptors. This is why the `__clerk_db_jwt` object is not used in production instances and the same-site cookie (`__client`) is used instead.

For this reason:

1. Never deploy a development environment to production.
2. Do not rely on `__clerk_db_jwt` in your application code, as it will break in production.

For more information on the differences between development and production environments, see the [dedicated guide on Clerk environments](https://clerk.com/docs/guides/development/managing-environments.md#session-architecture-differences).

### Session token

The session token is a short-lived token that is stored in the `__session` cookie, which is set on your app's domain.

- **Cookie name:** `__session`
- **Contents:** A Clerk-signed JWT containing [a set of default claims](https://clerk.com/docs/guides/sessions/session-tokens.md#default-claims). Can be customized in the Clerk Dashboard to include additional claims.
- **Domain:** Set on your application's domain directly, scoped strictly so it cannot be shared across subdomains. This is done to prevent a different app on a different subdomain from being able to take over your users' accounts. If you need to send the session token value across subdomain boundaries, such as from `example.com` to `api.example.com`, you can [put the token in a `request` header instead](https://clerk.com/docs/guides/development/making-requests.md).
- **Expiration:** 60 seconds
- **HttpOnly:** No - must be able to be accessed by client-side SDKs
- **SameSite:** Lax

When your app makes a request from the frontend to your backend, if the backend is on the same origin, the `__session` cookie will automatically be sent along with the request. Your backend can then [cryptographically verify](https://clerk.com/docs/guides/sessions/manual-jwt-verification.md) the session token's signature and extract the user ID and other claims.

> [!QUIZ]
> Why is the `__session` cookie not `HttpOnly`? Is this a security issue?
>
> ***
>
> Setting cookies as `HttpOnly` is generally recommended to prevent client-side JavaScript access, reducing the risk of cross-site scripting (XSS) attacks. However, due to Clerk's architecture, this approach wouldn't work.
>
> Remember that FAPI is hosted on a different origin than your app. Let's say, in production, your app is `example.com`. Then FAPI is `clerk.example.com`. Since cookies are only sent to the domain that set them (or its subdomains, depending on the `Domain` attribute), any cookie set by FAPI would be scoped to `clerk.example.com` and would not be sent to `example.com`.
>
> To work around this, Clerk returns the session token from FAPI after a user signs in, and the client-side SDK used by your app (e.g., React SDK) sets the `__session` cookie containing the session token on your app's domain **via JavaScript**. A benefit of this is that it allows the token's value and session claims to be accessed on the client-side. This is often quite valuable, as it allows developers to send the session token as a custom header in requests and also makes it possible to use a subdomain (like `api.example.com`) for your backend. However, because it's set client-side, it cannot be `HttpOnly`, making it more vulnerable to XSS attacks.
>
> Clerk mitigates this risk substantially by setting the session token's expiration to a very short duration of 60 seconds. For an XSS attack to succeed, the developer would need to ship a vulnerability on their site, and the attacker would need to exfiltrate users' tokens and use them to take over accounts in an average of less than 30 seconds. This is an extremely difficult scenario and extremely unlikely to be an issue for the most common type of XSS attacks, which are broad sweeps across many sites to harvest tokens, typically after a CVE is disclosed, that can take advantage of those who didn't patch their sites in time. Additionally, Clerk's fast-expiring token provides an extra layer of protection against other attacks that `HttpOnly` cookies alone wouldn't mitigate.
>
> Summary: Clerk's `__session` cookie is not `HttpOnly` because it needs to be accessible to the client-side SDKs. However, Clerk mitigates the risk of XSS attacks by setting the session token's expiration to a very short duration of 60 seconds.

## The Handshake

The short-lived nature of session tokens introduces a case that requires special handling. Consider this scenario: A user signs in to your application and then closes their browser tab. When they return after five minutes by opening a new tab, their session token will have expired since the refresh mechanism could not run while the tab was closed. At this point, Clerk needs to determine the user's authentication status and potentially issue a new session token.

For client-rendered applications, this process is straightforward. Clerk's frontend SDK makes a direct request to FAPI with the `__client` cookie. If the client token is valid, FAPI issues and returns a new session token. This is _secure by default_, because only users who are properly authenticated and signed in will have a valid client token in their browser. If the client token is invalid, the user is redirected to the sign-in flow.

However, server-rendered applications present a unique challenge. Server-to-server requests cannot include browser cookies, as cookies are stored by the browser. This means that, if your app's backend made a request directly to FAPI, the client token would not be available with that request, as the request would not flow through the browser. To solve this problem, Clerk implements a "handshake" flow:

1. The server returns a **redirect response** to the browser
2. The browser follows the redirect to FAPI
3. FAPI receives the request with the `__client` cookie
4. FAPI validates the authentication state and issues a new session token

[View video](https://clerk.com/docs/raw/_public/images/how-clerk-works/handshake.mp4)

This server → browser → FAPI request includes the client token, so FAPI is able to verify the user's auth state and issue a new session token securely. This handshake ensures secure token renewal while maintaining the benefits of server-side rendering.

> [!QUIZ]
> Why does handshake do a redirect? Why can't it make a fetch request to FAPI and get a new token back that way? Not needing to redirect would be a better user experience.
>
> ***
>
> First remember that with Clerk, the **session token** is the short-lived one that refreshes every 60 seconds, and the **client token** is the long-lived one whose expiration is your session's lifetime.
>
> Now let's get back to the previous scenario: you open your server-rendered application after having closed the tab for a few minutes, and now you have an expired session token. Normally, to refresh your session token, you make a request to FAPI with the `__client` cookie that contains the long-lived client token. FAPI will validate the client token and check if there's an active session. If there is, it will mint a new session token and send it back.
>
> But in this scenario, you have only an invalid (expired) session token, and since your app is rendering on the server, you cannot send a request with the client token since it's stored in a cookie in the browser and server-to-server requests don't flow through the browser. So the question becomes this: how do you get a new session token?
>
> - You could send the expired session token, but then any attacker with an expired session token would be able to mint a new one and hijack your session.
> - You could verify that the request only comes from your application's domain, but then any attacker could just send an `http` request with a spoofed domain value and get a token on your behalf.
>
> Neither of these options are secure. Clerk's unique authentication model requires a new mechanism to get a new session token. This is where handshake comes in - here's how it works:
>
> 1. A request is made to an application that uses Clerk on the server.
> 2. The Clerk SDK used by your app determines the authentication state of the request: `signed-in` (valid session token), `signed-out` (no session token), or `handshake` (expired session token).
> 3. If the authentication state is `handshake`, meaning Clerk knows the session token is expired but can't be sure if the user is signed in or out, the Clerk SDK responds with a 307 redirect to the handshake endpoint: `fapi/v1/client/handshake`.
> 4. The handshake endpoint gets information about the current session and returns a handshake payload. The encoded handshake payload contains a list of `Set-Cookie` header directives to be passed along with the final response.
>    - If the session is active, a new, valid `__session` cookie is returned.
>    - If the session is inactive, the `__session` cookie is wiped and the request will be treated as signed out.
> 5. The handshake endpoint redirects back to the host application along with the handshake payload, encoded either in the URL (development) or as a cookie (production).
> 6. The handshake payload is parsed and `Set-Cookie` headers are set on the response by the Clerk SDK.
> 7. If an updated `__session` cookie is returned, the JWT is verified. If verification is successful, the request is treated as signed in.
> 8. If an updated `__session` cookie is not returned, the request is treated as signed out.
>
> This makes it such that you can't provide proof of authentication via an API call - the proof comes from the **`__client` cookie** on FAPI which can't be tampered with by attackers since it's not set on the application domain and is `HttpOnly`.

---

## Sitemap

[Overview of all docs pages](https://clerk.com/docs/llms.txt)
