πŸ“ Sign Up | πŸ” Log In

← Root | ↑ Up

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ πŸ“„ shadcn/directory/clerk/clerk-docs/guides/how-clerk-works/overview β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

╔══════════════════════════════════════════════════════════════════════════════════════════════╗
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘

title: How Clerk works description: Learn how Clerk is architected and how it works under the hood.

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.

The frontend API

When you create a new application through the Clerk Dashboard, 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 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 instance 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:

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

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

[!NOTE] In previous versions of Clerk, the Frontend API 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{{ 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.

Some tasks, such as signing up a user{{ 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{{ 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.

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() method or the <SignIn /> 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 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, but not the HTML structure or the logic/ordering of how the authentication flow works.

Clerk Elements

The next level of abstraction is Clerk Elements, a headless UI library that provides the foundational building blocks used in Clerk's prebuilt components. Similar to established libraries like Radix, Reach UI, and Headless UI, Clerk Elements exposes a set of unstyled React components that handle complex authentication logic while giving you complete control over the presentation layer. Clerk Elements is still in beta, and only supports sign-up and sign-in flows, with more components planned for future releases.

Customizability: You have full control over both the CSS and the HTML structure of the components, but you can't change 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". 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{{ 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. 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 the password and comparing it with a stored password hash. Upon successful validation, it creates a new 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 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. <Video src="/docs/images/how-clerk-works/stateful-auth.mp4" width="1400" height="700" autoPlay muted loop playsInline controls />
  4. The next time the browser sends a request to the server, it automatically includes 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. <Video src="/docs/images/how-clerk-works/stateful-auth-2.mp4" width="1400" height="700" autoPlay muted loop playsInline controls />

[!NOTE] 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 (ensures attacker can't sniff it) and ensure the cookie is set as 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 containing essential user data like the user ID and any relevant claims. 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 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. <Video src="/docs/images/how-clerk-works/stateless-auth.mp4" width="1400" height="700" autoPlay muted loop playsInline controls />
  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. <Video src="/docs/images/how-clerk-works/stateless-auth-2.mp4" width="1400" height="700" autoPlay muted loop playsInline controls />

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.

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 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 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 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 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!

    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.

    <Video src="/docs/images/how-clerk-works/hybrid-auth.mp4" width="1400" height="700" autoPlay muted loop playsInline controls />

  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. <Video src="/docs/images/how-clerk-works/hybrid-auth-2.mp4" width="1400" height="700" autoPlay muted loop playsInline controls />

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{{ 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
<Video src="/docs/images/how-clerk-works/renewal.mp4" width="1400" height="700" autoPlay muted loop playsInline /> <br /> <Video src="/docs/images/how-clerk-works/invalidation.mp4" width="1400" height="700" autoPlay muted loop playsInline />

Clerk's cookies & tokens in detail

[!TIP] 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.

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:
    • session_id: Unique session identifier
    • rotating_token: Anti-session-fixation 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: Set to your session lifetime, which is 7 days by default. Can be configured in the Clerk Dashboard.
  • 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.

<Include src="_partials/clerkdbjwt-vs-client-cookie" />

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.

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. 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.
  • 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 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
<Video src="/docs/images/how-clerk-works/handshake.mp4" width="1920" height="1080" autoPlay muted loop playsInline />

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.

{/* Future sections to add

  • the anatomy of clerk's sign up and sign in flows
  • subdomain session sharing
  • satellite domains */}
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

← Root | ↑ Up