# Clerk Blog — Engineering

# Adding Clerk auth to your CLI
URL: https://clerk.com/blog/adding-clerk-auth-to-your-cli.md
Date: 2026-06-04
Category: Engineering
Description: A practical OAuth 2.0 + PKCE localhost-callback pattern for authenticating CLI users with Clerk.

CLI auth usually goes wrong in one of two ways: users paste API keys into config files, or developers invent long-lived tokens that never quite expire.

The better pattern is the one developers already recognize from tools like Vercel, Stripe, Supabase, and GitHub: open the browser, sign in, return to the terminal, and keep the session in the OS keychain. Under the hood, that usually means OAuth 2.0 Authorization Code with [Proof Key for Code Exchange (PKCE)](/glossary#code-exchange-pkce) and a one-shot localhost callback.

Until now, Clerk's docs haven't laid this pattern out in one place. Last summer, developer [Erik Steiger](https://www.ersteiger.com/posts/clerk-cli-auth/) spent three days reverse-engineering the flow to wire Clerk into a Rust CLI. His writeup is excellent, and it exposed a real docs gap.

This post puts the flow in one place with Clerk OAuth primitives you can use today and a small TypeScript implementation you can copy, adapt, or vendor.

## Two paths, both valid

When a CLI needs to authenticate a human user through a browser, you have two standard choices:

|                     | Localhost callback + PKCE                                            | Device Authorization Grant (RFC 8628)                                     |
| ------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Flow                | CLI opens browser → browser redirects to `127.0.0.1:{port}/callback` | CLI shows a `user_code` → user types it on a secondary device → CLI polls |
| Incoming port       | Required                                                             | Not required                                                              |
| UX friction         | Low (browser auto-opens, redirect just works)                        | Medium (typing codes, secondary device)                                   |
| Headless / CI       | Poor                                                                 | Excellent                                                                 |
| Real-world examples | `vercel`, `supabase`, Stripe CLI (hybrid), Erik's post               | `gh` (GitHub CLI)                                                         |

The right choice depends on where the CLI runs. `gh` picked device flow because GitHub cares about CI parity; `vercel` picked localhost callback because their audience is desktop-first.

Clerk's backend already supports everything the localhost-callback flow needs: [OAuth Applications](/docs/guides/configure/auth-strategies/oauth/how-clerk-implements-oauth) with PKCE (S256), authorize + token + userinfo + revoke endpoints, and loopback redirect behavior for `127.0.0.1` callbacks per [RFC 8252 §7.3](https://datatracker.ietf.org/doc/html/rfc8252#section-7.3). So that's where we start. (We'll come back to device flow at the end.)

## The five moving parts

```
┌──────────┐  1. /oauth/authorize        ┌──────────┐
│   CLI    │ ──────────────────────────► │ Clerk UI │
└────┬─────┘                             └────┬─────┘
     │                                        │
     │                                        │ 2. user approves
     │                                        ▼
     │       3. redirect ?code=...     ┌─────────────────────┐
     │ ◄────────────────────────────── │ 127.0.0.1:{port}/cb │
     │                                 └─────────────────────┘
     │
     │       4. POST /oauth/token      ┌────────────────┐
     │ ──────────────────────────────► │  Clerk backend │
     │ ◄────── access + refresh ────── │                │
     │                                 └────────────────┘
     │
     │       5. keychain.set(session)
     ▼
┌─────────────┐
│ OS keychain │
└─────────────┘
```

1. **Generate PKCE + state.** `code_verifier` is generated from 32 random bytes, giving 256 bits of entropy and encoding to a 43-character base64url string; `code_challenge = base64url(sha256(verifier))`; `state` is a separate [Cross-Site Request Forgery (CSRF)](/glossary#cross-site-request-forgery-csrf) token. A dozen lines of `node:crypto`.
2. **Spin up the listener.** A one-request HTTP server on `127.0.0.1:0` (random port). Use `127.0.0.1` over `localhost` so the backend doesn't have to resolve DNS weirdness, and over `0.0.0.0` so other hosts on the network can't reach the listener.
3. **Open the browser** to `{issuer}/oauth/authorize?response_type=code&client_id=…&redirect_uri=http://127.0.0.1:{port}/callback&code_challenge=…&state=…&code_challenge_method=S256`. When the callback arrives, validate that `state` matches the original value before exchanging the code.
4. **Exchange the code** with a POST to `/oauth/token` with `grant_type=authorization_code` + the `code_verifier`. You get back `access_token`, `refresh_token`, `expires_in`.
5. **Store the token set** in the OS keychain (Keychain on macOS, Credential Manager on Windows, Secret Service on Linux). Fall back to a `chmod 600` JSON file if the keychain isn't available.

## Setup (one-time)

Before the CLI can start the flow, register an [OAuth Application](/docs/guides/configure/auth-strategies/oauth/overview) in your Clerk dev instance. The CLI only needs two config values at runtime: the issuer URL and the OAuth application's `client_id`.

You can create the OAuth application three ways:

- **Clerk Dashboard** — In the [**OAuth Applications**](https://dashboard.clerk.com/~/oauth-applications) page, create an application. Set the redirect URI to `http://127.0.0.1/callback`, enable public client + PKCE, and request the scopes you need.
- **`curl` against the Backend API** with the instance's secret key, if you prefer scripting setup.
- **The [Clerk CLI](/docs/cli)** if you already have it installed:

  ```bash {{ filename: 'terminal', lineNumbers: false }}
  clerk link --app app_...

  clerk api /oauth_applications --app app_... -X POST -d '{
    "name": "my-cli",
    "redirect_uris": ["http://127.0.0.1/callback"],
    "scopes": "profile email offline_access",
    "public": true,
    "pkce_required": true
  }' --yes
  ```

None of these setup paths are required at runtime.

## Small enough to audit

The example implementation lives at **[clerk/cli-auth-example](https://github.com/clerk/cli-auth-example)**. It's designed to be copied, adapted, or vendored into your own CLI. It is intentionally small and inspectable.

Directory layout:

```
src/
├── types.ts                    # the public contract
├── clerk-cli-auth.ts           # the class
└── lib/
    ├── pkce.ts                 # node:crypto
    ├── auth-server.ts          # node:http, one-shot server
    ├── token-exchange.ts       # fetch + error mapping
    └── credential-store.ts     # keychain, file, memory strategies
```

These are the traps worth handling if you copy the code:

**The auth server must respond to the browser *before* resolving its promise.** If the CLI "moves on" to the token exchange before the browser finishes rendering the success page, the user sees a hang. Respond first, then fire your callbacks inside `res.end()`.

**Don't hard-code a fixed callback port.** Bind to `127.0.0.1:0` and let the OS pick. Register your redirect URI as `http://127.0.0.1/callback`; at runtime, send the actual `http://127.0.0.1:{port}/callback` redirect URI. Clerk accepts loopback callbacks with dynamic local ports for this CLI pattern. Fixed ports cause collisions.

**Your keychain layer needs a file fallback.** Not every environment has a keychain. CI runners, Docker containers, SSH sessions over tmux. `@napi-rs/keyring` can throw on init. Wrap every `get/set/delete` in a try/catch that falls back to a `chmod 600` JSON file, and log a warning so users know where their credentials actually live.

## What this post doesn't cover

**Device flow.** RFC 8628. Best for CI, headless servers, and environments where the CLI can't open a browser. Outside the scope of this post.

**Token refresh semantics.** The example refreshes when the access token is within 30s of expiry. That's fine for interactive CLIs but naive for long-running workers. A production implementation should offer configurable skew and retry behavior.

**Token revocation on logout.** The example clears local credentials. A production SDK should also revoke the refresh token server-side when possible.

**Machine-to-machine tokens.** Different primitive (machine tokens), different use case (service-to-service, not user auth). [Covered separately in the docs](/docs/guides/development/machine-auth/overview).

## The next path should be shorter

Adding user auth to a CLI shouldn't be a three-day spike. The building blocks are already in Clerk: [OAuth Applications](/docs/guides/configure/auth-strategies/oauth/how-clerk-implements-oauth), PKCE, [token verification](/docs/guides/configure/auth-strategies/oauth/verify-oauth-tokens), refresh grants, and [machine-to-machine auth](/docs/guides/development/machine-auth/overview).

If your CLI runs on a user's machine, start with localhost callback + PKCE. If it needs headless or CI parity, use device flow when available. Either way, don't make users paste credentials into config files.

The example lives at [clerk/cli-auth-example](https://github.com/clerk/cli-auth-example). Fork it, copy it, or just lift the pattern into your own codebase. Credit to Erik Steiger for [writing up the gap](https://www.ersteiger.com/posts/clerk-cli-auth/). The next developer's path should be a lot shorter.

Thoughts, questions? Reach out on [X](https://x.com/clerk) or in the [Clerk Discord](https://clerk.com/discord).

---

# Going to production with Clerk Deploy
URL: https://clerk.com/blog/clerk-deploy.md
Date: 2026-05-29
Category: Engineering
Description: Take a Clerk app to production with a single, resumable command, all from your terminal.

Your app's at a point where it's almost ready to go live.

Users can sign up, sign in, and manage their accounts. Organization owners can create workspaces and invite members. Billing works as expected; your plans are set up, and your users can subscribe to them. It feels *shippable*.

Not yet, though.

You still need to set up a production instance, which involves a few steps spread across different pages and external services. The dashboard handles this well, and on a focused day, the whole thing can be done in under 30 minutes.

But what if the final stretch from 'almost ready' to 'ready' had fewer clicks and less context switching?

Our new [CLI](/docs/cli) brings that flow directly to your terminal.

Meet `clerk deploy`.

## A different kind of deploy

`clerk deploy` doesn't ship code. Your hosting platform still takes care of that. Instead, it's a helpful wizard that walks you through everything Clerk needs to take your app to production.

In an interactive terminal session, you get the wizard. Without one (or with the `--mode agent` flag), `clerk deploy` instead returns a JSON snapshot of your deployment state so coding agents can check progress and suggest the next step. For agents, this command is purely informational and doesn't perform any changes.

If you need automated deployments for CI/CD pipelines or agent workflows, you can compose the same flow from a handful of lower-level Clerk commands. Stay tuned for a follow-up post.

Now let's see what the wizard can do.

## How it works

The wizard runs from your project root.

```bash {{ prompt: '$' }}
clerk deploy
```

It starts with a pre-flight pass over your linked app, confirming the app is ready to deploy, identifying which OAuth providers need production credentials, and checking for any deployment already in progress.

![A clerk deploy session showing the pre-flight checks, plan, and production domain prompt](./deploy-example.png)

On the first run, the wizard walks through the steps below. If a production instance already exists but isn't fully configured, it picks up from the next pending step.

### Production domain

The wizard begins by asking for your production domain. Confirming it creates a fresh production instance in your Clerk account and registers the domain to it.

The deploy is resumable from this point on.

### CNAME records

Next is DNS. The wizard prints the CNAME records to add at your DNS provider. Propagation runs in the background while OAuth setup continues, with verification happening at the end.

### OAuth credentials

For each provider detected, the wizard shows the exact redirect URI to register, then prompts for the Client ID and Client Secret. Google can also load OAuth credentials from a JSON file. Apple reads the `.p8` private key from a file path.

### Verification

After OAuth, the wizard verifies DNS, SSL, and email DNS together. If propagation is still in progress, `clerk deploy status` shows where each one stands, plus OAuth provider readiness and the next step to take. Rerunning `clerk deploy` resumes verification.

### Production summary

Once verification clears, a production summary prints with your live URL and production instance ID.

## Ready to ship?

With `clerk deploy`, the CLI now covers Clerk's full arc from development to production. And while the dashboard is always there when you need it, that's one less side-quest pulling you out of flow. The gap between 'almost ready' and 'live' just got smaller.

So this is your nudge. If your app's been on localhost too long, what's stopping you from going to production? Make today the day.

Open a terminal and give it a try:

```bash {{ prompt: '$' }}
clerk deploy
```

Happy shipping!

[Read the docs](/docs/cli)

---

# Clerk Init: The fastest way to start a new project
URL: https://clerk.com/blog/clerk-init.md
Date: 2026-05-11
Category: Engineering
Description: Bootstrap a new project in seconds using Clerk CLI`s `init` command

Clerk's been quietly building toward a moment where you can start a new app in a single command. That command is `clerk init`.

Shipped with the [Clerk CLI](/blog/introducing-clerk-cli), `clerk init` covers the essential scaffolding for some of the most popular frameworks. Run this command in an empty directory and Clerk delivers a fully bootstrapped project with auth pages and route protection ready to go.

This post demonstrates how quick and easy it is to get a fully working app with auth, organizations, and billing configured - all without leaving your terminal or agent.

Break out your terminal and follow along.

## Starting from zero

From an empty directory, run:

```bash {{ prompt: '$' }}
clerk init
```

Follow the interactive prompts for project name, framework, and package manager. Choose "Next.js" and Bun for this example.

Clerk scaffolds a new Next.js project with batteries included. Before `init` completes, the CLI offers to install [agent skills](https://github.com/clerk/skills) like `clerk`, `clerk-setup`, and `clerk-nextjs-patterns`. You accept, pick your agent, and select "Project" for the installation scope.

A few seconds later, the terminal prints the next steps:

```bash {{ prompt: '$' }}
cd my-clerk-next-app && bun dev
```

`localhost` loads. There's a sign-in page. There's a sign-up page. They just work. No Clerk account created, no API keys copied, no dashboard opened. Clerk development keys are generated on-the-fly so you can start building right away.

## Going beyond the bootstrap

Building a SaaS product with Clerk means tapping into some of our other key products like [Organizations](/docs/guides/organizations/overview) and [Billing](/docs/nextjs/guides/billing/for-b2b). The bootstrapped app gets you started, but you'll want to link your new project to your account for greater flexibility and control. Once your project is linked, you can configure your instance straight from the terminal.

Run `clerk link` from the project root:

```bash {{ prompt: '$' }}
clerk link
```

If you're not already authenticated, the CLI opens a browser tab for sign-in. From there, `clerk link` claims the app you've been building.

Next, pull the credentials from the dashboard into your local environment:

```bash {{ prompt: '$' }}
clerk env pull
```

> \[!WARNING]
> `clerk env pull` writes credentials into the env file Clerk picks based on your framework's conventions (for Next.js, `.env.local`). Any existing Clerk publishable or secret keys in that file will be overwritten. If you've already populated the file with your own keys for other purposes, those will be safely preserved.

Restart the dev server (if it doesn't auto-refresh), and you're connected to your Clerk instance.

## Enabling features

This used to be the part where you'd context-switch in and out of the dashboard to turn features on. With your project linked, that same workflow gets reduced to a single step: `clerk enable`.

Start with Organizations:

```bash {{ prompt: '$' }}
clerk enable orgs
```

Done. Organizations are now enabled. You didn't leave your terminal.

Want to get paid? Turn on Billing:

```bash {{ prompt: '$' }}
clerk enable billing --for orgs
```

The CLI offers to install the `clerk-billing` skill. You accept, and now your favorite coding agent just got more superpowers.

`clerk enable` is designed for incremental opt-in to Clerk's core services. Each feature you turn on ships with a matching skill, and that pairing wasn't an afterthought, it was the point. Enabling a feature on your instance should also enable your agent's understanding of how to use it.

Organizations and Billing are the start. Expect more features to land behind `clerk enable` over time.

## Pairing with your agent

Stacked on the `init` skills, the `clerk-billing` skill rounds out your agent's Clerk context. One prompt:

> "Add three subscription tiers to my clerk app: Free, Pro, and Enterprise."

Or:

> "Add a /pricing page where users can compare plans and subscribe."

Seconds later, your app has plans configured, a pricing table on the page, and a working checkout your customers can use.

If you're using shadcn/ui, you can now prompt the agent to build out your dashboard:

> "Build a /dashboard layout using a collapsible shadcn sidebar with an inset. The sidebar header should include clerk's org switcher and the footer should have the user button."

It scaffolds the layout, then drops in the right Clerk components for account management and organization switching. You follow up:

> "Add a /developers page where users can create and manage api keys using clerk's api keys component."

Done. Want to add a new login provider? No problem:

> "Add GitHub as a login option for my app."

The agent calls the CLI, runs `clerk config patch` and enables the GitHub provider on your instance.

This is the moment the rhythm starts to emerge. When the agent has the right tools and reference, the friction between what you ask for and what comes back keeps shrinking. There's still tuning - there always will be -  but the corrections get smaller, and the loop starts moving with you instead of against you.

## Adopting Clerk mid-project

Not every project starts with Clerk; `init` is built for those too. Run it inside an existing codebase and Clerk adapts. It detects your setup, installs the SDK, and scaffolds only what's missing. If you're migrating from NextAuth, Auth0, or Supabase Auth, the CLI detects that and links you to the relevant migration guide.

If you already have middleware, Clerk composes with it rather than overwriting it. After scaffolding, it scans for hardcoded keys and leftover imports from previous auth libraries, so you know exactly what to clean up.

That flexibility is the real benefit of the init workflow. It meets a project at whatever stage it's in.

## The sky's the limit

`clerk init`. `clerk link`. `clerk enable`. Three commands lay the foundation. Agent skills take you further so you can ship faster. Some of the most tedious parts of setting up a SaaS application? Gone. Now you can focus on the parts that matter.

This is how getting started should feel.

[Get started with Clerk CLI](/docs/cli)

---

# Clerk for the AI era
URL: https://clerk.com/blog/2026-03-03-clerk-for-the-ai-era.md
Date: 2026-03-03
Category: Engineering
Description: We're doubling down on making Clerk composable by default, with clear APIs and tools that work as well for human developers as they do for agents.

Since 2019, Clerk has focused on bringing a best-in-class developer experience to the auth and user management ecosystem. We built for [frontend developers](https://vercel.com/blog/authentication-for-the-frontend-cloud) newly empowered to ship full-stack applications, armed with React components and platforms like Vercel. [Clerk's prebuilt components](https://www.youtube.com/watch?v=enUuBY3HXh4) were a huge accelerant for this ecosystem. The ability for us to embed so much functionality and logic into so few lines of code felt magical at the time. It still does today.

Things are different now. The industry has undergone a radical shift over the last year. Components remain a powerful, token-efficient abstraction, but it's never been easier to prompt an app into existence, turning what was once days of work into minutes. This shift is reshaping how we build Clerk.

We're doubling down on making a best-in-class developer experience for however you choose to build. We've revamped our APIs for building your core authentication flows, shipped the first version of our agent skills and MCP server, and are continuing investment in tools and APIs that will help Clerk support whatever you're building next.

## Components that work with you

Our components remain a phenomenal way to use Clerk. We've iterated on and modernized the styling capabilities for our components. Our beloved `appearance` prop has gotten a refresh, we've introduced [more flexible theming primitives](https://clerk.com/changelog/2025-07-29-theme-simple), and invested in making sure our components can [match your existing design system](https://clerk.com/changelog/2025-07-23-shadcn-theme). We've also introduced a [dedicated changelog](/docs/reference/components/changelog) for our components, so we can share the iterative improvements you're getting out-of-the-box when you opt to build with Clerk's components.

Components are a powerful way to build with Clerk, and you have always been able to go a long way by bringing your own CSS. To explore our components and their theming capability, check out the [Theme Editor](https://clerk.com/components/theme-editor) our team recently shipped!

![Theme Editor](./theme-editor.png)

## Primitives built for agents

Components are great, but many users want total control over their UI, especially as AI has the ability to build so quickly. This is why we're reinvesting in making our React hooks and lower-level APIs even better.

Our `useSignUp()`, `useSignIn()`, `useCheckout()`, and `useWaitlist()` hooks have been redesigned to make building custom UIs with Clerk easier. We've simplified state management, redesigned the API surface, and removed the need for tedious boilerplate. We've also invested in making sure the updated APIs perform well when used with AI tools, and we're tipping the scales by embedding examples and the latest reference content into our [agent skills](https://github.com/clerk/skills). These primitives are the foundation for [building custom UIs with Clerk](https://clerk.com/docs/guides/development/custom-flows/overview).

```typescript
// signIn is stateful, updates will trigger re-renders
const { signIn, fetchStatus, errors } = useSignIn()

// Step methods map directly to the flow
await signIn.password({ emailAddress, password })
await signIn.emailCode.sendCode()
await signIn.emailCode.verifyCode({ code })

// Read the resource's status directly
signIn.status // 'needs_first_factor' | 'needs_second_factor' | 'complete'

// Built-in fetch state
fetchStatus // 'idle' | 'fetching'

// Structured field-level errors
errors.fields.identifier // "Couldn't find your account"
errors.fields.password // "Password is incorrect"
```

## More copy-to-install examples

Shadcn/ui has become the *lingua franca* of UI development over the last year, and both developers and AI tools have gravitated to the copy-to-install model. We've already launched a [Clerk shadcn/ui registry](https://clerk.com/docs/guides/development/shadcn-cli), with a growing library of copy-to-install examples for developers and AI tools. This model pairs really nicely with the improved primitives, and we're excited to continue building and sharing polished components that you can use as a starting point for your own custom UIs.

![Clerk themes](./themes.png)

## Codified best practices

Clerk's secret sauce expands beyond our components; we have years of experience building authentication and user management flows, processing billions of events, and we're committed to sharing those learnings with you. Auth flows can be deceptively complicated and riddled with edge cases. We want to help you tackle that complexity. The team is hard at work on expanding our [MCP server](https://clerk.com/docs/guides/ai/mcp/clerk-mcp-server), [agent skills](https://github.com/clerk/skills), and building tools that will help ensure your authentication UIs have best practices baked in, and account for edge cases. When you use our agent tooling, you'll get the latest docs and best practices straight from Clerk, whether your building with our components or rolling your own UI.

![Clerk agent skill](./clerk-skill.png)

### Looking forward

The choice to use Clerk doesn't hinge on committing to a specific component abstraction. Whether you're dropping in our components, crafting pixel-perfect UIs by hand, or letting your agents handle it for you, Clerk has your back. We're committed to making Clerk composable by default, with clear APIs that work as well for human developers as they do for agents.

This is just the beginning; we're excited to share our progress over the next few months as we continue to shape Clerk for however you build. Stay tuned...

![Clerk CLI](./clerk-cli.png)

If you want to learn more about recent improvements for a more composable Clerk, check out what we've shipped in [Core 3](/changelog/2026-03-03-core-3).

---

# Add API Key support to your SaaS in minutes
URL: https://clerk.com/blog/add-api-key-support-to-your-saas-with-clerk.md
Date: 2026-02-27
Category: Engineering
Description: Learn how to implement multi-tenant API Key functionality in your SaaS platform without the complexity.

As your SaaS grows, your customers may want to connect your product with the rest of their tools, whether that's running scheduled jobs, syncing data, triggering workflows, or building private internal tools. These integrations all require the same foundational capability: A secure, reliable way to programmatically access your API.

[API keys](/glossary/api-key) remain the simplest and most familiar way for customers to integrate with SaaS products. From a customer's perspective, API Keys should be easy to manage and use. But implementing API Keys correctly in a multi-tenant application takes real work:

- Where do you store keys? How do you rotate or revoke them?
- How do you ensure keys are scoped to the correct user or organization?
- How do you build a secure UI for users or organization members to manage their keys?

Most teams eventually take on the work of building and maintaining their own approach to key management, which often turns into a complex, error-prone, and time-consuming effort.

**Clerk removes that entire burden.**

With Clerk API Keys, you can securely expose your application's functionality through an API for individual users or entire organizations, without having to build the underlying infrastructure yourself.
In this guide, we'll introduce you to Clerk's new API Keys feature and show you how to integrate it into your SaaS product.

> \[!NOTE]
> API Keys are now available for use in public beta. Learn more in [the announcement on our changelog](/changelog/2025-12-11-api-keys-public-beta).

## Follow along

To make this tutorial practical and easy to apply, we'll be working with the AgentOps demo app — an open-source, multi-tenant Next.js application that showcases how to integrate **Clerk's new API Keys** system into a real SaaS product.

This demo includes a simple "Agents" concept (AI assistants) and uses organization-scoped API Keys to ensure that only authorized members of organizations can access resources.

The app features:

- Minimalistic dashboard UI using shadcn
- Combined "Sign-In" and "Sign-Up" flow page powered by Clerk
- An "Organization Settings" page that exposes Clerk's managed `<APIKeys/>` component
- Protected Next.js API CRUD routes to demonstrate organization-scoped token verification

You'll need the following before you get started:

- [A Clerk account](https://dashboard.clerk.com)
- Node and any package manager installed locally (e.g. npm, pnpm, yarn, bun)
- Familiarity with Next.js

If you want to follow along, clone the [API Keys Demo](https://github.com/clerk/demo-api-keys) repository on GitHub and navigate to the directory:

```bash {{ filename: 'terminal' }}
git clone https://github.com/clerk/demo-api-keys
cd demo-api-keys
```

Then, run the following commands to install the dependencies and start the development server:

```npm
npm install
npm run dev
```

## Enabling API Keys in Clerk

To follow along with the demo project, you'll need to enable a few features in your app instance.
But before turning on API Key support, you'll need to decide how keys should be scoped.

Clerk supports two types of keys:

- **User API Keys** - Keys tied to an individual user. Ideal for personal scripts, CLI tools, or user-initiated automations.
- **Organization API Keys** - Keys tied to an organization. Better suited for multi-tenant SaaS apps where members of organizations (or workspaces) need keys to access shared data.

This tutorial will focus on **Organization API Keys**. Before you can use API Keys, you'll need to enable Organizations first.

### Enable Organizations and Roles

To get started, open your app instance in the Clerk dashboard and navigate to **Organizations** > **[Settings](https://dashboard.clerk.com/~/organizations-settings)** from the sidebar on the left.
Toggle **Enable organizations** on if the feature is not already enabled. From here, make sure personal accounts are disabled.

Next, switch to the **[Roles & Permissions](https://dashboard.clerk.com/~/organizations-settings/roles)** tab and grant the following system permissions to your test user:

- `org:sys_api_keys:read`
- `org:sys_api_keys:manage`

These permissions determine who can view, generate, and revoke API Keys within your organization.
In the following sections, you'll integrate API Keys into your application and start protecting your backend routes using Clerk's token verification layer.

Finally, navigate to **Configure** > **Developer** > **[API Keys](https://dashboard.clerk.com/~/platform/api-keys)**.
Toggle the **Organization API keys** option to enable it and click **Save**. Make sure to leave the **User API keys** option disabled.

## Adding API Key management to your SaaS dashboard

Once API Keys are enabled in your Clerk instance, the next step is to expose a place in your product where your users can view, create, and manage their own API Keys. In our [**AgentOps example**](https://github.com/clerk/demo-api-keys), this component is integrated directly into the existing shadcn dashboard layout.

### Using `<OrganizationProfile/>` component

If your application already includes an `<OrganizationProfile/>` or `<OrganizationSwitcher/>` component, Clerk automatically adds an **API Keys** tab when the minimum permissions (`org:sys_api_keys:read`) are present. These permissions are not set by default, so you'll need to apply them to both admin and member roles in the Clerk dashboard.

In the demo app, you can try this by:

1. Switching to an organization using the `<OrganizationSwitcher/>` in the dashboard header
2. Clicking **Manage Organization**
3. Opening the **API Keys** tab

From here, organization members with `org:sys_api_keys:read` can view all active Organization API Keys, and members with `org:sys_api_keys:manage` can create new keys with one click, revoke any key immediately, and copy keys securely for external usage.

Here's the header layout used in the demo:

```tsx {{ mark: [1, 14, 15], filename: 'app/dashboard/layout.tsx' }}
import { OrganizationSwitcher, UserButton } from '@clerk/nextjs'

export default function DashboardLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <SidebarProvider>
      <AppSidebar />
      <SidebarInset className="overflow-hidden">
        <header className="flex h-16 shrink-0 items-center gap-2">
          <div className="flex w-full items-center justify-between gap-2 px-4">
            <OrganizationSwitcher />
            <UserButton />
          </div>
        </header>
        {children}
      </SidebarInset>
    </SidebarProvider>
  )
}
```

When you click **Manage Organization** in the `<OrganizationSwitcher/>` component, you'll see the **API Keys** tab appear automatically if permissions are configured correctly.

### Using the `<APIKeys/>` component

If you'd prefer to have more control over where API Keys appear in your application, Clerk also provides a dedicated [`<APIKeys/>`](/docs/reference/components/api-keys) component that you can use.

[Our demo app](https://github.com/clerk/demo-api-keys/blob/main/src/app/dashboard/layout.tsx) shows how to embed API Key management directly inside your own dashboard pages, which is useful if you want a dedicated “Developer Settings” or “Integrations” section.

Here's the example from the demo:

```tsx {{ mark: [1, 9], filename: 'app/dashboard/settings/api-keys/page.tsx' }}
import { APIKeys } from '@clerk/nextjs'
import { RequestTester } from '@/components/request-tester'

export default function SettingsPage() {
  return (
    <div className="flex flex-col gap-4 p-8 pt-6">
      <h1 className="text-lg font-semibold">API keys</h1>
      {/* Clerk-managed API Keys UI component */}
      <APIKeys />
      <RequestTester />
    </div>
  )
}
```

This version gives you full control over where API Keys appear in your dashboard while Clerk handles security and key management.

## Creating protected backend routes

With key generation enabled, the next step is to allow your API to accept API Keys alongside normal user session tokens. Clerk makes this extremely simple with a single configuration change.

### Using multi-token verification

In server-side functions and API routes, Clerk's [`auth()`](/docs/reference/nextjs/app-router/auth) helper can accept more than one token type.
In our demo app, we've configured our `/api/agents/route.ts` file to accept both session tokens and API Keys.
Below is a simplified example:

```tsx {{ mark: [1, [5, 7]], filename: 'app/api/agents/route.ts' }}
import { auth } from '@clerk/nextjs'

export async function GET() {
  // Needs to have the `acceptsToken: 'api_key'` or it will only accept session tokens
  const res = await auth({
    acceptsToken: ['session_token', 'api_key'],
  })

  if (!res.isAuthenticated) {
    return new Response('Unauthorized', { status: 401 })
  }
  if (!res.orgId) {
    return new Response('Unauthorized: please use a valid org key', { status: 401 })
  }
  // Continue handling request...
}
```

This line tells Clerk to accept:

- Browser-based user sessions (*from logged-in users*)
- API Keys (*passed as a Bearer token in request headers*)

```tsx
acceptsToken: ['session_token', 'api_key']
```

Clerk automatically extracts and verifies the token, determines the organization, and ensures the key has not been revoked.

### Rejecting unauthenticated or cross-org access

Because Clerk includes `orgId` directly in the token, your backend logic becomes extremely straightforward:

- If no `orgId` → reject
- If `orgId` does not match the requested resource → reject
- Otherwise → process the request

This ensures strong multi-tenant isolation and prevents customers from ever accessing resources outside their organization.

## Testing your API Keys

Now that API Keys are enabled and visible in your application’s dashboard, it’s time to test them against real API endpoints in the demo app.
You can use cURL or tools like [Insomnia](https://insomnia.rest/) or [Postman](https://www.postman.com/) to test your API Keys.

Go ahead and create an API Key on the `/dashboard/settings/api-keys` page using the `<APIKeys/>` component from the previous section.

> \[!IMPORTANT]
> After you've generated a key, copy it and store it somewhere safe — **it will only be shown once.**

With your key in hand, you can start making real requests.

### Creating an agent

Let's start by creating a new agent using the `POST /api/agents` endpoint. This endpoint requires a valid **Organization API Key**.

```bash {{ filename: 'terminal' }}
curl http://localhost:3000/api/agents \
 -X POST \
 -H "Authorization: Bearer <ORG_API_KEY>" \
 -H "Content-Type: application/json" \
 -d '{"name": "Support Assistant", "description": "Handles inbound support tickets.", "model": "gpt-5.1"}'
```

If successful, you'll see the newly created agent returned in the response.

```json
{
  "id": "agent_tURoMlenCly4sL2cJXiUN",
  "name": "Support Assistant",
  "description": "Handles inbound support tickets.",
  "model": "gpt-5.1"
}
```

Since every request is tied to your Organization ID, the new agent is automatically scoped to your organization.

Now, try to create an agent with an invalid API Key.
If the key is invalid (or revoked / expired), the request will be rejected with a `401 Unauthorized` response.

You can test the same functionality from a logged-in user context by creating an agent on the `/dashboard` page.

### Listing agents

Next, fetch all agents that belong to your organization.

```bash {{ filename: 'terminal' }}
curl http://localhost:3000/api/agents \
  -H "Authorization: Bearer <ORG_API_KEY>"
```

You should get a response with an array containing the recently created agent:

```json
[
  {
    "id": "agent_tURoMlenCly4sL2cJXiUN",
    "name": "Support Assistant",
    "description": "Handles inbound support tickets.",
    "model": "gpt-5.1"
  }
]
```

### Deleting an agent

Finally, delete an existing agent by using the `DELETE /api/agents` endpoint and sending its `agentId` in the request body:

```bash {{ filename: 'terminal' }}
curl http://localhost:3000/api/agents \
  -X DELETE \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <ORG_API_KEY>" \
  -d '{"agentId": "<AGENT_ID>"}'
```

Clerk validates the API Key, resolves the organization it belongs to, and ensures the deletion request only affects resources owned by that org.

You can test the same functionality from a logged-in user context by deleting an agent on the `/dashboard` page.

### What you've verified

By this point, you've tested that:

- Organization API Keys work alongside user session tokens for backend access
- All requests are scoped to the correct organization
- Requests are rejected if the API Key is invalid

Your platform can support external integrations and every API request passes through Clerk's verification. This mirrors how your real users will interact with your API.

## Conclusion

Adding API Key support to a SaaS product is often a significant engineering effort, especially in a multi-tenant environment where every request must be scoped to the correct organization. Clerk removes that complexity by managing key creation, access controls, token validation, and providing ready-made UI components for your customers.

In just a few steps, you've put all the essential pieces in place:

- Enabling API Keys in your Clerk app instance
- Adding managed UI to your application
- Protecting backend routes with multi-token verification
- Enforcing organization-level data isolation
- Testing endpoints with organization-scoped keys

Taken together, these steps give you a fast, secure foundation for customer-facing integrations—one that you can adapt to your product's needs. With this setup in place, your customers can connect your SaaS to the rest of their workflow with ease.

To explore the complete setup for this guide, check out the [**AgentOps API Keys Demo**](https://github.com/clerk/demo-api-keys).
For more advanced use cases, check out the examples in this [repository](https://github.com/clerk/demos).

Other resources:

- [API keys guide](/docs/guides/development/machine-auth/api-keys) — Complete walkthrough of enabling and using API keys
- [Backend SDK reference](/docs/reference/backend/api-keys/list) — Full API for creating, listing, verifying, and revoking keys
- [Dashboard](https://dashboard.clerk.com/~/configure/api-keys) — Enable API keys for your application

---

# Resilience in Practice: Regional Failover at Clerk
URL: https://clerk.com/blog/resilience-in-practice-regional-failover.md
Date: 2025-08-18
Category: Engineering
Description: See how Clerk's new regional failover kept services running during a cloud provider outage.

On Monday, August 4th, we shared that Clerk had implemented [automatic regional failover](/changelog/2025-08-04-regional-failover)
for critical parts of our infrastructure, a major upgrade to protect against large-scale, regional-level outages.

A few days later, that system was put to the test.

## The August 6th incident

On August 6th, between 02:30 UTC and 04:11 UTC, our primary cloud region experienced intermittent issues.
Outages came in short intervals of 5-10 minutes. During each disruption, our health checks detected failures and
automatically rerouted traffic to our failover region.

From a customer perspective, there was no noticeable disruption. Aside from a few early errors, which were
automatically retried by our SDKs, the only potential impact was a brief increase in API latency during some
failover periods.

### The timeline

*2:55 UTC*: We experienced a sudden spike of `429` responses.

![Graph of 429 response spike](./responses-429.png)

*2:58 UTC*: Our team was alerted about downtime on our services.

![Screenshot of internal alert](./alert.png)

*2:59 UTC*: Investigation began. We noticed that our failover region had already picked up traffic and scaled up its
available containers, explaining why no customers had reported issues.

![Screenshot of request throughput and container count in failover region](./regional-failover-kicks-in.png)

*3:35 UTC*: Google confirmed their internal incident.

![Screenshot of Google confirming incident](./google-incident.png)

*3:50 UTC*: Another switchover to our failover region occurred.

*4:11 UTC*: Google's network stabilized and traffic returned to our primary region.

## Why resilience matters so much to Clerk

As an authentication provider, Clerk sits in front of every application that uses our platform.
This means that if our services experience an outage, the impact is immediate and visible within our customers'
applications.
Even brief interruptions can affect sign-ins, sign-ups, and session management, critical flows for end users.

High resilience isn't just a nice-to-have for us. It's fundamental to ensuring our customers' apps remain
reliable and trusted.

## How our regional failover works

We've always run our services across multiple availability zones to handle localized failures.
But the [June 26th service outage](/blog/postmortem-jun-26-2025-service-outage) highlighted a gap: a single-region
architecture, even with AZ redundancy, is still vulnerable to full regional outages.

Our new setup adds a continuously running failover region:

- **Always-on failover region:** The failover region continuously handles live production traffic to ensure it stays warm, healthy, and ready at all times.
- **Fast detection & switchover:** Health checks trigger an immediate reroute when issues are detected in the primary region.
- **Bidirectional failover:** If the failover region experiences issues, traffic switches back to the primary.
- **Local storage in failover:** Data is replicated to a dedicated storage layer in the failover region, minimizing latency during switchover.

![Regional failover high-level architecture](./architecture.png)

## What's next

This failover system is an important milestone but not the end of our reliability journey.

We're actively working on:

- Increasing the resilience of our stateful systems
- Exploring multi-cloud redundancy to remove single-provider dependencies
- Further automating recovery playbooks to reduce operational response times even more

Last week's event validated our regional failover strategy, showing early positive ROI as we continue expanding our
resilience capabilities.

---

# How OAuth Works
URL: https://clerk.com/blog/how-oauth-works.md
Date: 2025-06-13
Category: Engineering
Description: A practical guide to OAuth Scoped Access that walks through the Authorization Code Flow with real code examples, security best practices, and clear explanations of how third-party app integrations actually work.

OAuth is confusing. There's no getting around it - the specification is complex, the terminology is overloaded, and the security considerations are numerous. But it doesn't have to be intimidating once you understand the core concepts.

This guide focuses on practical understanding over abstract theory. While Clerk handles most OAuth complexity for you with a built-in authorization server, understanding the fundamentals will make you a better developer and help you debug issues when they arise.

Here's what we'll cover:

- **OAuth Scoped Access** - letting third-party apps access user data without sharing passwords
- **The Authorization Code Flow** - the step-by-step process that makes it work
- **Real implementation details** - actual code you can run, not just theory
- **Security essentials** - PKCE, state parameters, and other protections

There are plenty of OAuth articles out there, but many focus on abstract concepts without showing you how the pieces actually fit together. This guide takes a different approach - we'll walk through a complete implementation so you can see exactly how OAuth works in practice.

## What is OAuth?

[OAuth](https://oauth.net/2/) stands for "Open Authorization", and it is a set of standard specifications designed by the [IETF](https://www.ietf.org/) that address how a user can grant a third party application access to some of their data/resources, without providing their login directly.

As an example, imagine that you are a brand manager at a trendy coffee shop, and you have been tasked with writing marketing content and scheduling it to go out at certain times throughout the week. There are many "content planning" apps that will allow you to do this across several social platforms -- LinkedIn, Twitter/X, Facebook, etc -- let's imagine we're using a fictional one called "Content Planner". Content Planner needs to make posts on your behalf on social platforms.

Content Planner *could* just ask you for your email and password for each social platform, but this is less than ideal for several reasons:

- **Restricting access:** You probably do not want to give Content Planner full access to your account -- only the ability to make posts, but restrict access to send DMs, change your password, or delete your account. If Content Planner has a security incident, it would be best to minimize the damage. [Principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), right?
- **Avoiding bot protection issues:** If Content Planner did have your credentials, it would need to sign in as you. Many apps and authentication providers will automatically detect and block any sort of scripted login attempts as a bot, to prevent fraud and bot attacks (shameless plug: Clerk [will do this for your app automatically](/docs/security/bot-protection)). So, it would be tough for Content Planner to actually sign in as you even if you did provide your email/password.
- **Avoiding MFA issues:** It's becoming increasingly common as a security measure to require more than just an email/password. Sending an [OTP](/docs/custom-flows/email-sms-otp) to your phone or email, getting an [authentication code from an app](/docs/custom-flows/email-password-mfa), using a [passkey](/docs/custom-flows/passkeys), etc. are all common methods of increasing the security of your users' accounts (shameless plug once again, each of these are a simple toggle with Clerk). In these cases, an email and password alone wouldn't be sufficient anyway.

What we need here is a legitimate avenue through which you, as a user of Content Planner, can tell Twitter (going to refer to it as Twitter for this post, I know it's "X" now though), LinkedIn, Facebook, etc that you would like to grant access to Content Planner to make posts on your behalf. This type of interaction is exactly what OAuth was designed to handle.

## Common OAuth Terminology

Let's start with some common terminology. This will help to communicate concepts clearly through the rest of the piece.

- **Client**: This is a very generic term that is common in software discussions. When discussing OAuth, however, it has a very specific meaning that is important to really internalize: *A Client is an entity that wants to get something from another entity*.
  Referencing our above example, Content Planner is the Client, since it wants to get permission to make posts from Twitter, Facebook, LinkedIn, etc.
- **Authorization Service**: This refers to the service that is responsible for signing the user in and out and delegating access to the user's account to third parties. For example, users signing into Content Planner with their Twitter account would use Twitter's authorization service. This may or may not be a *server*, though you will often see it referenced as a "authorization server", including in OAuth specs, but we're going to call it a service in this piece, because it's more accurate, and less confusing. This is sometimes also referred to as an "Identity Provider" or "IdP".
- **Resource Service**: This refers to the service that has the resources the client wants access to. Referencing our above example again for Twitter, this would be the Twitter app itself, which is what has the ability to post things. As mentioned above, this may or may not run on a separate server from the authorization service, and you will likely see it referred to as a "resource server" in other writing about OAuth, but we're sticking with "service" in this piece for accuracy. This is sometimes also referred to as an "Service Provider" or "SP".
- **OAuth Access Token**: A string of random characters (which is generally what a "token" is in the realm of web development) or [JWT](https://datatracker.ietf.org/doc/html/rfc7519) that is given to the *Client* by the *Authorization Service* if the OAuth process is completed successfully. The Client can then send this Token to the *Resource Service* as a form of proof that it should be allowed to access what it's trying to access.

With all of this covered, we should be able to piece together a very basic overview of how OAuth works:

![oauth.png](./oauth-flow-full.jpg)

## Other OAuth use cases

We're going to get deeper into the details of how all of this works in a minute, but be aware that OAuth is broader than this. The diagram above is just one way of using OAuth, and there are several others. OAuth as a protocol can be used for creating users and signing in with a regular username and password, for signing in with one of those number codes that you sometimes do with TV apps, for single sign on (where you "Sign in with Google"), for authorizing requests between two servers that don't even involve a human, and much more. In fact, there are *30+ RFCs* describing modifications and extensions to OAuth.

This makes the term "OAuth" fairly confusing in general. When someone refers to using OAuth, are they talking about signing in to their app using a third-party app? Are they talking about building authentication for their app? Or are they talking about granting access to resources on a user's behalf as described above? This confusion makes it really hard to research and learn about OAuth. In order to further clarify this, we are introducing more specific terms for each of these three flows, that we are hoping will be adopted more widely for the benefit of all developers on the OAuth learning journey:

1. **OAuth Scoped Access** - scoped 3rd party data access via OAuth (the one we described above)
2. **OAuth SSO** - sign on to an app through a third-party app, using OAuth (like Sign in with Google, etc)
3. **OAuth User Management** - OAuth as a user registration and sign in/out mechanism (a full authorization service built with OAuth)

In this post, we're going to discuss *OAuth Scoped Access*, the same flow we have been describing since the start.

## The OAuth authorization code flow in action

Now that we have a broad idea of the scope and goals of OAuth, let's get into the specifics for what is likely the most common OAuth flow, called the ["Authorization Code Flow"](https://oauth.net/2/grant-types/authorization-code/), which allows for OAuth scoped access. You are now stepping into the shoes of the developer of the Content Planner app, tasked with *implementing* an OAuth flow with Twitter. We will walk through how to write a Client that interacts with an Authorization Service. We'll use the same example we have been using above, with Content Planner as the Client and Twitter as the Authorization/Resource Service.

> \[!NOTE]
> *All references to Twitter and its API are fictional, just for example purposes.*

1. You make a request to Twitter's API to make a post on your user's behalf. This will fail, since you do not have permission to do so. However, Twitter will return a `www-authenticate` header alongside the 401, which has the value `Bearer resource_metadata=http://api.twitter.com/.well-known/oauth-protected-resource` (as defined by [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)).

2. You can visit this URL and get back information about *where to go* to get an OAuth flow going with Twitter. Here's what you might see as the response:

   ```json
   {
     "resource": "https://api.twitter.com",
     // ⚠️ This line below is the important part!
     "authorization_servers": ["https://auth.twitter.com"],
     "token_types_supported": ["urn:ietf:params:oauth:token-type:access_token"],
     "token_introspection_endpoint": "https://auth.twitter.com/oauth/token",
     "token_introspection_endpoint_auth_methods_supported": ["client_secret_basic", "none"],
     "service_documentation": "https://docs.twitter.com/oauth",
     "authorization_data_types_supported": ["oauth_scope"],
     "authorization_data_locations_supported": ["header"],
     "key_challenges_supported": [
       {
         "challenge_type": "urn:ietf:params:oauth:pkce:code_challenge",
         "challenge_algs": ["S256"]
       }
     ]
   }
   ```

3. You can now select an authorization server (though `authorization_servers` is an array, realistically you are nearly always just going to get just one back), and make a request to that authorization server for its metadata, which outlines the OAuth endpoints. This should be found at our authorization server URL we got from the metadata file above, `https://auth.twitter.com`, then we tack on the established path, `/.well-known/oauth-authorization-server` according to [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414). Making a request to this path should return something along these lines:

   ```json
   {
     "issuer": "https://twitter.com",
     // ⚠️ This line below is the important part!
     "authorization_endpoint": "https://auth.twitter.com/oauth/authorize",
     "token_endpoint": "https://auth.twitter.com/oauth/token",
     "token_endpoint_auth_methods_supported": ["client_secret_basic", "none"],
     "token_endpoint_auth_signing_alg_values_supported": ["RS256", "ES256"],
     "userinfo_endpoint": "https://auth.twitter.com/oauth/userinfo",
     "jwks_uri": "https://auth.twitter.com/.well-known/jwks.json",
     "registration_endpoint": "https://auth.twitter.com/oauth/register",
     "scopes_supported": ["openid", "profile", "email"],
     "response_types_supported": ["code"],
     "service_documentation": "https://docs.twitter.com/oauth",
     "ui_locales_supported": ["en-US"]
   }
   ```

   > \[!NOTE]
   > It should be noted that the steps above are not required, nor does every service that has implemented OAuth support them. Normally, all you need to get started with OAuth is the `authorization_endpoint` seen above, so if you know that endpoint already the steps above can be skipped. They are still recommended though - in the case the service makes changes to their endpoints in any way, following the above steps would allow your app to automatically "heal" through this.

4. Now that we have the authorization endpoint, we can kick off the "Authorization Code Flow" in earnest. The way an OAuth flow begins is normally that your user clicks a button or takes some action within your app. In this case, we'll imagine there's a "Connect with Twitter" button in the interface for Content Planner, and the user clicks that button.

   ![connect-twitter.png](./authorization-flow-kickoff.png)

   As the app developer, we can decide what that link looks like, and we'd like for it to point to the *authorization endpoint*, but also provide some extra details according to the OAuth spec. Let's write that code (in React, just for the example):

   ```jsx
   function OAuthConnection() {
     // we just got this from the authorization server metadata
     const authorizeEndpoint = 'https://auth.twitter.com/oauth/authorize'

     // we'll talk about these these below, promise
     const params = {
       response_type: 'code',
       client_id: 'abc123',
       redirect_uri: `https://contentplanner.app/oauth_callback`,
       scope: 'email profile tweet:write tweet_stats:read',
       state: 'random-value',
     }

     // this just takes the params and tacks them on to the authorize endpoint
     // as a querystring
     const authorizeUrl = `${authorizeEndpoint}?${new URLSearchParams(params).toString()}`

     return (
       <div>
         <p>Connect Content Planner to your X account</p>
         <a href={authorizeUrl}>Connect!</a>
       </div>
     )
   }
   ```

   First, let's talk about the "params" specified above, to make sure we understand what's actually going on there:

   - `response_type` - we [mentioned earlier](#other-o-auth-use-cases) that are several different types of OAuth flows, which makes OAuth a rather confusing thing in general. Now we are starting to pay this debt - since there can be several different flows available from the same authorize endpoint, we need to supply this parameter to specify which type of flow we want to run. In this case, if you recall, we're after the *"Authorization Code Flow"*, and the param *"code"* is what tells OAuth that this is what we want.

   - `client_id` - OAuth specifies that *Clients* must *register* with the *Authorization Service* in order for the flow to work. This is so that when the user goes through the flow in which they grant access, the Authorization Service is able to make it clear *whom* the user is granting access to. You may have done something like this before. Normally, this involves making an account with the Resource Service, going into some sort of "developer settings" and creating an "OAuth Application" or "OAuth Client", where you put in a name, a redirect url, sometimes an avatar, etc. When you have created an OAuth client, the UI will provide you with a **Client ID** and **Client Secret**. We'd take that Client ID and use it for this parameter.

   For our example, this would mean logging into Twitter, going into their developer settings, creating an OAuth Client through their UI, and pasting the Client ID in here (or more commonly, pulling it from an environment variable). It's worth noting that OAuth also includes an optional extension called "dynamic client registration" that authorization services can implement, in which there is an API endpoint through which an OAuth Client can be created, rather than through an application's UI, but this isn't super common at the moment. [We'll talk about that more later](#dynamic-client-registration).

   - `redirect_uri` - When our user clicks on the "connect" link, it will send them out to the Authorization Service for Twitter -- after they are done, Twitter needs to send them back to Content Planner. This is the URL that they are sent back to.

   ![redirect-uri.png](./redirect-uri.png)

   - `scope` - *Authorization Services* may define "scopes" which determine what users are able to grant access to. As a *client*, you can then request access to the scopes you need via this param when hitting the authorize endpoint. Here, we're asking for some basic info about the user, as well as the ability to write tweets and read the tweet stats, so we can see how well our scheduled tweets performed.

   - `state` - This is for security, and ensures that the initial authorization request matches up with the response that was sent back. The base purpose of this param is for the *Client* to generate it, then for the *authorization service* to send it back as a query string when it goes to the `redirect_uri`. The *client* then checks it against the initial value to make sure it matches. Leaving this out can expose users to [CSRF](https://owasp.org/www-community/attacks/csrf) attack shenanigans where an attacker can begin an OAuth flow with their own account, then get someone else to click a link that will complete it with *the victim's* account, letting the attacker take over the victim's account. The state param can also be used to pass some data safely through the OAuth redirect flow, since we know we will get it back on the other side.

   Clicking the `authorizeUrl` link will bring users to a "consent screen", which might look something like this:

   ![OAuth consent screen](./consent-screen.png)

   This screen clearly lays out what permissions you are granting as a user and whom you are granting them to. It utilizes the `scopes` that we passed in the query to clarify for the user what they are allowing, and to whom. If the user accepts, they will be sent back to the `redirect_uri` specified in the params, with `state` and `code` as query parameters. The redirect might look something like this:

   ```
   https://contentplanner.app/oauth_callback?code=h89sf89d8hfsd&state=random-value
   ```

5. So now we need to handle the `oauth_callback` route. The next step in the OAuth flow is to take the "code" here, which is referred to as an "Authorization Code" and exchange this for an actual OAuth Token. Let's write that code (express endpoint as an example):

   ```tsx
   app.get('/oauth_callback', async (req, res) => {
     const qs = new URLSearchParams(req.query)
     const code = qs.get('code')
     const state = qs.get('state')
     const error = qs.get('error')

     // Handle user denial or other errors
     if (error) {
       return res.status(400).send(`OAuth error: ${error}`)
     }

     // we'd want to store the state we sent to the authorize endpoint
     // somewhere so we can compare the two here
     if (state !== originalStateValue) {
       return res.status(400).send('State param mismatch')
     }

     // this is the token endpoint we got from the metadata in step 3
     const response = await fetch(tokenEndpoint, {
       method: 'POST',
       headers: {
         'Content-Type': 'application/x-www-form-urlencoded',
       },
       body: new URLSearchParams({
         client_id: process.env.CLIENT_ID,
         client_secret: process.env.CLIENT_SECRET,
         code,
         grant_type: 'authorization_code',
         redirect_uri: `https://contentplanner.app/oauth_callback`,
       }).toString(),
     }).then((res) => res.json())

     // normally we'd store these somewhere secure for use with
     // making requests to the resource server!
     const accessToken = res.access_token
     const refreshToken = res.refresh_token

     res.json({ success: 'true' })
   })
   ```

   > \[!NOTE]
   > You may be asking - why does this happen in two steps? Why return an "authorization code" and not just send back the OAuth token to save time and resources? We skimmed over this detail in the initial diagram for clarity, but this extra "authorization code for access token exchange" is a required part of the authorization code flow.
   >
   > The answer here is security. For me, understanding 'security' things is easiest by hearing an explanation of how an attacker would be able to exploit this if it were done a different way. So in that spirit, let's imagine that, in the name of efficiency, we decide that we're going to return the OAuth token straight into the redirect URL, and skip the authorization code.
   >
   > In this case, the user is in the browser when they hit the "accept" button on the consent screen, and the authorization service then issues a redirect back to the `redirect_uri`, including the token. The OAuth token now appears fully in the user's browser history, it's visible at least briefly in the URL bar, it's accessible to browser extensions, it shows up in CDN logs, ISP logs, server logs, etc -- these are all problematic as they could leak and make the sensitive OAuth token available to attackers. Additionally, because the Client ID is not a sensitive or secret value and is already exposed in the browser, an attacker with access to it could obtain an OAuth token for your service, bypassing client verification via the client secret.
   >
   > It is generally for this reason that we need both a Client ID and Client Secret - the Client ID can be public without issue, but the Client Secret cannot, which is why we never send it through the browser, and only utilize it when making a direct request from our Client to the Authorization Service, as we can see happening in the code example above.
   >
   > Ensuring that the Token exchange happens as a *server to server* connection makes it more secure, since by avoiding the browser, the sensitive Access Token has fewer places that it appears where it could be extracted by an attacker.

6. Now that we have the OAuth Access Token, we can use it to make a request to the Resource Service. In this example, at the scheduled time, Content Planner could hit Twitter's API in order to send the tweet. As long as we include a valid Access Token with the right scopes, it should work just fine. It might look something like this:

   ```tsx
   await fetch('https://api.twitter.com/tweet', {
     method: 'POST',
     headers: {
       Authorization: 'Bearer <my_access_token>',
     },
     body: JSON.stringify({ text: 'Developers developers developers...' }),
   })
   ```

   Because we have included the Access Token with the request, Twitter will allow Content Planner to post this tweet on our behalf, even though it doesn't have our account sign-in details. Hooray!

Sometimes, it can help to actually write this code to fully lock it in to your brain. If that's the case, I'd encourage you to give it a shot! [Here's the implementation that I wrote](https://github.com/clerk/test-oauth-client) to do this, in case it's helpful at all. It didn't take too long and is very little code.

## Common OAuth questions

Understanding the Authorization Code Flow is a huge step forward in understanding OAuth and how it works overall. But there are also a bunch of other details that I was curious about in my own journey to learn about OAuth, which you might be too, so let's address them.

### What happens if you already finished the flow, but now want to request more scopes?

There's no concept of modifying an existing OAuth Access Token. If you want a token with more scopes, you'd go through the flow again from the start, but request more scopes as parameters to the authorization endpoint. You could then replace the existing Access Token with the new one with more scopes.

### Does the Access Token expire? What happens if/when it does?

Yes, normally, they do, as do most types of access tokens. When you get back an Access Token at the end of the flow, the Authorization Service is expected to also return a *Refresh Token.* It has a much longer expiration than the Access Token and can be used to request a new Access Token. Generally, this works as such:

1. You make a request with an Access Token that has just expired, and get back a 401 response from the Resource Service.
2. You assume this is because the Access Token has expired, and make a request to the "Token Endpoint" (you may have noticed this in the authorization server metadata above), with a valid Refresh Token, and it will return a new Access Token and Refresh Token pair.
3. Now you replay the original request with your new Access Token

If the Refresh Token expires due to the user not using the service for a long time, they will need to re-establish the connection by going through the Authorization Code Flow again.

Here's an example of how a token refresh call might look:

```ts
const refreshResponse = await fetch(tokenEndpoint, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: currentRefreshToken,
    client_id: process.env.CLIENT_ID,
    client_secret: process.env.CLIENT_SECRET, // if confidential client
  }).toString(),
})

// Both tokens are replaced for security
const { access_token, refresh_token } = await refreshResponse.json()
```

### Can tokens be opaque tokens or JWTs? What's the difference?

Opaque tokens are random strings that need to be verified on each request with the authorization server. Because they "phone home" to the authorization server to verify on each request, opaque tokens can be instantly revoked, but are also slower since they add an extra step to each request that includes one. JWTs are digitally signed by the issuer, and can be verified without ever contacting the authorization server by [verifying the signature](/docs/backend-requests/resources/tokens-and-signatures#digital-signatures). However, because of this, they cannot be revoked, and in order to stay secure normally have shorter expiration times. Either token type *can* be used as an OAuth Access Token; which one is preferred is up to the developer. Ory provides a very good overview of the tradeoffs [in their article here](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token).

### What if you want to revoke access?

It's expected that an authorization server has a revocation endpoint, which, if hit with a valid OAuth Access Token, will revoke that token's access and make it useless. This, in combination with clearing out any existing stored Access/Refresh Tokens should be enough to remove an OAuth grant and require a fresh new connection if needed.

## Public clients and PKCE

So far, we've been assuming that Content Planner is running on a server where we can safely store the `client_secret` and use it during the token exchange step. This type of OAuth client is called a **confidential client** because it can keep secrets... well, secret.

But what if you want to build a mobile app or a [single-page web application](https://en.wikipedia.org/wiki/Single-page_application) (SPA) that runs entirely in the browser? In these cases, there's no secure server-side environment to store the `client_secret`. Any secret you embed in a mobile app can be extracted by someone with the right tools, and anything in a browser-based app is visible to browser extensions and anyone who opens the developer tools. These are called **public clients** because they cannot securely store secrets.

This creates a problem with our authorization code flow. Remember in step 5 above, we made a server-to-server request that included the `client_secret` to exchange the authorization code for an Access Token? If we can't safely store a `client_secret`, how do we prove to the authorization service that we're the legitimate client and not an attacker who intercepted someone else's authorization code?

The answer is **PKCE** (Proof Key for Code Exchange, pronounced "pixie"), defined in [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636). PKCE replaces the `client_secret` with a dynamically generated proof that only the Client who started the OAuth flow can provide.

Here's how it works: instead of relying on a pre-shared secret, the Client generates a random value called a **code verifier** at the start of each OAuth flow, along with a **code challenge** (which is a hashed version of the code verifier). The Client sends the code challenge when starting the authorization flow, then proves it started the flow by providing the original code verifier during the token exchange.

Let's see how this changes our code example. First, we need to generate the PKCE values when creating our authorization URL:

```tsx
// Helper function to generate a random code verifier
function generateCodeVerifier() {
  const array = new Uint8Array(32)
  crypto.getRandomValues(array)
  return btoa(String.fromCharCode(...array))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '')
}

// Helper function to create code challenge from verifier
async function generateCodeChallenge(verifier: string) {
  const encoder = new TextEncoder()
  const data = encoder.encode(verifier)
  const digest = await crypto.subtle.digest('SHA-256', data)
  return btoa(String.fromCharCode(...new Uint8Array(digest)))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '')
}

async function OAuthConnection() {
  const authorizeEndpoint = 'https://auth.twitter.com/oauth/authorize'

  // Generate PKCE values using Web Crypto API
  const codeVerifier = generateCodeVerifier()
  const codeChallenge = await generateCodeChallenge(codeVerifier)

  // Store code verifier in browser storage
  sessionStorage.setItem('pkce_code_verifier', codeVerifier)

  const params = {
    response_type: 'code',
    client_id: 'abc123', // we still need this, but no secret required
    redirect_uri: `https://contentplanner.app/oauth_callback`,
    scope: 'email profile tweet:write tweet_stats:read',
    state: 'random-value',
    // PKCE parameters
    code_challenge: codeChallenge,
    code_challenge_method: 'S256', // indicates we used SHA256 hashing
  }

  const authorizeUrl = `${authorizeEndpoint}?${new URLSearchParams(params).toString()}`

  return (
    <div>
      <p>Connect Content Planner to your X account</p>
      <a href={authorizeUrl}>Connect!</a>
    </div>
  )
}
```

Then, in our callback handler, instead of sending a `client_secret`, we send the `code_verifier`:

```ts
// This would run when the user is redirected back to your SPA
async function handleOAuthCallback() {
  const urlParams = new URLSearchParams(window.location.search)
  const code = urlParams.get('code')
  const state = urlParams.get('state')
  const error = urlParams.get('error')

  if (error) {
    console.error(`OAuth error: ${error}`)
    return
  }

  if (state !== originalStateValue) {
    console.error('State param mismatch')
    return
  }

  // Retrieve the code verifier from browser storage
  const codeVerifier = sessionStorage.getItem('pkce_code_verifier')

  if (!codeVerifier) {
    console.error('No code verifier found')
    return
  }

  const tokenEndpoint = 'https://auth.twitter.com/oauth/token'

  const response = await fetch(tokenEndpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      client_id: 'abc123',
      code,
      grant_type: 'authorization_code',
      redirect_uri: `https://contentplanner.app/oauth_callback`,
      code_verifier: codeVerifier, // PKCE proof instead of secret
    }).toString(),
  }).then((res) => res.json())

  const accessToken = response.access_token
  const refreshToken = response.refresh_token

  // Clean up the stored code verifier
  sessionStorage.removeItem('pkce_code_verifier')

  // Store tokens securely in your SPA (consider using secure storage)
  sessionStorage.setItem('access_token', accessToken)
  sessionStorage.setItem('refresh_token', refreshToken)

  console.log('OAuth flow completed successfully!')
}
```

The security here works because an attacker who intercepts the authorization code still can't use it — they would need the original `code_verifier` to complete the token exchange, and only the client that started the flow has that value.

> \[!NOTE]
> You might wonder: if the `code_verifier` is stored in the browser (like in sessionStorage), couldn't an attacker just steal that instead?
>
> The key difference is *timing* and *access*. The `code_verifier` only exists for the brief duration of the OAuth flow and is likely only accessible to the specific application that created it. A `client_secret`, on the other hand, would be permanently embedded in the app code where it could be extracted by anyone. Additionally, each PKCE flow uses a unique `code_verifier`, so even if one was somehow compromised, it couldn't be reused for other users or future flows.
>
> While these restrictions make it more difficult for an attacker to compromise the OAuth flow, it's not impossible. For this reason, public clients are inherently less secure than confidential clients, and this should be acknowledged when making decisions about which type of client to use.

PKCE is becoming mandatory for many OAuth providers, even for confidential clients, because it provides an additional layer of security. It's considered a best practice to use PKCE for all OAuth flows when possible, regardless of whether your client can store secrets or not. In fact, the most up to date OAuth spec, [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13), requires that PKCE be used for all authorization code flows.

## Dynamic Client Registration

We briefly mentioned above the concept that it was possible to register an OAuth Client with your Authorization Service via API, rather than manually through their UI. This is not required, and many Authorization Services do not implement this capability, but it is possible, and is referred to as *dynamic client registration*, as defined by [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591).

If an Authorization Service implements Dynamic Client Registration, this means that anyone can register an OAuth client through a public API endpoint. There are a variety of scenarios where this might be needed, largely centered around when many different OAuth clients need to be created in a self-serve manner.

However, it does come with some substantial security risks:

- Dynamic client registration creates an unauthenticated, public endpoint that is accessible to anyone on the internet. This means there is no way to trace or verify the identity of who creates OAuth clients, allowing attackers to create Clients anonymously without leaving any audit trail. This lack of accountability makes it extremely difficult to track malicious activity back to its source.
- Attackers can exploit this open access to create thousands of OAuth Clients over time. Even with rate limiting measures in place, determined attackers can slowly build up large numbers of Clients by spreading their registration attempts across extended periods. This proliferation makes legitimate Clients increasingly difficult to identify among potentially fraudulent ones, especially since Client names and metadata can be set to anything, making proper identification significantly harder for administrators.
- The ability to freely register Clients opens the door to sophisticated social engineering attacks. Attackers can create Clients with legitimate-sounding names and descriptions, potentially tricking users into authorizing malicious applications that appear trustworthy. These deceptive Clients can request broad scopes while maintaining their facade of legitimacy, making it extremely difficult for both users and administrators to distinguish legitimate Clients from malicious imposters attempting to harvest user data or gain unauthorized access.
- Implementing dynamic client registration increases the administrative burden required for monitoring and cleanup activities. This additional complexity makes security audits and compliance reviews more challenging, as administrators must sift through potentially thousands of dynamically created clients. The system requires ongoing vigilance to identify and remove malicious Clients, creating a operational overhead that many organizations may not be prepared to handle effectively.

That being said, as long as you have evaluated and accepted the risks, dynamic client registration can be an essential feature in some scenarios.

## OAuth & OIDC

There's another standard that is built on top of and often used with OAuth called ["OpenID Connect"](https://openid.net/developers/how-connect-works/) (OIDC). This standard is written and maintained by a different standards committee, the [OpenID Foundation](https://openid.net/foundation/).

The key addition to OAuth is that when you return the access and refresh tokens, so long as an `openid` scope is included, you should also return another property called `id_token`, which is a JWT that has some basic user information like name, email, profile photo, etc. This helps the *Client* to know who the user is that just went through the flow. Without OIDC, if the *Client* needed to get any details about the user, it would need to use the Access Token to make another request to some API endpoint that returns user info (OIDC also standardizes a ["user info" endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) for this purpose). With OIDC, this step can be skipped since the user info is returned as part of the initial OAuth response.

There's more in the OIDC specification that expands out more details on session management if you're using OAuth for user login, but we won't address those in this piece, since it's focused on OAuth scoped access.

## Using OAuth with Clerk

Now that you understand how OAuth scoped access works under the hood, you might be ready to make this happen for one of your applications. If you're using Clerk, we have a robust OAuth authorization server built in for you automatically, that's ready to use.

You can navigate to ["Config > OAuth applications" in the Clerk dashboard](https://dashboard.clerk.com/last-active?path=oauth-applications) to register an OAuth application. This gives you back the `client_id` and `client_secret` we discussed, along with all the proper endpoint configurations. For scenarios where you need the dynamic client registration we discussed earlier (like multi-tenant SaaS platforms or developer marketplaces), Clerk also supports this - though it's optional and needs to be explicitly enabled due to the security considerations we covered.

And remember all those security considerations we covered - CSRF protection via the state parameter, PKCE for public clients, secure token storage? Clerk supports all of these automatically, with the most security-conscious implementation out of the box.

If you are looking to build out an OAuth feature for your Clerk application, check out [our step-by-step guide on how to do so](/docs/oauth/oauth-scoped-access). We also have [a minimal demo OAuth client](https://github.com/clerk/test-oauth-client) that can be used to quickly test against Clerk's built-in authorization server.

---

# Clerk launches EASIE SSO and eliminates SSO fees
URL: https://clerk.com/blog/clerk-launches-easio-sso-and-drops-all-sso-fees.md
Date: 2024-11-20
Category: Engineering
Description: EASIE SSO brings Clerk’s signature simplicity to a notoriously agonizing corner of authentication.

Since we started Clerk, it’s been clear that enterprise SSO is a tremendous disappointment to users, developers, and IT admins alike. Despite being crucial to enterprise security, and despite the high fees usually associated with the feature, there has been a severe underinvestment in the experience for all stakeholders.

That’s why we’re absolutely delighted to share **EASIE SSO** with the world today. It’s enterprise SSO with a healthy dose of Clerk’s signature simplicity, baked right into our dashboard and `<SignIn/>` component. We're leveraging Google and Microsoft OIDC for their ease of setup, then adding a thin layer of enterprise SSO capabilities on top, including:

- Enrollment based on a user's email domain
- Mandatory sign-in through the enterprise's choice of Google or Microsoft OIDC
- Just-in-time provisioning during sign-in
- Automatic deprovisioning and session revocation

Plus, **we’re eliminating usage-based SSO connection fees** (previously $50/mo each) to make enterprise SSO more accessible than ever, including SAML SSO connections!

EASIE SSO can be enabled through the **SSO Connections** page of your dashboard: just enter the enterprise's email domain and choose if they should sign in with Google or Microsoft… Clerk will handle the rest!

## Why EASIE SSO?

SSO is vital for enterprise security, but existing solutions are complex and frustrating for all parties. We've met countless developers who try to mimic enterprise SSO behavior by forcing authentication through Google or Microsoft and then verifying the email domain. This is easier than “real SSO,” but often carries security oversights:

- Verifying the email domain alone doesn't confirm that the user signed in through the correct tenant, and can be exploited through a [tenant](https://trufflesecurity.com/blog/google-oauth-is-broken-sort-of) [swap](https://www.youtube.com/watch?v=ceeA3FmKxtM) attack
- When a user is deactivated through the Google or Microsoft admin console, their sessions are not revoked, so terminated teammates can continue accessing tools and services as long as they don’t sign out

**EASIE SSO** is a thin layer on top of OIDC that solves these security challenges, while retaining the simple setup of verifying users based on email domain. There is no need to manage per-enterprise credentials like SAML metadata files or one-off OIDC secrets.

The technical details and future plans for EASIE SSO are maintained on [easie.dev](https://easie.dev) as an open specification, and we encourage others to implement the strategy even if they are not using Clerk.

## The future of enterprise SSO

It’s clear that enterprise SSO has been sitting at a crossroads. While there is unanimous agreement of its benefits, the challenges associated have limited its adoption to only the most security-conscious and cost-insensitive enterprise software buyers.

Clerk believes in a future where the benefits of enterprise SSO are realized far more frequently, among both the largest and smallest businesses. We’re confident this future can be achieved, and we expect EASIE SSO and the elimination of SSO connection fees to be a leap forward in the right direction.

If you are also interested in creating this future, we would love to hear from you! To connect with the EASIE team, please email [easie@clerk.dev](mailto:easie@clerk.dev).

---

# Combining the benefits of session tokens and JWTs
URL: https://clerk.com/blog/combining-the-benefits-of-session-tokens-and-jwts.md
Date: 2024-09-11
Category: Engineering
Description: Learn about how leveraging the benefits of both session token authentication and JWTs results in the best of both approaches.

Neither session tokens nor JWTs alone are sufficient as an optimal authentication strategy.

Session token authentication has been in use for decades and is still widely implemented in modern applications, but can become a bottleneck when scaling an application. JWT authentication enables fast request validation, making it suitable for scaling distributed applications, yet struggles with session invalidation. While both approaches have their tradeoffs, the two methods can be combined to amplify their benefits and cancel out their drawbacks.

Let's explore these two authentication strategies, and discuss how rethinking the approach to JWTs can result in the best of both worlds.

## Session token authentication

Session token authentication functions by tracking the state of a signed-in user on the server using the concept of a “session”.

When a user signs into a system, the server will check their provided credentials against a database to validate the information. If correct, the server will create a session with a unique identifier along with information about the session such as the user's ID, the creation time, expiration time, session status, etc. These details will be tracked in persistent storage, such as a database, and the session identifier will be sent back to the client to be used with future requests.

The client will often store these session identifiers in browser cookies, which are small pieces of information that are automatically sent with every request that matches the domain for which they were created. When a web server receives a request that contains a cookie with a session identifier, the server will look up the session among the list of tracked sessions, ensure its validity, and tie the request to the user associated with the session.

This allows the server to identify *who* is making the request.

Because sessions are stored on the server, they can be easily invalidated as needed, resulting in subsequent requests being denied.

While traditionally not an issue with most small to moderately-sized applications, the latency associated with checking sessions on each request can become a problem as the application grows. Geographically distributed services are especially impacted based on the additional latency between parts of the system, more so than when utilizing a single database.

## JSON Web Token (JWT) authentication

JWT authentication is an alternative to session tokens that largely solves the latency issue by embedding session details directly into the object sent back to the client, instead of only an identifier.

### What are JWTs?

A JWT itself is an encoded version of a JavaScript object with a cryptographic signature attached to it to prevent tampering.

JWTs are composed of three parts: the header, the payload, and the signature. The **header** contains general information about the token type and the algorithm used for the signature. The **payload** is simply a JavaScript object that contains all of the information you need to transmit. The **signature** is the result of combining the header, payload, and a secret (typically the private key of an [asymmetrical key pair](https://www.cloudflare.com/learning/ssl/what-is-asymmetric-encryption/)) being [hashed](https://www.geeksforgeeks.org/what-is-hashing/) using the algorithm specified in the header.

Each of these three sections is base64 encoded and joined with a period (.) to obtain the final result.

![Combining The Benefits Of Session Tokens And Jwts guide illustration](./jwt.png)

Any modification to the JWT will make the signature invalid, allowing recipients of a valid JWT to trust the authenticity of the data it contains.

### How are JWTs used in authentication?

When a user signs into a system using JWT authentication, the server still needs to validate the credentials as with session token authentication. Instead of a session being created and tracked on the server, much of the information typically associated with a session (user ID, creation time, expiration time, etc.) is added to the payload of a JWT and sent back to the client.

When a request is received by the server, it will verify the signature of the JWT instead of querying a database for session information. If the signature is valid, the server can trust the details embedded into the JWT such as the user ID and expiration date.

JWT authentication solves the latency issues in distributed applications by removing the overhead associated with querying a database on each request, however, the system that creates the JWT has little control over it once it leaves the server. If a JWT is obtained by an unauthorized party after it's been created, that JWT can be used to impersonate the party for whom it was created with no way to invalidate it since the server trusts the combination of the payload and signature.

This is especially problematic considering most implementations of JWT authentication set the expiration value of JWTs as they would sessions. This results in JWTs created with long lifetimes, sometimes days or even weeks.

Ultimately, JWT authentication provides faster request validation with the tradeoff of being able to easily revoke JWTs. This shifts part of the trust associated with the authentication system away from the server since you need to trust that the client will not allow the JWT to leak.

## Using a hybrid approach for optimal results

So the big question now is how to get the speed of JWT authentication while still enabling the revocation ability of session token authentication.

The first step is to create JWTs with extremely short lifetimes that will be used to authenticate requests. While leaked JWTs can still be used for impersonation, the window of opportunity to utilize the JWT is so short that it is extremely unlikely that any meaningful action can be taken by attackers.

This change means that JWTs used for authentication will need to be created more frequently to keep the user signed in. At first glance, this may seem like a downside, but it creates an opportunity for the system to decide whether or not it *should* create a new JWT for that user.

To make that decision, the system creates sessions upon user sign-in that will be managed and tracked on the server. This part of the hybrid model follows the same approach as with session token authentication, but instead of using sessions to validate each request, the system will only use them to mint new JWTs prior to their expiration.

The state of the session (valid, expired, revoked, etc.) would be used to determine if the new JWT should be created or not. If a session is no longer valid, the request to renew the JWT will be denied, which allows the server to retain control over the user's ability to access the service.

### Implementing the hybrid model

Using this model, the client would receive a session identifier (as defined with session token authentication) as well as a JWT upon sign-in.

As requests are made to the server, the JWT is used to authenticate the request and grant the user access to that resource.

Before the JWT's short expiration window elapses, the session identifier is used to renew the authentication JWT before it expires to maintain access to the system. This renewal process would occur as a background task, outside of the typical interaction between the application and the server.

Clerk SDKs perform this operation in the browser by setting an interval that executes every minute to request a new JWT. This operation is performed in the background without any user interaction.

If a session is expired or invalidated on the server, the authentication system will no longer create a JWT, effectively signing the user out of the system.

By decoupling the session lifetime from the JWT used to authenticate requests and instead tracking it elsewhere, you can have a hybrid approach to authentication that combines the best of both JWT authentication and session token authentication.

## Conclusion

Both JWT and session token authentication have their tradeoffs. While it is common to debate between using one over the other, this assumes that the benefits of each are exclusive to their respective method.

By instead combining the perks of each approach, we can provide fast responses from JWTs while enabling developers and users to invalidate sessions.

---

# What are passkeys and how do they work?
URL: https://clerk.com/blog/what-are-passkeys.md
Date: 2024-04-22
Category: Engineering
Description: Passkeys allow for a simple, yet extremely secure, sign-in experience. Learn more about what they are and how they work in this article.

Passkeys are a big step towards a passwordless future.

Allowing users the ability to sign in with their device or biometric data, passkeys provide a streamlined and convenient way of logging into supported services. And while convenient, they are also very secure thanks to the underlying tech that makes it possible: public-key cryptography. Interested in learning more about passkeys and how they work?

In this article, we’ll discuss what passkeys are, as well as some of the benefits they provide.

## What are passkeys?

Passkeys are a method of signing into websites and services using cryptographically generated keys which are often stored on physical devices such as hardware tokens and modern smartphones.

When a passkey is created for a user account, the device that is being registered for the passkey will generate two keys: a public and a private key. The public key is sent to the service where the passkey is being registered, and the private key is stored securely on the device.

These keys are then used to securely sign into the service where the passkey is registered.

When the user tries to sign in using a passkey, the client will request a challenge from the server. The client then signs this challenge with its private key and sends it to the server. Once received, the server checks that the signature is valid using the public key it has.

Provided the signature is valid, the user is then signed into the service without having to enter any information manually.

![A sign in user diagram with passkeys.](./passkeys-signin-diagram.png)

1. User device requests a challenge from the server.
2. Server sends a challenge back, which goes to the authenticator
3. The authenticator checks with the user that they are trying to sign in.
4. On confirmation, the authenticator signs the challenge using the private key and sends it to the server
5. The server validates the signature with the user’s public key
6. If valid, the server completes the request and the user is signed in

The use of passkeys creates a passwordless experience that is both convenient and secure for the user signing into the service.

## How does public key crypto work?

To understand the magic behind passkeys, it's worth understanding how public-key cryptography works.

Public-key cryptography works by using a pair of keys in order to securely store, sign, or transmit data. The private key can be used to encrypt or decrypt data, as well as generate a signature based on the data. The private key should always be stored securely since anyone who has the private key can also sign and decrypt data.

The public key can be used only to encrypt data, or to validate a signature from the private key. The public key can be distributed without risk as its primary utility is to secure data before sending it to a destination that has the private key.

Because this system uses two different keys, it is also referred to as asymmetric cryptography.

## A practical use of public-key cryptography

While passkeys are relatively new, public-key cryptography has been around since the 1970s and has a wide number of applications and use cases. One such application that demonstrates these concepts well is end-to-end encrypted messaging.

Let’s say Ava and Brent use a secure chat app to communicate regularly. They each have a set of public and private keys. When they first connected, Ava would have sent her public key to Brent, and Brent would have sent his key to Ava.

![A diagram showing how public keys are exchanged between Ava and Brent.](./chat-key-exchange.png)

When Ava sends a message to Brent, her device encrypts the message using Brent's public key and the encrypted data is transmitted over the internet to Brent. When Brent receives the message, his device would use his private key to decrypt it before displaying it.

![A diagram showing how messages are encrypted and decrypted between Ava and Brent.](./chat-encrypted-message.png)

Now let’s say Charlotte tries to intercept the messages Ava is sending to Brent. Because the messages are encrypted and Charlotte does not have Brent's private key, the data being received is unreadable.

![A diagram showing how Charlotte is unable to read the encrypted messages.](./chat-mitm.png)

Even though this example uses a chat application to demonstrate how public and private keys work together, the same can be applied to the login process for passkeys.

![A diagram showing a public key being sent to a server and a private key being stored on a device.](./client-server-key-exchange.png)

## How are passkeys more secure than a username and password?

Passkeys are more secure than using a traditional username and password for a few reasons.

### Convenience

There is often an indirect relationship between how secure something is and how convenient it is. Passkeys however have the benefit of being convenient for users to take advantage of by providing a very streamlined experience to signing in to a service.

People tend to gravitate towards solutions that are easier to use. Combining the high security of public key crypto with the simplicity of just signing in by tapping a button, users of passkeys get the best of both worlds.

### Password reuse

Reusing passwords across services is a bad security practice. If one password gets compromised, whether it's your fault or theirs, any service where that password is also used should now be considered compromised as well. This can be a nightmare if you use the same password across dozens of services!

With passkeys, each service automatically gets a unique key pair. This means that if one service does lose control of its secrets, any other accounts you have that use passkeys are still secure, eliminating the work needed to cycle your passwords in the event of a breach.

### Credential strength

One strategy for hacking an account is by repeatedly guessing a password until the correct one is discovered. This is known as brute-forcing, and password length is often the biggest determining factor on how realistic it is to brute-force a password. The longer the password, the less chance it can be guessed.

A 16-character/byte password is often considered very secure, but passkeys are approximately 100-1400 bytes [according to the Electronic Frontier Foundation](https://www.eff.org/deeplinks/2023/10/what-passkey), which means they are nearly impossible to determine with this approach.

### Built-in MFA

Multifactor authentication (or MFA) is the use of multiple methods to verify that the user attempting to sign in is who they claim to be. Common factors used are something you know, something you have, or something you are.

For instance, if you sign into a service with a username and password, those are considered a single factor since you know both of those pieces of data (and one is often public). Adding an authenticator app or hardware token adds an additional factor since it utilizes something you have.

With Passkeys, MFA is built in. The device (along with the public/private key pair it generates) is one factor, and since biometric data is often used to unlock the device, that's a second factor.

### Eliminates phishing attempts

Phishing is where an attacker creates a look-alike website and attempts to trick users into entering their credentials. For example, a user might receive an email that appears to be from Amazon stating they are going to place an order that was never approved and telling the user they can manage it using a link embedded in the email. Instead of this link going to "amazon.com", it may go to "anazon.com" or another similar domain.

Since the domain looks close enough to the real one, the user may click on it and enter their credentials, which are then stolen by the attacker.

Since the passkeys generated on a device are specific to the domain for which they were created, this removes phishing as an attack vector. If an attacker does mimic a website and tries to get the user to log in, the device will understand that the domain is incorrect and will not offer the option to sign in. This also includes domain names that contain non-standard characters that appear to look the same.

## Clerk supports passkeys

Passkeys are an officially supported authentication method for services secured with Clerk.

You can enable passkeys just like you would any other login method for your users.

![The dashboard showing the passkey option in the authentication settings.](./dashboard-passkeys.png)

Once enabled, your users can add passkeys through their User Profile.

![The User Profile showing the option to add a passkey.](./add-passkey.png)

When signing in, they’ll be presented with the option to use a passkey.

![The sign in modal showing the option to sign in with a passkey.](./signin-passkeys.png)

For more information on using passkeys with Clerk, check out [the passkeys overview in our docs](/docs/authentication/configuration/sign-up-sign-in-options#passkeys).

---

# How to Add an Onboarding Flow for your Application with Clerk
URL: https://clerk.com/blog/add-onboarding-flow-for-your-application-with-clerk.md
Date: 2024-01-30
Category: Engineering
Description: Leverage Clerk’s customizable session tokens, publicMetadata and Next’s Middleware to create a robust onboarding experience within a few lines of code.

As part of your onboarding flow, you may want to collect extra information from your user and use it to drive your application state. Let’s walk through a quick example using Next.js and TypeScript to show you how simple implementing an onboarding flow can be.

In this guide, you will learn how to:

1. Add custom claims to your session token
2. Configure your middleware to read session data
3. Update with the user’s onboarding state

To see a working example, check out our [sample demonstration app here](https://sample-onboarding-app.clerkpreview.com).

The examples below have been pared down to the bare minimum to enable you to easily customize them to your needs, you
can build them with the [Clerk + Next Quickstart](/docs/quickstarts/nextjs) using @clerk/nextjs 6.0.2 and Next
15.0.2.

Let’s get started!

![Add Onboarding Flow For Your Application With Clerk guide illustration](./3e1f0959942a439402674187ee45ef6c592e5e38-2400x1392.png)

## Add Custom Claims to Your Session Token

Session tokens are JWTs that are generated by Clerk on behalf of your instance, and contain claims that allow you to store data about a user’s session. With Clerk, when a session token exists for a user, it indicates that the user is authenticated, and the associated claims can be retrieved at any time. \[[Learn More](/docs/backend-requests/making/custom-session-token)]

First, navigate to [Sessions](https://dashboard.clerk.com/last-active?path=sessions) in your Clerk Dashboard and click the ‘Edit’ button. In the modal that opens, there will be a window where you can augment your session token with custom claims.

In there, add the following and hit save:

```json
{
  "metadata": "{{user.public_metadata}}"
}
```

If you haven’t already, we can make the public metadata type information accessible to our application by adding the following to `src/types/globals.d.ts`:

```typescript {{ filename: 'src/types/globals.d.ts' }}
export {}

declare global {
  interface CustomJwtSessionClaims {
    metadata: {
      onboardingComplete?: boolean
      applicationName?: string
      applicationType?: string
    }
    firstName?: string
  }
}
```

We have just added custom data to our session token in the Clerk Dashboard and made those claims accessible to our app. Next, we’ll use `clerkMiddleware` to redirect the user based on `onboardingComplete` status.

## Configure your Next.js middleware to read session data

Clerk's `clerkMiddleware()` allows you to configure access to your routes with fine grained control. You can also retrieve claims directly from the session and redirect your user accordingly. \[[Learn More](/docs/references/nextjs/clerk-middleware#usage)]

Add the code sample below to your `src/middleware.ts` file:

```typescript {{ filename: 'src/middleware.ts' }}
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'

const isPublicRoute = createRouteMatcher(['/', '/onboarding'])

export default clerkMiddleware(async (auth, req) => {
  const { userId, sessionClaims, redirectToSignIn } = await auth()

  // If the user isn't signed in and the route is private, redirect to sign-in
  if (!userId && !isPublicRoute(req)) {
    return redirectToSignIn({ returnBackUrl: req.url })
  }

  // Catch users who do not have `onboardingComplete: true` in their publicMetadata
  // Redirect them to the /onboading route to complete onboarding
  if (
    userId &&
    !sessionClaims?.metadata?.onboardingComplete &&
    req.nextUrl.pathname !== '/onboarding'
  ) {
    const onboardingUrl = new URL('/onboarding', req.url)
    return NextResponse.redirect(onboardingUrl)
  }

  // If the user is logged in and the route is protected, let them view.
  if (userId && !isPublicRoute(req)) {
    return NextResponse.next()
  }
})

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}
```

Next, create a `layout.tsx` file in `src/app/onboarding` and add the following code to the file. This logic could go in the Middleware, but by adding to the `layout.tsx` to the route the logic remains in one place. This file can also be expanded to handle multiple steps, if multiple steps are required for an onboarding flow.

```tsx {{ filename: 'src/app/onboarding/layout.tsx' }}
import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  // Check if a user has completed onboarding
  // If yes, redirect them to /dashboard
  if ((await auth()).sessionClaims?.metadata?.onboardingComplete === true) {
    redirect('/dashboard')
  }

  return <>{children}</>
}
```

Now that we have the logic for where to direct the user, we’ll need a way to track their onboarding status and note it on their session, let’s dig into that now!

## Update publicMetadata based on onboarding state

Updating a user's `publicMetadata` as they progress through the flow will allow us to recognize when they have successfully completed their onboarding and, per the logic above, are now able to access the application. \[[Learn More](/docs/users/metadata)]

To do this you need:

- A method in your backend to securely update the user `publicMetadata`
- A process in your frontend with logic to collect and submit all the information for onboarding. In this guide you’ll use an example form.

### Add userUpdate method to your backend

First, add a method in your backend, that will be called on form submission and update the user’s `publicMetadata` accordingly. The example below uses the `clerkClient` wrapper to interact with the Backend API.

Under `src/app/onboarding/_actions.ts` add the following code snippet:

```typescript {{ filename: 'src/app/onboarding/_actions.ts' }}
'use server'

import { auth, clerkClient } from '@clerk/nextjs/server'

export const completeOnboarding = async (formData: FormData) => {
  const client = await clerkClient()
  const { userId } = await auth()

  if (!userId) {
    return { message: 'No Logged In User' }
  }

  try {
    await client.users.updateUser(userId, {
      publicMetadata: {
        onboardingComplete: true,
        applicationName: formData.get('applicationName'),
        applicationType: formData.get('applicationType'),
      },
    })
    return { message: 'User metadata Updated' }
  } catch (e) {
    console.log('error', e)
    return { message: 'Error Updating User Metadata' }
  }
}
```

Now that we have a method to securely update our user’s `publicMetadata` we can call this server action from a client side form.

### Add a form to your frontend

With the backend updateUser method in place, we’ll add a basic page that contains a form to complete the onboarding process.

This example form that will capture an application name (applicationName) and application type of either B2C or B2B (applicationType). This is a very loose example — you can use this step to capture information from the user, sync user data to your database, have the user sign up to a course or subscription, or more.

To implement this logic, insert the following into your `src/app/onboarding/page.tsx`:

```tsx {{ filename: 'src/app/onboarding/page.tsx' }}
'use client'

import * as React from 'react'
import { useUser } from '@clerk/nextjs'
import { useRouter } from 'next/navigation'
import { completeOnboarding } from './_actions'

export default function OnboardingComponent() {
  const { user } = useUser()
  const router = useRouter()

  const handleSubmit = async (formData: FormData) => {
    await completeOnboarding(formData)
    await user?.reload()
    router.push('/dashboard')
  }
  return (
    <div className="px-8 py-12 sm:py-16 md:px-20">
      <div className="mx-auto max-w-sm overflow-hidden rounded-lg bg-white shadow-lg">
        <div className="p-8">
          <h3 className="text-xl font-semibold text-gray-900">Welcome!</h3>
        </div>
        <form action={handleSubmit}>
          <div className="space-y-4 px-8 pb-8">
            <div>
              <label className="block text-sm font-semibold text-gray-700">
                {' '}
                Application Name{' '}
              </label>
              <p className="text-xs text-gray-500">Enter the name of your application.</p>
              <input
                type="text"
                name="applicationName"
                className="mt-1 w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
              />
            </div>

            <div>
              <label className="block text-sm font-semibold text-gray-700">Application Type</label>
              <p className="text-xs text-gray-500">Describe the type of your application.</p>
              <input
                type="text"
                name="applicationType"
                className="mt-1 w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
              />
            </div>
          </div>
          <div className="bg-gray-50 px-8 py-4">
            <button
              type="submit"
              className="w-full rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
            >
              Submit
            </button>
          </div>
        </form>
      </div>
    </div>
  )
}
```

## Wrap Up

Your onboarding flow is now complete! 🎉 New users who haven’t yet onboarded will now land on your `/onboarding` page and, once they have completed onboarding, will be sent through to the dashboard. By using Clerk, which already handles user authentication, we were able to simplify the process of creating a custom user onboarding flow as well.

---

# Introducing has(), protect(), and <Protect>
URL: https://clerk.com/blog/introducing-authorization.md
Date: 2023-12-13
Category: Engineering
Description: Authorization is now a core feature of Clerk – learn more about our new authorization helpers for B2B SaaS

Clerk is best-known for our dead-simple *authentication* with `<SignIn/>` and `<SignUp/>` components. But since launching Organizations for onboarding business customers, our top feature request has become better *authorization* capabilities: not just helping developers determine *who* a user is, but also what permissions they have within their organization.

Today we’ve launched two new features to make robust authorization easy:

1. Custom roles and permissions
2. `has()`, `protect()`, and `<Protect>` – our new authorization helpers

## Custom roles and permissions

In Clerk’s dashboard, developers can now define custom roles, which their business customers can assign to their employees. Previously, roles were restricted to just “admin” and “member.”

In addition, fine-grained permissions can now be defined and mapped to roles. When used properly, this extra layer of abstraction allows developers to create additional roles as-needed, without making any changes to their codebase.

Each role and permission defined in Clerk’s dashboard has a corresponding key, like `org:admin` or `org:widgets:create`.

![Custom roles and permissions screenshot](./d69547abef435115556125370cab753a5e7d08f3-2062x1298.png "The new role manager in Clerk's dashboard")

## has(), protect(), and \<Protect>

To make it easy to leverage permissions for authorization checks, we’ve introduced three new helpers in our SDKs.

### 1. has()

Developers can use the `has()` function on both the frontend and the backend to determine if the active user “has” a particular role or permission.

`has()` can be used anywhere that Clerk returns an “auth” object:

- `auth()` in Next.js App Router
- `useAuth()` in client components, including during SSR
- `getAuth(request)` in server contexts outside of the App Router (Remix Loaders, Node, Express, Fastify, etc)

```typescript
// Within a server component, server action, or route handler
const CreateWidget = () => {
  const { has } = auth()
  if (!has({ permission: 'org:widgets:create' })) {
    return null
  }
  // ...
}

// Within a client component
export const AdminsOnly = () => {
  const { has } = useAuth()
  if (!has({ role: 'org:admin' })) {
    return null
  }
  // ...
}

// Within an Express API route
app.post('/api/widgets', (req, res) => {
  const { has } = getAuth(req)
  if (!has({ permission: 'org:widgets:create' })) {
    return null
  }
  // ...
})
```

### 2. protect()

The `protect()` function is a declarative extension of `has()`, which takes definitive action to block further processing, instead of allowing developers to manually handle the unauthorized case. It is intended to be used in cases where unauthorized users are assumed to be attackers.

```typescript
export const POST = () => {
  const { userId, orgId } = auth().protect({ permission: 'org:widgets:create' })

  // Protect would throw an error if the user is unauthorized
  // If this point is reached, it is guaranteed that userId and ordId exist,
  // and that userId has the "org:widgets:create" permission in orgId

  // Bonus: TypeScript knows that userId and orgId exist!
  // ...
}
```

Consider an endpoint that your product would never issue a request to unless the current user is authorized. For example, you may have a page with a button that can issue requests to that endpoint, but the page is only accessible to authorized users. That is the perfect place to use `protect()` – you don't need a precisely-written error, just confidence that an attacker's requests will fail.

Note: `protect()` has launched in Next.js only, though we will extend the helper to other frameworks in the future. Please use [feedback.clerk.com](https://feedback.clerk.com) to request the feature for your framework.

### 3. \<Protect>

Similar to the `protect()` function, the `<Protect>` component is intended to block further processing. It only renders children for authorized users, and optionally renders a `fallback` for unauthorized users.

`<Protect>` can be used across all React frameworks, in CSR, SSR, and RSC contexts.

```tsx
export const Navigation = () => {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/widgets">Widgets</Link>
      <Protect role="org:admin">
        <Link href="/admin">Admin Panel</Link>
      </Protect>
    </nav>
  )
}
```

## Documentation

For full details and usage information, please see our updated Organization documentation, including:

- [Roles and Permissions overview](/docs/organizations/roles-permissions)
- [Create Roles and assign Permissions](/docs/organizations/create-roles-permissions)
- [Verify the active user’s permissions](/docs/organizations/verify-user-permissions)

## Authorization is now a core feature of Clerk

With today’s launch, authorization has become a core feature of Clerk, and you will see significant, continued investment in the future.

In 2024, we will extend our authorization helpers to support entitlements, so Clerk can be used to gate features based on the subscription plan a customer selected.

Further, you likely noticed above that every role and permission is namespaced with the `org:` prefix, which relates to the Organization resource. We recognize this is limiting, and in the future we will be extending our authorization helpers to work with other resources.

---

# Next.js Authentication with Clerk: Streamlined SSR Handling
URL: https://clerk.com/blog/nextjs-auth-clerk-streamlined-ssr-efficiency.md
Date: 2023-11-18
Category: Engineering
Description: Discover streamlined Next.js authentication with Clerk, simplifying SSR for efficient user data handling in web development.

In the ever-evolving world of web development, streamlining tasks is paramount. Clerk, the versatile User Management platform, presents a contemporary way to manage user data in Next.js applications, leaving behind the complexity of old patterns for server-side rendering (SSR). Let's explore the key differences and the value of this new approach with basic code snippets.

## Navigating Next.js SSR: The Previous Approach

Previously, incorporating Clerk in a Next.js app involved intricate setups and token management. Here's a reminder of what the deprecated approach looks like

```typescript {{ title: 'Deprecated approach' }}
import { withServerSideAuth } from '@clerk/nextjs/ssr'

export const getServerSideProps = withServerSideAuth(({ req, resolvedUrl }) => {
  const { sessionId } = req.auth

  if (!sessionId) {
    return { redirect: { destination: '/sign-in?redirect_url=' + resolvedUrl } }
  }

  return { props: {} }
})
```

## **The New Way: Streamlined Handling**

These are just a few examples of the new streamlined approaches. If you are looking for a more comprehensive breakdown of the best way to read session data in your Next.js app using the [Pages Router](https://nextjs.org/docs/pages) or [App Router](https://nextjs.org/docs/app), please [reference the docs](/docs/references/nextjs/read-session-data?utm_source=DevRel\&utm_medium=blog\&utm_campaign=docslanding\&utm_content=mirco-blog).

### SSR in App Router – Server Component

The modern Clerk approach in App Router simplifies user data handling with straightforward helper functions. Here's a snippet of the new approach:

```tsx {{ title: 'NextJS new approach - App Router ' }}
import { currentUser } from '@clerk/nextjs'

export default async function Example() {
  const user = await currentUser()

  return (
    <div>
      Hello {user.firstName}, your ID is {user.id}
    </div>
  )
}
```

### SSR in Pages Router

If you are using the Pages Router, you can find more detailed examples beyond this basic snippet in the [docs.](/docs/references/nextjs/build-clerk-props?utm_source=DevRel\&utm_medium=blog\&utm_campaign=docslanding\&utm_content=micro-blog)

```typescript {{ title: 'NextJS new approach - Pages Router ' }}
import { getAuth, buildClerkProps } from '@clerk/nextjs/server'
import { GetServerSideProps } from 'next'

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const { userId } = getAuth(ctx.req)

  if (!userId) {
    // handle user is not logged in.
  }

  // Load any data your application needs for the page using the userId
  return { props: { ...buildClerkProps(ctx.req) } }
}
```

### **The Value of the New Approach**

1. **Simplicity**: The new approaches offer a cleaner, more straightforward codebase for effortless data access.
2. **Efficiency**: Data access becomes a breeze.
3. **Improved Workflow**: Focus on building features, not grappling with complex User Management setups.
4. **Maintainability**: A tidy codebase equals easier maintenance and fewer debugging hassles.

Dive headfirst into Clerk's innovative approach to supercharge your development process, ensuring your apps are always at the cutting edge. This newfound simplicity isn't just for show; it's here to make your work smoother, your code cleaner, and your applications more maintainable. And what's even more exciting? This flexibility isn't confined to one corner of your project; it stretches its arms to [Route Handlers](/docs/reference/nextjs/app-router/route-handlers) as well, making it the perfect fit for the demands of modern web development.

## **Ready to Implement Authentication in Your App?**

Don't hesitate to explore our [Next.js Clerk Docs](/docs/references/nextjs/overview?utm_source=DevRel\&utm_medium=blog\&utm_campaign=docslanding\&utm_content=micro-blog) for a quick and comprehensive guide on integrating Clerk [Next.js authentication](/nextjs-authentication) into your application within minutes, not days.

For more in-depth technical inquiries or to engage with our community, feel free to [join our Discord](https://clerk.com/discord). Stay in the loop with the latest Clerk features, enhancements, and sneak peeks by following our X account, [@clerk](https://x.com/clerk). Your journey to seamless User Management starts here!

---

# Stable Support for the Next.js App Router, plus a Middleware Update!
URL: https://clerk.com/blog/nextjs-13-4.md
Date: 2023-05-04
Category: Engineering
Description: App Router support is out of beta, plus we've launched a major middleware update.

## @clerk/nextjs\@4.17.0 is released

Clerk has updated our SDK to support Next.js version 13.4.0. We've added stable support for the App Router, which is the default for new applications. We've also improved the ergonomics of our middleware helper for more composability and to mitigate common pitfalls.

[See the updates in our new Next.js documentation](/docs/nextjs/get-started-with-nextjs), or read on for more details.

### Stable App Router Support

Next.js version 13.4.0 launched today and the App Router is now stable. In turn, Clerk's support for the App Router is also now stable in `@clerk/nextjs` version 4.17.0.

If you previously adopted `@clerk/nextjs/app-beta`, there have been very few changes:

- The `auth()` and `currentUser()` helpers have been moved to a permanent home in `@clerk/nextjs`
- React imports from `@clerk/nextjs/app-beta/client` are no longer necessary, and you can import from `@clerk/nextjs` intstead. The package now automatically detects if components are being used from a client component or a server component, and loads the proper form.

That's all! Our documentation has been updated to [reflect the changes](/docs/nextjs/get-started-with-nextjs).

### Middleware Update

Along with today's launch, we've also released a new middleware helper to make things a little easier and more composable.

In its simplest form, these few lines are all you need to [integrate Clerk](/nextjs-authentication):

```typescript
import { authMiddleware } from '@clerk/nextjs'

export default authMiddleware()

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)'],
}
```

This sets your application in a protected mode – if users are signed out the will be automatically redirected to a sign in. Unprotected pages need to be explicitly listed in the `publicRoutes` argument:

```typescript
export default authMiddleware({
  // Make the homepage accessible while signed out
  publicRoutes: ['/'],
})
```

This new helper also has `beforeAuth` and `afterAuth` arguments to enable more composability with libraries like `next-intl`. For more details, checkout the updated [middleware documentation](/docs/nextjs/middleware).

---

# Refactoring our frontend API key: Familiar DX is the best DX
URL: https://clerk.com/blog/refactoring-our-api-keys.md
Date: 2023-01-27
Category: Engineering
Description: We switched to the familiar Publishable Key, but we changed less than you'd think

Like most other developer tools, Clerk's SDKs are configured with two "keys," one for the backend and one for the frontend.

And we share the same core requirements:

- The backend key must have significantly random so it cannot be guessed
- The frontend key is public, so it only needs to be a unique identifier

Although there's no security benefit in doing so, many tools have their frontend key mirror the format and length of their backend key. Take Stripe, for example:

!\[Although there's no security benefit in doing so, many tools have their frontend key mirror the format and length of their backend key. Take Stripe, for example: screenshot]\(./995856b7b27f199531ec648ab53d2866eaeae372-814x332.png '(Don't worry, the secret key has been rolled.)

But as an authentication company, Clerk has an extra requirement for our frontend key, and it meant that mirroring our secret key would cause *performance* issues.

Wait, what? How could a key cause performance issues?

The purpose of a frontend key is to be a unique identifier when interacting with a frontend-facing API. Here's an example of how Stripe's SDK uses the publishable key:

![The purpose of a frontend key is to be a unique identifier when interacting with a frontend-facing API. Here's an example of how Stripe's SDK uses the publishable key: screenshot](./6de4356a8cc28037f37729a941cdfb6e483ce6ef-689x175.png "That's the same publishable key as above!")

Notice how this request is being sent to a **stripe.com** domain? Stripe SDKs always make requests to **stripe.com**, even if you're embedding their elements into your own website.

But since Clerk runs an authentication API and since we're responsible for maintaining sessions, we can't securely do the same. We need to set [HttpOnly cookies](https://owasp.org/www-community/HttpOnly) from a first-party context, which means our API needs to be accessible through our customers' domains. *(Developers configure this by setting a CNAME in their DNS records.)*

When we launched, our frontend key was simply the hostname where our frontend API is hosted. Developers configured it like this in their React apps:

```jsx {{ prettier: false }}
<ClerkProvider frontendApi="clerk.example.com">
```

By passing the API hostname directly, we avoided making an extra, waterfalled API request to exchange a traditional random key with the hostname. This led to faster overall loading speeds.

But we hit an unexpected problem with this strategy: it *really* confused developers. It was a common complaint in friction logs, and when we watched developers integrate Clerk we could see the confusion wash over their face.

A hostname as a frontend key is completely unfamiliar, and no matter what we tried with design and naming, we couldn't get over the hurdle.

So this week, we finally threw in the towel. Our new frontend API keys look just like Stripes:

![So this week, we finally threw in the towel. Our new frontend API keys look just like Stripes: screenshot](./483632fdf53ea7aa1f455e10d9ca7faa463384e5-1006x197.png "Clerk's new publishable key")

But we refuse to exchange developer experience for performance, so there's a not-so-secret subtlety to our new publishable key. Take a closer look:

`pk_test_Y2xlcmsuZXhhbXBsZS5jb20k`

Now, base64-decode the part after `pk_test_`:

`clerk.example.com$`

Indeed, we just base64-encoded the old value and started calling it a publishable key. We added a $ as a simple stop character so we can detect when keys are malformed.

As a final step, we changed the prop name for React:

```jsx {{ prettier: false }}
<ClerkProvider publishableKey="pk_test_Y2xlcmsuZXhhbXBsZS5jb20k">
```

Quick, dirty, and a little silly – but it works! We haven't sacrificed performance and our new publishable key feels much more familiar. Developers have stopped raising their eyebrows and breeze through initial setup.

---

# How to skip Next.js middleware for static and public files
URL: https://clerk.com/blog/skip-nextjs-middleware-static-and-public-files.md
Date: 2022-10-06
Category: Engineering
Description: Stop your Next.js middleware from running on static assets like images and Next.js internals

[Next.js middleware](https://nextjs.org/docs/advanced-features/middleware) is incredibly powerful, but by default it runs on more requests than is normally necessary. To see this in action, you can add a simple logger:

```javascript {{ title: 'middleware.js' }}
export default function middleware(req) {
  console.log(req.url)
}
```

Here's the log for loading [http://localhost:3000/](http://localhost:3000) on a brand new Next.js application:

```text
http://localhost:3000/
http://localhost:3000/_next/static/chunks/webpack.js?ts=1665116452838
http://localhost:3000/_next/static/chunks/main.js?ts=1665116452838
http://localhost:3000/_next/static/chunks/react-refresh.js?ts=1665116452838
http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1665116452838
http://localhost:3000/_next/static/development/_buildManifest.js?ts=1665116452838
http://localhost:3000/_next/static/development/_ssgManifest.js?ts=1665116452838
http://localhost:3000/_next/static/chunks/pages/index.js?ts=1665116452838
http://localhost:3000/favicon.ico
http://localhost:3000/_next/static/development/_devMiddlewareManifest.json
http://localhost:3000/_next/static/development/_devPagesManifest.json
http://localhost:3000/vercel.svg
```

The log shows several static assets that you probably don't want to process in your middleware, and doing so could break your application. To solve this, you can configure a [matcher](https://nextjs.org/docs/advanced-features/middleware#matcher), which tells your middleware to only run on requests that match a certain pattern.

To prevent middleware from running on static files, you can leverage a simple trick: static files have a **`.`** in the path, while dynamic files do not.

With a regular expression, you can configure middleware to run on every file that does not contain a **`.`**:

```javascript {{ title: 'middleware.js' }}
export default function middleware(req) {
  console.log(req.url)
}

export const config = { matcher: '/((?!.*\\.).*)' }
```

With this matcher, the log only shows one request:

```text
http://localhost:3000/
```

This matcher is a great starting point that minimizes middleware invocation. As your application grows, though, you may need to tweak it. Below are the two most common revisions.

### Including *some* static assets

The easiest way to include some static assets in middleware is to retain the exclusionary regular expression, but define an *additional* matcher. Here's a sample that excludes all static assets from middleware except `favicon.ico`:

```javascript {{ title: 'middleware.js' }}
export const config = {
  matcher: ['/((?!.*\\.).*)', '/favicon.ico'],
}
```

### Excluding more paths from middleware

Sometimes you will want to filter more than just static files. This can be done in two ways:

#### 1. Expanding the regular expression

Expanding the regular expression prevents additional middleware invocations, but regular expressions are also harder to read and write. Here's an example that excludes API routes in addition to static assets:

```javascript {{ title: 'middleware.js' }}
export const config = { matcher: '/((?!.*\\.|api\\).*)' }
```

#### 2. Adding a conditional statement

A more readable approach is to create a conditional statement, but this will waste compute resources since your middleware will be unnecessarily invoked. Here's an example:

```javascript {{ title: 'middleware.js' }}
import { NextResponse } from 'next/server'

export default function middleware(req) {
  if (req.nextUrl.pathname.startsWith('/api/')) {
    return NextResponse.next()
  }
  // ... your middleware
}

export const config = { matcher: '/((?!.*\\.).*)' }
```

This should be everything you need to get started on the right foot with middleware!

---

# Let's stop arguing about JWTs and just fix them
URL: https://clerk.com/blog/lets-stop-arguing-about-jwts-and-just-fix-them.md
Date: 2022-10-05
Category: Engineering
Description: JWTs have won. It's time we embrace them and fix the dangerous implementations.

This week there was a [fresh](https://news.ycombinator.com/item?id=33019960) [batch](https://news.ycombinator.com/item?id=33061873) of debates about JWTs as session tokens on Hacker News. For the uninitiated, this happens incredibly frequently on HN, to the point where moderator dang has posted roundups:

![Lets Stop Arguing About Jwts And Just Fix Them guide illustration](./5e84204a848495c412d2cf8d45a8b193b3417bcd-1258x476.png)

Just about every thread goes the same way:

1. Everyone agrees that infrequently-refreshed, stateless JWTs should not be used as session tokens because they're irrevocable
2. Everyone agrees that it's pointless to use long-lived, stateless JWTs in combination with a stateful, database-backed blocklist of revoked JWTs
3. Everyone agrees that database-backed, stateful session tokens are easy to build and sufficient for the 99% case
4. With trepidation, everyone agrees that frequently-refreshed, stateless JWTs are acceptable as session tokens because the short expiration serves as a revocation mechanism

Why the trepidation on 4? Usually, it's because 3 is easier. Here's [one viral post's](https://apibakery.com/blog/tech/no-jwt) rationale (they refer to stateful session tokens as bearer tokens):

> \[...] you've just reintroduced a bearer token, because that's exactly what the refresh token is. Looking at JWTs from that perspective, you've introduced a client-side cache of user identity (the JWT) and added a bunch of complexity (involving the creation, verification, and token refresh) for the hope of optimizing part of the work you used to do on the server (checking user identity using a bearer token). Was it worth it?

The author is right: The complexity of 4 is high and the benefits for the 99% case are minimal.

Yet, JWTs are clearly winning. Their usage has grown, not dwindled, while two major trends in software development have increased their demand.

### Trend 1: Developers are relentlessly focused on efficiency

This has been a trend for decades. It's *impressive* that today's developers care about a 10ms query vs a \<1ms JWT, but it's not *surprising*.

The simple reality is that Google Lighthouse scores – and the wealth of evidence supporting that users prefer fast applications – have placed an unprecedented emphasis on loading speed.

The developers who care most are no stranger to complexity, as they're often thinking about problems like distributed storage and operating at the edge. It's no surprise they're eager to treat one less query as an easy win.

### Trend 2: Modern integrations often require a synced session

This trend is more recent. Third-party integrations are sweetening the buy-vs-build deal by doing more for their customers. If an integration knows which user is signed in and their permissions, then it can expose APIs directly to the frontend, and even offer user-facing UIs as part of their service:

- New database tools like Supabase, Hasura, Grafbase, Convex all allow querying from the frontend, and use JWTs to know which user is signed in
- Embeddable help desk and community tools like Zendesk and Canny use JWTs to know which user is signed in
- Stripe's user-facing UIs (Checkout, Customer Portal, Payment Element) use a backend API request and session token to know which user is signed in (not a JWT, but still part of the trend!)

The newer a tool is, the more likely it is to allow syncing sessions by JWT.

### It's time to embrace JWTs and disarm the footguns

These trends aren't going anywhere. It's time we embrace JWTs and fix the bad implementations that have stained their reputation:

#### For web developers

Check your application code. If you use stateless JWTs (no database check) and they don't expire in 5 minutes or less, report it to your security team.

#### For authentication tools

Don't generate session JWTs with a lifetime greater than 5 minutes. If developers want to configure a longer lifetime, warn that it's bad practice for XSS attacks.

Provide a refresh mechanism that depends on a HttpOnly cookie in the browser.

#### For integrations

Don't accept session JWTs with a lifetime greater than 5 minutes. If you must, warn developers that it's bad practice for XSS attacks.

Critically, provide developers with a mechanism to send a new JWT after the first JWT expires – don't receive a JWT once and then manage an independent session.

### Really, just expiration?

Yes, long expiration is the #1 most common security mistake with JWTs. It leaves many sites especially vulnerable when XSS attacks or account takeovers occur.

#### Does Clerk do this?

Yes! Clerk's session JWTs default to a 1 minute lifetime, and our refresh mechanism depends on an HttpOnly cookie. We even offer "Active Device" management in our `<UserProfile/>` component so end-users can revoke their own sessions.

---

# How to pass a value from Next.js middleware to API routes and getServerSideProps
URL: https://clerk.com/blog/nextjs-pass-value-from-middleware-to-api-routes-and-getserversideprops.md
Date: 2022-10-05
Category: Engineering
Description: Compute a value in middleware and pass it to your API route or getServerSideProps. Works in both Node and Edge runtimes.

Unlike many web frameworks, [Next.js middleware](https://nextjs.org/docs/advanced-features/middleware) doesn't have a built-in mechanism for passing values from middleware to other parts of the application.

> For authentication-specific middleware patterns in Next.js, see our [Next.js Authentication](/nextjs-authentication) solution.

Instead, middleware has a feature called "rewrites" that we can leverage to pass data.

The high-level idea is to "rewrite" requests to the same URL, but modify the request's metadata to include the data we want to pass. Then, we can read the metadata from our API route or getServerSideProps.

Unfortunately, there are inconsistencies across runtimes that make it difficult to get this working. We hope these snippets help save you save you some headaches.

**License: MIT**

**[Live Demo](https://nextjs-context-demo.vercel.app) | [Source Code](https://github.com/clerkinc/nextjs-context-example)**

**Usage: middleware.js**

```javascript
import { NextResponse } from 'next/server'
import { withContext } from './context'

// Pre-define the possible context keys to prevent spoofing
const allowedContextKeys = ['foo']

export default withContext(allowedContextKeys, (setContext, req) => {
  setContext('foo', 'bar')
  return NextResponse.next()
})
```

**Usage: API route (Node)**

```javascript
import { getContext } from '../../context'

export default function handler(req, res) {
  res.status(200).json({ foo: getContext(req, 'foo') })
}
```

**Usage API route (Edge)**

```javascript
import { getContext } from '../../context'

export default function handler(req) {
  return new Response(JSON.stringify({ foo: getContext(req, 'foo') }))
}
```

**Usage: getServerSideProps (Edge and Node)**

```javascript
import { getContext } from '../context'

export const getServerSideProps = ({ req }) => {
  return { props: { foo: getContext(req, 'foo') } }
}
```

**Source: (saved to context.js on your root)**

```javascript
import { NextResponse } from 'next/server'

const ctxKey = (key) => `ctx-${key.toLowerCase()}`

export const getContext = (req, rawKey) => {
  const key = ctxKey(rawKey)

  let headerValue =
    typeof req.headers.get === 'function'
      ? req.headers.get(key) // Edge
      : req.headers[key] // Node;

  // Necessary for node in development environment
  if (!headerValue) {
    headerValue = req.socket?._httpMessage?.getHeader(key)
  }

  if (headerValue) {
    return headerValue
  }

  // Use a dummy url because some environments only return
  // a path, not the full url
  const reqURL = new URL(req.url, 'http://dummy.url')

  return reqURL.searchParams.get(key)
}

export const withContext = (allowedKeys, middleware) => {
  // Normalize allowed keys
  for (let i = 0; i < allowedKeys.length; i++) {
    if (typeof allowedKeys[i] !== 'string') {
      throw new Error('All keys must be strings')
    }
    allowedKeys[i] = ctxKey(allowedKeys[i])
  }

  return (req, evt) => {
    const reqURL = new URL(req.url)

    // First, make sure allowedKeys aren't being spoofed.
    // Reliably overriding spoofed keys is a tricky problem and
    // different hosts may behave different behavior - it's best
    // just to safelist "allowedKeys" and block if they're being
    // spoofed
    for (const allowedKey of allowedKeys) {
      if (req.headers.get(allowedKey) || reqURL.searchParams.get(allowedKey)) {
        throw new Error(`Key ${allowedKey.substring(4)} is being spoofed. Blocking this request.`)
      }
    }

    const data = {}

    const setContext = (rawKey, value) => {
      const key = ctxKey(rawKey)
      if (!allowedKeys.includes(key)) {
        throw new Error(`Key ${rawKey} is not allowed. Add it to withContext's first argument.`)
      }
      if (typeof value !== 'string') {
        throw new Error(`Value for ${rawKey} must be a string, received ${typeof value}`)
      }
      data[key] = value
    }

    let res = middleware(setContext, req, evt) || NextResponse.next()

    // setContext wasn't called, passthrough
    if (Object.keys(data).length === 0) {
      return res
    }

    // Don't modify redirects
    if (res.headers.get('Location')) {
      return res
    }

    const rewriteURL = new URL(res.headers.get('x-middleware-rewrite') || req.url)

    // Don't modify cross-origin rewrites
    if (reqURL.origin !== rewriteURL.origin) {
      return res
    }

    // Set context directly on the res object (headers)
    // and on the rewrite url (query string)
    for (const key in data) {
      res.headers.set(key, data[key])
      rewriteURL.searchParams.set(key, data[key])
    }

    // set the updated rewrite url
    res.headers.set('x-middleware-rewrite', rewriteURL.href)

    return res
  }
}
```

**Known limitations:**

Depending on the runtime, your data will be transmitted as either an HTTP header or a URL query string. This leads to several limitations:

- Headers and query strings only accept strings for key/value pairs. If you'd like to use non-strings, you'll need to bring your own serializer
- Keys are lowercases because headers are case-insensitive
- Your host likely limits the total overall length of headers and query strings. [Here are the limits for Vercel's edge runtime](https://vercel.com/docs/concepts/functions/edge-functions/limitations#limits-on-requests), for example

---

# @clerk/nextjs v4.5
URL: https://clerk.com/blog/clerk-nextjs-4-5.md
Date: 2022-10-04
Category: Engineering
Description: New in 4.5: Authentication moves to middleware, switchable runtime support, improved developer experience, preparation for Layouts

Version 4.5 of **@clerk/nextjs** brings sweeping improvements to server-side authentication in Next.js.

Get started with `npm i @clerk/nextjs@latest`

## Authentication moves to middleware

Going forward, developers will authenticate requests once in [Next.js middleware](https://nextjs.org/docs/advanced-features/middleware), then simply read the authentication state in endpoints (API routes and getServerSideProps).

**middleware.js example:**

```javascript
import { withClerkMiddleware } from '@clerk/nextjs/server'

export default withClerkMiddleware((req) => {
  // Your own middleware
})
```

**API routes example:**

```javascript
import { getAuth } from '@clerk/nextjs/server'

export default function handler(req, res) {
  const { userId } = getAuth(req)
  // ...
}
```

**getServerSideProps example:**

```javascript
import { getAuth } from '@clerk/nextjs/server'

export const getServerSideProps = ({ req }) => {
  const { userId } = getAuth(req)
  // ...
}
```

With this change, [authentication in Next.js](/nextjs-authentication) will finally feel familiar for developers who have worked with frameworks like Express or Ruby on Rails, where authentication is normally handled within middleware.

## Switchable runtime support

Next.js 12.2 brought a switchable runtime, so developers could choose between a node runtime or an edge runtime for each of their API routes and server-rendered pages.

Our new **getAuth()** helper is isomorphic, so it works regardless of the runtime you choose.

## Improved developer experience

If you've used Clerk in the past, you'll notice three small but delightful improvements to our developer experience:

1. All server-side imports are now from **@clerk/nextjs/server** instead of having import paths specific to the endpoint-type
2. Wrapper functions are no longer required inside endpoints, only once in middleware
3. **getAuth(req)** works everywhere there's a request object

```javascript {{ title: '@clerk/nextjs v4.5', prettier: false }}
// pages/api/foo.js
import { getAuth } from '@clerk/nextjs/server'

export default function handler(req, res) {
  const { userId } = getAuth(req)
  // ...
}


// pages/bar.js
import { getAuth } from '@clerk/nextjs/server'

export const getServerSideProps = ({ req }) => {
  const { userId } = getAuth(req)
  // ...
}


// middleware.js
import { withClerkMiddleware, getAuth } from '@clerk/nextjs/server'

export default withClerkMiddleware((req) => {
  const { userId } = getAuth(req)
  // ...
})
```

```javascript {{ title: 'Previous' }}
// pages/api/foo.js
import { withAuth } from '@clerk/nextjs/api'

export default withAuth((req, res) => {
  const { userId } = req.auth
  // ...
})

// pages/bar.js
import { withServerSideAuth } from '@clerk/nextjs/ssr'

export const getServerSideProps = withServerSideAuth(({ req }) => {
  const { userId } = req.auth
  // ...
})

// middleware.js
// Clerk hasn't supported Next.js 12.2+ middleware until today
```

## Preparation for Layouts

Our team has been watching the [Next.js Layouts RFC](https://nextjs.org/blog/layouts-rfc) closely, and we designed the new helpers in anticipation of layouts support.

We expect that by computing the authentication state in middleware, we'll be able to share it with each of your parallel-loading layout files.

## Get started

Get started right away with our [guide to Next.js authentication](/docs/quickstarts/get-started-with-nextjs), or [join our community Discord](https://clerk.com/discord) to discuss with our team.

Thanks to the contributors: Mark Pitsilos, Nikos Douvlis

---

# Refactoring Stripe's API for frontend access
URL: https://clerk.com/blog/refactoring-stripes-api-for-frontend-access.md
Date: 2022-06-10
Category: Engineering
Description: We built `use-stripe-subscription` to make it easier for React developers to implement Stripe Billing

Today we launched [`use-stripe-subscription`](https://github.com/clerkinc/use-stripe-subscription), a package that makes it easier for frontend developers to implement Stripe billing. It features:

- `useSubscription()` - a React hook that returns:
  - `products` - an array of Product objects available for subscription
  - `redirectToCheckout()` - Redirects to Stripe Checkout to purchase a subscription to one of the `products`
  - `subscription` - the current customer’s active Subscription object, if it exists
  - `redirectToCustomerPortal()` - Redirects to Stripe Billing’s Customer Portal so the customer can manage their subscription
- `<Gate>` - a component that selectively renders children based on the customer’s subscription

`use-stripe-subscription` is open-source and was built to work with any authentication and user management solution, not just Clerk.

## Why did Clerk build this?

Clerk is a Customer Identity *Platform.* We don’t just handle authentication, we also make it easier to sync and leverage customer identity with developers’ favorite tools.

`use-stripe-subscription` leverages customer identity to add a new authorization layer to Stripe’s API. By default, Stripe’s API is only designed for backend access, since it relies on a secret key for authorization that cannot be exposed to the frontend.

We added a per-customer authorization layer, which allows frontend developers to securely retrieve subscription information about the signed-in customer, without exposing the secret key to the frontend.

This sounds fancier than it is: most teams using Stripe are effectively building this in-house. We just packaged the solution to save in-house teams from reinventing the wheel.

## Securing Stripe’s API for frontend access

To secure an API for frontend access, developers can refer to one, simple question:

> *What actions can a signed-in user perform on their own?*

Frontend APIs should be designed to power self-serve user interfaces. If the API is granted broader permissions, then a malicious actor may make unauthorized requests.

Luckily, Stripe’s API contains two objects that `use-stripe-subscription` leverages to determine which actions a user can perform on their own.

### The Customer object

In Stripe, every Subscription object belongs to a Customer object. The Customer object can represent an individual or a business.

As a long as an API can map the signed-in user to their Customer object, it’s trivial to restrict endpoints

- If a user *does not* have a Subscription, allow them to initialize a Checkout Session with their Customer ID to begin a new subscription
- If a user *does* have a Subscription, allow them to read the object, and to start a Customer Portal session to manage the subscription

### The Customer Portal Configuration object

Allowing users to start a Checkout Session from the frontend might sound unsafe – how can the API know which products a user is allowed to purchase on their own?

For this, we leverage Stripe’s Customer Portal product, which requires developers to specify which subscriptions a customer can switch between on their own:

![Refactoring Stripes Api For Frontend Access setup guide](./28f3676c57c3089778598df7cb781aa66675edb3-1764x948.png)

*Stripe’s Customer Portal settings page requires developers to configure which products a customer can switch between on their own*

Before `use-stripe-subscription` creates a Checkout Session, it verifies that the product is listed in the Customer Portal Configuration object. We assume that, if the configuration shows a customer is able to switch to a product, they should also be allowed to purchase that product new.

To make things easy for developers, the list of available subscription products is always accessible in the `products` attribute of the `useSubscription()` hook. This list is derived directly from the Customer Portal Configuration object.

## Passing in the Stripe Customer ID

To configure `use-stripe-subscription`, developers must create an endpoint on their server that communicates with Stripe’s API. The endpoint is responsible for retrieving the product list and current subscription information, as well as for generating Checkout and the Customer Portal sessions.

The package provides a complete Javascript implementation for this endpoint, except that developers must build their own function to determine the Stripe Customer ID associated with the request.

```javascript
import { subscriptionHandler } from 'use-stripe-subscription'

const handler = async (req, res) => {
  // Build your own findOrCreateCustomerId
  const customerId = await findOrCreateCustomerId(req)

  res.json(await subscriptionHandler({ customerId, query: req.query, body: req.body }))
}
```

This implementation is ideal because it works for both B2C and B2B subscription companies. The package doesn’t know (and therefore, is not opinionated about) whether the user is operating on behalf of a personal Customer object, or on behalf of a business’s Customer object.

## What about webhooks?

We know that frontend developers prefer to avoid webhooks, so `use-stripe-subscription` does not require them. Instead, it makes just-in-time API requests to Stripe to ensure it always has the latest data.

For very high-traffic websites, this strategy unfortunately has the potential to run into to Stripe’s API rate limit (100 read operations per second).

From our perspective, it’s quite unfortunate that Stripe asks developers to configure webhooks and setup a cache just to have access to updated data. It’s much simpler to query data directly from Stripe as it’s needed.

To alleviate this limitation, we investigating ways to add a robust caching solution to the package. Discussions and PRs toward this end are very much appreciated.

---

# Three best practices for building React REST SDKs
URL: https://clerk.com/blog/best-practices-for-building-react-rest-sdks.md
Date: 2022-05-20
Category: Engineering
Description: For optimal developer experience, React SDKs require completely different patterns than Node

## 1. No secret keys allowed

This may be obvious but it must be stated as #1. Since React hooks run in the browser, SDKs cannot use secret keys for API access.

Instead, the API should be designed to scope access based on the currently signed-in user. Then, a session token or a JWT can be used to authorize requests to the API.

## 2. GETs should be Hooks

This is the critical change that turns a Node SDK into a React SDK. In Node SDKs, GET methods usually return a Promise:

```jsx
import { getUser } from '@clerk/nextjs'

const user = await getUser()
```

Unfortunately, using a Promise in a React Component is a big headache. The developer needs to add `useEffect` and `useState` hooks to render a loading state while the Promise is pending, then update when the resolves.

The end result is quite verbose and hard to decipher:

```jsx
import { useEffect, useState } from 'react'
import { getUser } from '@clerk/nextjs'

const UserProfile = () => {
  const [user, setUser] = useState(null)
  useEffect(() => {
    const load = async () => {
      const res = await getUser()
      setUser(res)
    }
    load()
  }, [getUser, setData])
  if (!user) {
    return <div>Loading</div>
  }
  return <div>Name: {user.name}</div>
}
```

The solution is to provide developers with a hook instead of a Promise. Hooks are composable, so under the hood this is still built with `useEffect` and `useState`, it just doesn't burden the developer with setting them up manually:

```jsx
import { useUser } from '@clerk/nextjs'

const UserProfile = () => {
  const { user } = useUser()
  if (!user) {
    return <div>Loading</div>
  }
  return <div>Name: {user.name}</div>
}
```

That's much easier to use!

## 3. Hooks should "react" to mutations

There is still a problem in the example above. What if the user wants to change their name and the developer triggers an update:

```jsx
user.update({ name: 'Ben Bitdiddle' })
```

By default, the `useUser` hook in our `<UserProfile/>` component won't automatically refresh. But since it's a hook, developers will expect it to – that's the whole point of React!

There are two high-level strategies to refreshing the hook, **eager refresh** and **sequential refresh**.

### Eager refresh

Eager refresh is the most performant solution, but it can be hard to configure. The idea is that the API endpoint behind `user.update()` should return the updated User object in its response body. When the response is received, `user.update()` can propogate the new value to any components calling `useUser()`.

This example isn't particularly hard to configure because the mutation is contained to the `User` object. It can be achieved with a globally-scoped React context to store the value of the User object, instead of using `useState` directly in the `useUser` hook.

Eager refresh is much harder to configure when there are side effects involved. For example, at Clerk we have a `SignIn` object that is responsible for tracking the state of a sign-in flow while the user completes their first and second factor. Once the user successfully signs in, it has the side effect of generating a `Session` object, which changes the application's state from signed-out to signed-in.

We achieve eager refresh when a sign in completes by returning both the `SignIn` object and `Session` object from the server, but establishing a clean pattern for this has proven easier-said-than-done.

### Sequential refresh

The alternative to eager refresh is sequential refresh, which is less performant but can be easier to configure, especially when side effects are possible.

Using our example above, instead of trying to return both `SignIn` and the new `Session` at once, the final `SignIn` endpoint can instead return only the `SignIn` resource.

When the client sees that the `SignIn` is complete, though, it knows that a new `Session` must exist. So, it can trigger a second request to retrieve the new `Session`.

Two round-trips to the server will inherently be slower than one, but the development burden of eager refresh may make sequential refresh a viable alternative – especially as edge compute has made round-trips less costly over time.

---

# Next.js SSR authentication with Clerk
URL: https://clerk.com/blog/next-js-ssr-authentication-with-clerk.md
Date: 2022-05-07
Category: Engineering
Description: Next.js SSR authentication is easy with Clerk – just a few lines of code to get started.

## What is Server-Side Rendering (SSR) for React?

\*\*\
Update (11/2/2023):\*\* The code examples shown in this post have been deprecated. For the most up-to-date code examples on how to use Clerk with Next.js SSR, [read the documentation](/docs/references/nextjs/read-session-data#pages-router). Additionally we have a [new blog](/blog/nextjs-auth-clerk-streamlined-ssr-efficiency).

*Updated: 25/08/2022*

React was originally built to run on the client. Once React started, hooks would run to load data, and eventually a full page would be generated.

But since React is just a Javascript library, there was no reason it couldn't run on a server, too. **Server-side rendering** (SSR) runs React on the server to generate a page's initial HTML (a.k.a the first *render*), then runs React again on the client to provide reactivity.

Server-side rendering is particularly helpful for pages that must be indexed in search engines, since search engines cannot index pages that are rendered client-side.

The bigger benefit, though, is that server-side render can reduce the complexity of an application and lead to fewer lines of code. This is especially true in the modern era, where frameworks like Next.js provide helpers that drastically reduce the setup time for SSR.

### How to use SSR with Next.js?

Server-side rendering can be used by including `export async function getServerSideProps()` on any pages you need SSR. Below is an example of how a page would look when using SSR:

```jsx
function Page({ data }) {
  // use the data on our page
  ;<div>{data.content}</div>
}

export async function getServerSideProps() {
  // Fetch data from an API
  const res = await fetch(`https://api.example.com/data`)
  const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page
```

This example retrieves some data from api.example.com. Then, we pass the data to the UI using the return statement. Finally, in the UI, we present the data to the user.

*You can read more about Server-side rendering in the [Next.js SSR documentation](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props).*

Now that you have a basic understanding of how SSR works, let us investigate how Clerk and SSR can work together.

## How to use Clerk with SSR?

Clerk allows you to verify a user is authenticated and to retrieve user profile data when using SSR. Below, we will cover how to implement both.

### Securing pages with SSR

With Clerk and Next.js SSR, we can check if the user is authenticated and, if not, redirect them to the sign-in page without rendering anything on the client. Clerk provides a helper called `withServerSideAuth`, which allows you to access authentication information:

```jsx
import { withServerSideAuth } from '@clerk/nextjs/ssr'

export const getServerSideProps = withServerSideAuth(({ req, resolvedUrl }) => {
  const { sessionId } = req.auth

  if (!sessionId) {
    return { redirect: { destination: '/sign-in?redirect_url=' + resolvedUrl } }
  }

  return { props: {} }
})
```

The snippet above checks to see if there is a session. If not, it will redirect the user to the sign-in page. Once the user has successfully authenticated, Clerk will redirect the user back to the page they were initially trying to access. Here is a quick GIF showing the process in action.

![Next Js Ssr Authentication With Clerk guide illustration](./5db59e3e9f8e6c84c2ff95e84b0c21439ceeea8c-2560x1440.png)

*If you want to see it in action, check out this [fully functional example](https://replit.com/@perkinsjr/Clerk-SSR-Auth-Example) that contains all the code you need for SSR authentication with a redirect back.*

### Retrieving userId or JWT templates

Server-side rendering gives you the opportunity to retrieve data from 3rd party APIs and your own database. When using Clerk, you have the option of retrieving the user ID, or if you are using a Clerk integration such as Hasura you can also retrieve the JWT ready for use.

Below is an example of retrieving a Hasura JWT token ready to retrieve data before sending the page to the user.

```jsx
import { withServerSideAuth } from '@clerk/nextjs/ssr'

export const getServerSideProps = withServerSideAuth(async ({ req }) => {
  const { sessionId, getToken } = req.auth

  if (!sessionId) {
    return { redirect: { destination: '/sign-in?redirect_url=' + resolvedUrl } }
  }
  // use a token for your Clerk integrations
  const hasuraToken = await getToken({ template: 'hasura' })

  // retrieve data from your Hasura integration

  return { props: {} }
})
```

If you aren’t using a JWT template or a Clerk integration you can just retrieve the userId and use that against your own database.

```jsx
import { withServerSideAuth } from '@clerk/nextjs/ssr'

export const getServerSideProps = withServerSideAuth(async ({ req }) => {
  const { sessionId, userId } = req.auth

  if (!sessionId) {
    return { redirect: { destination: '/sign-in?redirect_url=' + resolvedUrl } }
  }

  // use the userId to retrieve your own data

  return { props: {} }
})
```

## Enabling Full SSR Mode

In some cases, you may need the full User object available to you. For example, if you need to retrieve the user’s primary email address

Clerk can use an additional network request to retrieve the full User object. To enable it, you will need to add `{ loadUser: true }` to your SSR request. Then, the complete User object will be available:

```jsx
import { withServerSideAuth } from '@clerk/nextjs/ssr'

export const getServerSideProps = withServerSideAuth(
  async ({ req, resolvedUrl }) => {
    const { sessionId, getToken, userId } = req.auth
    // retrieve the user object
    const { user } = req
    // return the users primary email address.
    const email = user.emailAddresses.find((email) => {
      return email.id === user.primaryEmailAddressId
    })

    // retrieve data using the email address.
    const data = getDataFromEmail(email)

    return { props: { data } }
  },
  { loadUser: true },
)
```

## **Ready to add auth to your app?**

Be sure to visit our [Next.js authentication with Clerk page](/nextjs-authentication) today to better understand how you can add authentication in minutes - not weeks.

Want to connect with other developers? Join our [Discord community](https://clerk.com/discord) or follow [@clerk on X](https://x.com/clerk) to stay up to date with the latest features, improvements, and sneak peeks to Clerk.

---

# How to skip CORS preflights and speed up your API with polyfills
URL: https://clerk.com/blog/skip-cors-options-preflight.md
Date: 2022-04-29
Category: Engineering
Description: CORS preflights add unnecessary latency to requests. Learn to use "simple" requests to skip the preflight entirely.

At Clerk, we have an API that is directly accessible from the frontend (we call it the Frontend API). It exclusively handles cross-origin requests, but none of those requests trigger a CORS preflight.

This is by design. CORS preflights do not add security for modern applications and they add an extra network round-trip, so we made sure that every API request is considered a "simple request."

## What do you mean CORS preflights do not add security?

It's a common misconception that CORS preflight requests add security to modern applications. Why else would they exist?

Surprisingly, CORS preflights exist to protect old applications, not new ones.

Specifically, the CORS designers were concerned about old applications that incorrectly assumed that browsers would never allow request methods besides GET or POST, or would never allow custom HTTP headers.

When browsers added the capability to send alternative request methods and custom headers via `fetch` (and its older sibling, `XMLHttpRequest`), suddenly applications that made this assumption were at risk.

To mitigate the risk to old applications, an extra "preflight" request was added to requests with PATCH, PUT, DELETE methods, and to requests with custom headers. The idea is that, if those applications fail to respond to the preflight in a very specific way, then the actual request will never be dispatched.

It's dirty and it adds latency, but it works.

The annoying part is: modern applications that anticipate PATCH, PUT, DELETE requests and custom headers don't gain any security from CORS preflights, it's just extra latency they need to incur to protect legacy applications. In 2022, it's like robbing Peter to pay an exceptionally stubborn Paul who won't update their decades old codebase, but we digress...

## How can CORS preflights be skipped?

Certain cross-origin requests are classified as "simple requests" and do not require a successful preflight before being dispatched.

Based on the section above, it might be easy to guess which requests qualify as simple: GET or POST requests without custom headers. *(Note: This is a slight simplification, the full details are [available on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).)*

To build an API that doesn't trigger preflights, we need to design polyfills for modern request methods and custom headers.

### One critical point first

The polyfills below assume you have configured your CORS middleware to outright reject requests that should not be processed.

As an example, consider CORS middleware running on `api.example.com` that is configured to allow the Origin of `https://www.example.com`.

Now, consider a request comes in with the Origin of `https://randomattacker.com`

Does your CORS middleware reject this request, or does it allow the request to be processed?

**It depends on your middleware.**

Some middleware might simply add an access-control header (below), then allow the request to continue:

```text
Access-Control-Allow-Origin: https://www.example.com
```

This header doesn't stop the request from being processed, but it does stop the browser from reading your server's response.

**This is not the behavior you want.**

Instead, you want your middleware compare the received Origin to the allowed Origin, and immediately cancel the request if they don't match.

**The only way to confirm your middleware's behavior is to write your own tests.** Clerk needed to write our own middleware to reject requests with undesirable CORS options (origin, credentials, etc).

### Polyfilling request methods

Polyfilling the request method is trivial - and we were fortunate to have inspiration [from Ruby on Rails](https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark). Every mutation request to our frontend API is dispatched as a POST, but the method can be overridden using a query string like `?_method=PATCH`. In our backend, we run middleware to ensure that the request is treated as a PATCH when this query string is present.

### Polyfilling custom headers

Custom headers can be more challenging to polyfill. It really depends on what type of content you're putting in the header:

- If the header *does not* contain sensitive information, the polyfill can use the same query string approach used above for request methods
- If the header *does* contain sensitive information, the polyfill should be handled within the the request body

Typically, developers want to customize two headers:

1. **Content-Type:** To set it to **application/json**. This is not sensitive and can be polyfilled using the query string.
2. **Authorization:** To include an access token. This is sensitive and should not be polyfilled using the query string.

It's important that sensitive information is not added to the query string because the request path is often logged to tools like bug trackers and analytics software. To obscure this information from those tools, it's better to add the field to the request body. In the case of the Authorization header, an extra form value or JSON attribute will suffice.

## That's everything!

These simple changes will eliminate CORS preflight requests from a frontend talking to a frontend API. In the process, it eliminates a round trip, which can easily take over 100ms if your user is geographically far from your server. Even in the best case of edge computing, this strategy will likely shave off \~20ms from your overall response time.

For the modern web, every millisecond counts!

---

# It's the little things: Three developer experience delights of our Remix authentication package
URL: https://clerk.com/blog/remix-delightful-developer-experiences.md
Date: 2022-04-23
Category: Engineering
Description: We integrate with a lot of React frameworks - here's what we love most about our Remix authentication package

Clerk is building authentication for the **modern web**. That means we don't just offer SDKs for different programming languages (Javascript, Ruby, Go), we offer SDKs for each modern React framework, too. We're proud to have first-class support for Next.js, Redwood, Gatsby, Expo, and – of course – Remix.

For every framework, we challenge ourselves to make Clerk's installation and usage as straightforward as possible. In this post, we highlight two developer experiences of [our Remix authentication package](/solutions/remix-authentication) that we absolutely love.

## 1. getAuth(request)

When handling a server-side request, there's only two theoretical ways developers can retrieve authentication data from Clerk.

First, with a wrapper function like `withAuth()`:

```javascript
const handler = withAuth(async (request) => {
  // withAuth adds the `auth` property to request
  const { auth } = request
})
```

Second, with a direct accessor like `getAuth()`:

```javascript
const handler = async (request) => {
  const auth = await getAuth(request)
}
```

Call us crazy, but we like `getAuth(request)` better. It feels more natural to access the data "just-in-time" instead of configuring it as a wrapper.

Fortunately, Remix enables us to provide this developer experience. After some upfront [configuration in root.tsx](/docs/get-started/remix#initialize-clerk-in-app-root-tsx), developers can use [`getAuth()` in the loaders and actions](/docs/get-started/remix#loaders-and-actions) of any route. It's really easy!

## 2. Splats ($.tsx)

We like to say splats are money.

Name a route $.tsx and it will serve all sub-routes. Clerk relies on this wildcard functionality when developers [mount our components directly in their application](/docs/get-started/remix#using-clerk-components).

Specifically, when a developer mounts `<SignIn/>` on the **/sign-in** route, we actually mount several sub-paths:

- **/sign-in** - to start the process
- **/sign-in/factor-one** - to verify the first factor
- **/sign-in/factor-two** - to verify the second factor
- **/sign-in/verify** - to handle [magic links](/blog/magic-links) clicked in an email

These subpaths are nice because it helps us handle the refresh button. If a user is entering the second factor and they click the refresh button, we don't want them to lose their progress. Storing some state in the URL bar helps us provide the best possible user experience.

We love splats because their fun, intuitive, and easy. Next.js calls the same concept "Optional Catch-all Routes" and has the syntax **/\[\[...slug]].tsx**, which we find a little cumbersome in comparison.

## 3. Edge-by-default

We'd be remiss if we didn't include that Remix runs on the edge by default.

In this case, it's really the absence of code we're showing off. There is no "adding" edge support, it all just works.

This is great because Clerk is optimized for the edge. We include [stateless authentication with JWTs](/features/session-management) in every plan, which are perfect for edge-based applications where georgraphy-bound network requests should be avoided. (Don't worry, our JWTs expire every minute so sessions are still revocable!)

The edge is the future, and we're thankful Remix's defaults will help speed up adoption and the web.

---

# Unicorn or Chameleon? Two strategies for exporting customizable React components
URL: https://clerk.com/blog/exporting-customizable-react-components.md
Date: 2022-04-15
Category: Engineering
Description: React Components are the future of APIs – but how can developer tools companies enable robust customization? We explore two strategies.

Clerk's React library exports [`<SignUp/>`](/components/sign-up), [`<SignIn/>`](/components/sign-in), and [`<UserProfile/>`](/components/user-profile) components. They come styled and fully-featured so developers can focus on building their application:

![Exporting Customizable React Components guide illustration](./3faabd62184abfaa71b1a810d56f82596e3d93ac-2000x1016.png)

Unsurprisingly, this leads to the question: *How can I customize the components to match my brand?*

Whitelabeling software is a famously hard and unsolved problem - it's extremely common to find widgets or portions of websites that have completely different styling.

One example is this chat widget from Alaska Airlines, which shows different form field styling (rounded vs square), different buttons (huge text, not capitalized), and a different font (Arial vs Circular).

![Exporting Customizable React Components guide illustration](./63848098c405f0be95f8a961520e83c4be036f83-2880x1596.png)

This quarter at Clerk, we're revisiting our customization strategy. We want to truly solve this problem with perfect matching styles instead of just "close enough" styles. Internally, we say we're switching from a "unicorn" strategy to a "chameleon" strategy.

While we haven't finalized the chameleon strategy yet, we do have a proof of concept running and are excited about the developer experience it produces.

## Unicorn strategy

![Exporting Customizable React Components guide illustration](./3b751ff2967063abbbfe2320254217991a362204-656x350.png)

Our initial approach to theming is a "unicorn" strategy because we came up with the system ourselves – developers have to learn our specific way of applying styles.

We drew inspiration from others, so you've probably seen something like it before. Developers simply pass a `theme` prop to `<ClerkProvider>` to customize aspects of the design:

```json
{
  "general": {
    "color": "#f1f1f1",
    "backgroundColor": "#f2f2f2",
    "fontColor": "#f3f3f3",
    "fontFamily": "Inter, sans serif",
    "labelFontWeight": "500",
    "padding": "1em",
    "borderRadius": "20px",
    "boxShadow": "0 2px 8px rgba(0, 0, 0, 0.2)"
  },
  "buttons": {
    "fontColor": "#f4f4f4",
    "fontFamily": "Inter, sans serif",
    "fontWeight": "300"
  }
}
```

While this system is great for quickly getting close styles, it suffers in the last mile. There simply aren't enough options to provide developers with the complete customization capabilities they desire.

## Chameleon strategy

![Exporting Customizable React Components guide illustration](./bde3d2490d7628fb2febd01d27101894bfae501c-1000x680.png)

Our next iteration approaches customization with a new mindset. Instead of asking developers to learn our strategy, we will integrate with any strategy they already use.

If you've been around the frontend ecosystem for a while, you know there are *several, very popular* styling systems that all work *completely differently.* Because they're so diverse and we want to blend in with all of them, we call this a "chameleon" strategy.

Let's work through an example to explain how it works.

Consider that one of our components has a "primary button." By default, that button renders to this HTML:

```html
<button class="clerk-button-primary">Action</button>
```

*Note: in this post, we're only focused on styles. We'll discuss customizing the "Action" string in the future.*

To change the style of this button, developers will still pass a `theme` prop, but now the selector will be for this specific element:

```jsx
<SignIn
  theme={{
    primaryButton: customButton,
  }}
/>
```

In this snippet, `customButton` can have one of three values:

1. A string with one or many class names. If passed, the value will replace the default `clerk-button-primary` class.
2. A React component that renders a `<button>` and forwards all props (including children). If passed, the default element will not be rendered at all, and instead the passed component will be rendered.
3. A dictionary that adheres to the `CSSStyleInterface` type. This is for completeness more than anything else. If passed, the value will be forwarded to the `style` prop, and the the default `clerk-button-primary` class will be omitted.

Now, let's see how it works for different styling libraries.

### Tailwind

Simply pass in Tailwind classes as a string:

```jsx
<SignIn
  theme={{
    primaryButton: 'p-4 rounded',
  }}
/>
```

### CSS modules

When a CSS module is imported, the object automatically returns class names. Simply pass it in:

```jsx
import styles from './Styles.css'
;<SignIn
  theme={{
    primaryButton: styles.customButton,
  }}
/>
```

### styled-components

styled-components works by returning a React component that automatically forwards props to a `<button>` element – exactly as specified by Clerk:

```jsx {{ prettier: false }}
const CustomButton = styled.button`
  border-radius: 1rem;
  padding: 1rem;
`

<SignIn
  theme={{
    primaryButton: CustomButton
  }}
/>
```

### Chakra

Chakra also provides React components, but they are modified with props, which makes the setup slightly more complex, but still quite simple:

```jsx
<SignIn
  theme={{
    primaryButton: (props) => <Button size="lg" {...props} />,
  }}
/>
```

Since the chameleon strategy ultimately hooks into HTML and React primitives, we're confident that we can make *every* styling library work with this strategy, not just the four we've listed above.

Thoughts, comments, questions? We're eager for your feedback! Please reach out to [@clerk on X](https://x.com/clerk) or [contact support](https://clerk.com/contact/support).

---

# Introducing Web3 Authentication
URL: https://clerk.com/blog/introducing-web3-authentication.md
Date: 2022-01-21
Category: Engineering
Description: Clerk is saving Web3 developers from the greatest evils of the Web2 platform: cookies, multifactor authentication, and profile enrichment

Clerk is launching our first Web3 authentication factor - Sign in with Metamask!

![Introducing Web3 Authentication guide illustration](./adca3d5c16282cbf8af17d8664bd4bb82d2ece4a-1040x452.png)

This launch is the result of dozens of developer interviews, focused on understanding if and how Clerk can contribute to the Web3 ecosystem.

It didn't take long before we discovered three common challenges that we can help Web3 developers with immediately: securing sessions, multifactor authentication, and profile enrichment.

## Securing Sessions

First and foremost - we found a near ubiquitous challenge around securing sessions. It's a two-part problem:

1. How do you verify a user is owns of a Web3 account?
2. How do you safely create a session to persist the users account information?

### Verifying the owner of a Web3 account

It's easy to *insecurely* determine the Web3 account address of a user visiting your website with Metamask connected, just run the following Javascript:

```javascript
await ethereum.request({ method: 'eth_accounts' })
```

Verifying that the returned address is accurate, however, is significantly more challenging. Developers must ask users to sign a transaction, which is a process that undergone several protocol revisions and is hard to keep up with.

This is where Clerk steps in - all the work of signing a transaction and verifying the signature is abstracted away, and wrapped in a `<SignInWithMetamask/>` button.

### Persisting a session

Let's be honest - persisting a session isn't a Web3 challenge at all, but it is one that Web3 developers must endure by virtue of operating in Web2 browsers.

We know Web3 developers don't want to think about things like httpOnly cookies, XSS attacks, and session revocation. Web2 developers don't either, and that's why built-in [session management](/features/session-management) is the foundation of every authentication product at Clerk. We use stateless JWTs that enable authentication in under 1 millisecond, and revocation in under 1 minute. We follow all the best practices so you can focus on building your application.

## Multifactor authentication

Sign in with Metamask uses the same abstraction as our other authentication factors like passwords or [magic links](/blog/magic-links).

Because of this, Clerk's [multifactor authentication](/features/multifactor-authentication) works with Metamask users out-of-the-box. Users simply need to navigate to their user profile and opt-in to multifactor authentication. There's no extra work for the developer beyond mounting a [`<UserProfile/>`](/docs/components/user/user-profile) component, or redirecting to the Clerk-hosted component.

## Profile enrichment

Off-chain profile enrichment is a topic of much debate in the Web3 community. Ideally, data like email addresses and phone numbers could be retrieved on-chain following the principles of [self sovereign identity](https://en.wikipedia.org/wiki/Self-sovereign_identity).

But as of today, there are no standard protocols for retrieving this data. As a result, top Web3 companies like OpenSea still collect and verify email addresses off-chain.

Clerk enables developers to easily do the same. We provide simple helpers for collecting and verifying phone numbers, email addresses, and even OAuth accounts. Beyond that, we accept structured data likes names and profile photos, as well as provide a generic metadata field for bespoke profile enrichment.

In the future, we look forward to enriching profiles in a more decentralized manner. Today, we hope this mechanism can help bridge the gap from Web2 to Web3.

## Try it out!

[Try out our guide for installing Web3 authentication.](/docs/users/web3-wallets) The guide will get you started with a new Next.js application with Metamask authentication.

## Future roadmap

This launch is the first of many upcoming Web3 launches. Our rough roadmap ahead includes:

- **Sign in with Ethereum** - we know that Metamask alone is a little limiting and will add generic Ethereum support soon
- **Authorization via Token Gating** - In the Web2 realm, our team is actively building authorization via Roles and Permissions. We see many corollaries to Web3 authorization via token gating are approaching the primitives in a way that will support both.
- **Multi-chain support** - Because we fully anticipate a multi-chain future

Need something we didn't mention? Please reach out through any of our [support channels](https://clerk.com/contact/support) - we're still defining this roadmap and very interested to hear how we can better meet your needs.

---

# Generating sortable Stripe-like IDs with Segment's KSUIDs
URL: https://clerk.com/blog/generating-sortable-stripe-like-ids-with-segment-ksuids.md
Date: 2021-08-06
Category: Engineering
Description: Learn how Clerk generates resource IDs with inspiration from Stripe and Segment.

On the shoulders of giants, as they say...

Early in Stripe's lifetime, they launched a new style of resource ID's to a great deal of fanfare. The ID's are prefixed with an abbreviation of the object they represented. For example:

- `ch_` represents a Charge object
- `cus_` represents a Customer object

Patrick Collison offered the motivation for these prefixes on [Quora](https://www.quora.com/How-does-Stripe-generate-object-ids):

![Generating Sortable Stripe Like Ids With Segment Ksuids guide illustration](./a47bef704ff445167a03ad95e862e34ef8f7ca52-1222x440.png)

At Clerk, we're big fans of the prefix and knew early on that we wanted to offer their convenience to our own customers.

That was the easy part of generating our IDs— we spent a lot more time deciding what to put *after* the prefix.

For the uninitiated, ID generation is a surprisingly wide subject area with many important considerations.

For a long time, it was commonplace for developers to rely on sequential IDs generated by the database. There are artifacts of this practice all over the web - but these days, they tend to be avoided because:

1. They can easily be guessed. This isn't inherently a problem, but many, many security vulnerabilities are made worse when attackers can guess resource IDs. On a more light-hearted note, a "bug" involving guessable IDs [led to the creation Reddit self-posts](https://news.ycombinator.com/item?id=20453120).
2. They can reveal a lot of information about application usage. Again, this isn't inherently a problem, but it can lead to unintended side effects. One example came recently when StackOverflow sold to Prosus, and there was [discussion on HackerNews about the low user IDs](https://news.ycombinator.com/item?id=27370507) some users had, indicating they were an early user.
3. They're impossible to generate in distributed systems. For modern applications, this should be the primary concern. If a system has servers in two different regions, it's impossible for them to generate sequential IDs independently, without coordination - but coordination would likely undermine the point of distributing the system in the first place.

To mitigate these issues, randomness has been introduced into ID generation. Enough randomness ensures that IDs cannot be guessed and that collisions are avoided in distributed systems.

But purely random IDs - like [UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier) - also eliminate a trait of sequential IDs that developers love: they're sortable.

Finding a middle ground between sortable and unique IDs was a primary motivation behind two newer ID generators:

- Twitter's [Snowflake](https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake)

  > To generate the roughly-sorted 64 bit ids in an uncoordinated manner, we settled on a composition of: timestamp, worker number and sequence number.

- Segment's [KSUID](https://segment.com/blog/a-brief-history-of-the-uuid)

  > It borrows core ideas from the ubiquitous UUID standard, adding time-based ordering and more friendly representation formats.

## Clerk's ID generator

After considering both Snowflake and KSUID, we decided to use KSUID for our primary ID generator. The key factor in our decision was that Twitter's Snowflake included a worker number, which we did not need.

Combined with Stripe-like prefixes, our IDs look like this:

![Generating Sortable Stripe Like Ids With Segment Ksuids guide illustration](./44bfae985e59215dc8839f8e96c82093ec5d6825-1344x239.svg)

---

# How we use End To End tests to bulletproof our authentication flows across browsers
URL: https://clerk.com/blog/how-we-use-end-to-end-tests-to-bulletproof-our-authentication-flows-across-browsers.md
Date: 2021-06-11
Category: Engineering
Description: Due to the evolution of tooling and the shift towards high-velocity product development, End To End testing is a regular topic of discussion for small teams.

At Clerk, we use End To End testing to create bulletproof authentication flows across browsers.

## The 10,000 ft view of the End To End landscape

In the past, End To End testing was almost exclusively considered a Quality Assurance Engineering topic. For one reason or another, the development of test automation pipelines and application workflow validation tests were not so popular with software engineers. That view has gradually become dated and replaced with the growing popularity of tools like Cypress, Puppeteer, Playwright, and even the latest version of Selenium.

![How We Use End To End Tests To Bulletproof Our Authentication Flows Across Browsers guide illustration](./490c57865ef4011f0134f142c2fee8d3057ad7fd-2000x1404.png)

The growth of those tools should not be seen as a simple technical advancement, but instead as an immediate answer to the growing need to efficiently and effectively validate fast moving development cycles of agile teams. At Clerk we deploy multiple times per day, and without these tools it would be impossible to ensure reliability.

## The high bar for reliability at Clerk

As a solution for authentication and user management, Clerk must maintain exceptionally high reliability, even as our product and team expands rapidly around our core offering. It is critical that end-users can always access their accounts quickly, even through edge cases like originally signing up with Google, then trying to sign in with email instead.We also take great pride in our developer experience, and often jump through hoops to ensure that Clerk works consistently across development, staging, and production environments and across all browsers.To keep reliability high across our myriad of flows - and importantly, to give our own development team confidence that our product won't break with new deploys - it became clear the most pragmatic solution was to develop an End To End test suite.

## Laying out our needs

These were our initial needs exactly as voiced by our team:

- **Cross-browser**: The suite needs to cover all major browsers with as little external service interaction as possible.
- **Happy to code in**: Meaning TypeScript. Most probably Frontend Engineers should be able to write and maintain the tests for the suite (*with as much joy as possible*).
- **Support containerized deployment**: The suite needs to be portable enough to run on a GitHub Action workflow.

## Choosing a platform

After investigating Selenium, Puppeteer, WebDriverIO, Cypress, and Playwright and weighing each platform against our needs, we decided to go with Playwright.

Although it felt less hyped, as a team we felt really confident in the architecture behind Playwright, its stellar documentation and tooling, and the excellent community backing the effort.

We will describe our exact selection process in a later post, but most critically, we appreciate the ability to execute our workflows across Chromium, WebKit and Firefox with so much ease and fine grained control.

![How We Use End To End Tests To Bulletproof Our Authentication Flows Across Browsers guide illustration](./8a588e6ff8136f408783a02c95ef6cbd64c402ef-1048x433.png)

## How the suite is orchestrated

Our End to End test suite is a separate system from our main codebase as we have seen this pattern working really well for most applications.

Its main components as a Node.js application written in TypeScript are:- **Playwright** as the browser automation framework.

- **Jest** as the test suite runner.
- [jest-playwright](https://github.com/playwright-community/jest-playwright) to connect Jest and Playwright as it makes our lives so much easier.
- [Faker.js](https://github.com/faker-js/faker) to create API fixtures that fit our needs for the sign-up and sign-in processes fixtures.
- [Page Objects](https://playwright.dev/docs/pom) as the main pattern representing the interaction facade with our application views in code.

These components have proved to work together seamlessly while staying welcoming to our frontend engineering team. One of our main goals was to ensure that new teammates could understand the system quickly and create new tests, and so far this structure has exceeded our expectations.

## Delivery of the suite on our day to day efforts

To keep us safe from accidental regressions, the test suite must run (and pass!) before any merge to production. To automate this process, integration with our Continuous Integration (CI) tooling was essential.

We run our CI through GitHub Actions, and fortunately, the Playwright team has created GitHub Action tools to simplify triggering the test suite. Paired with Vercel preview deployments, which is where most of our CI tasks take place, both Actions fit the spot quite nicely for End to End suite scenarios. The Playwright team has also created a GitHub action to quickly bootstrap Playwright tests.

The final action file that triggers our End to End suite on every pull request looks something like this:

```bat
jobs:
  e2e:
    if: github.event.deployment_status.state == 'success'
    name: End to End
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [14.x]
    steps:
      - uses: actions/checkout@v2
      - uses: microsoft/playwright-github-action@74fbf9d1a7c5d8735dab59804da3fdd367a98020
      - uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: Run Playwright tests
        run: cd e2e && npm install && npm run test
```

If the action succeeds, we are good to go!

## Parting words

This was a really brief overview of how we went about designing our End to End test suite at Clerk. As Clerk and our customers' needs continue to evolve, we will continue to share our experiences with Playwright and any new tools we adopt.

***P. S.** We have open sourced a template for starting up your own End To End suite using Playwright, so feel free to try it out! [https://github.com/clerkinc/playwright-e2e-template](https://github.com/clerkinc/playwright-e2e-template)*

---

# How HttpOnly cookies help mitigate XSS attacks
URL: https://clerk.com/blog/how-httponly-cookies-help-mitigate-xss-attacks.md
Date: 2021-05-06
Category: Engineering
Description: HttpOnly cookies do not prevent cross-site scripting (XSS) attacks, but they do lessen the impact and prevent the need to sign out users after the XSS is patched. HttpOnly cookies are not a substitute for XSS prevention measures.

**In short: HttpOnly cookies do not prevent [cross-site scripting (XSS)](https://owasp.org/www-community/attacks/xss) attacks, but they do lessen the impact and prevent the need to sign out users after the XSS is patched. HttpOnly cookies are not a substitute for XSS prevention measures.**

Our very first architecture decision at Clerk was to use HttpOnly cookies for session management. It has long been understood that HttpOnly cookies help mitigate cross-site scripting (XSS) attacks, and we felt it was important to include this best-practice directly in our product.

But while there's strong consensus that using HttpOnly cookies is a best practice, we've found many developers are unsure of how they help with XSS. We think this stems from the guidance, which often just says what to do rather than explaining why:

> A cookie with the HttpOnly attribute is inaccessible to the JavaScript[ Document.cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) API; it is sent only to the server. For example, cookies that persist server-side sessions don't need to be available to JavaScript, and should have the HttpOnly attribute. This precaution helps mitigate cross-site scripting ([XSS](https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks#cross-site_scripting_\(xss\))) attacks.\
> [Mozilla MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)

> Cookies \[...] SHOULD be tagged to be inaccessible via JavaScript (HttpOnly).\
> [NIST 800-63B](https://pages.nist.gov/800-63-3/sp800-63b.html#711-browser-cookies)

## The attack vector

After reading this guidance, you might be surprised to learn that HttpOnly cookies do not prevent XSS attacks.

Instead, HttpOnly cookies are helpful when you assume an XSS attack will happen and want to **lessen the impact**. Ultimately, they mitigate XSS attacks by making it easier for organizations to respond.

The specific threat HttpOnly cookies protect against is called **session token exfiltration**, which is a fancy way of saying that the attacker is able to steal a user's session token.

When a session token is stored in a cookie *without* the HttpOnly flag, the token can be stolen during an XSS attack with [document.cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie). This is problematic because session tokens are the primary mechanism used by backends to authenticate a user.

**Once an attacker has access to a session token, they can often act on behalf of a user until that token expires or is revoked.** Actions can be taken remotely - even if the user is no longer visiting the page with the XSS vulnerability - which can serve to dramatically increase the surface area of the attack.

Conversely, when a session token is stored in a cookie *with* the HttpOnly flag, that token cannot be directly exfiltrated during an XSS attack. This minimizes the surface area of the XSS attack and makes it easier for organizations to respond.

## Responding to XSS attacks - without HttpOnly cookies

When an organization is responding to an XSS attack, the first step is always patching the XSS vulnerability.

If HttpOnly cookies were not used, organizations should then assume that session tokens were exfiltrated. This means that - even with the XSS vulnerability patched - the attacker may still have the ability to act on behalf of users.

The next step is revoking the session of any user who may have been subjected to the XSS vulnerability, since those are the users who may have had their session tokens exfiltrated. These users will need to sign in again next time they visit the website.

Lastly, the organization will need to reverse any actions the attacker took on behalf of their users, from the time the vulnerability began to the time session tokens were revoked.

## Responding to XSS attacks - with HttpOnly cookies

With HttpOnly cookies, organizations still need to patch the XSS vulnerability and reverse any actions taken on behalf of their users, but they do not need to revoke sessions and ask users to sign in again.

## What about localStorage and sessionStorage?

Although [window.localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and [window.sessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) are newer client-side storage APIs, they function like cookies *without* the HttpOnly flag. HttpOnly cookies are still the only standard mechanism for persisting session tokens that cannot be exfiltrated during an XSS attack.