Skip to main content
Docs

How Clerk implements OAuth

Clerk supports secure, standards-compliant OAuth flows for your applications. You can create OAuth apps, enable dynamic client registration, manage consent screens, and configure public clients. This guide covers all key OAuth features and configurations in Clerk and helps you set up the right OAuth flow for your needs.

Build an OAuth app with Clerk

To create or edit an OAuth app, navigate to the OAuth applications page in the Clerk Dashboard. The following sections explain the available settings for configuring your OAuth app.

Dynamic client registration

In addition to configuring OAuth apps manually through the Clerk Dashboard, Clerk supports dynamic client registration, allowing OAuth clients to be created programmatically via a public API endpoint. To enable this feature, navigate to the OAuth applications page in the Clerk Dashboard and enable Dynamic client registration.

Caution

Dynamic client registration creates a public, unauthenticated endpoint that anyone can use to register OAuth clients with your authorization service. While this can enable use cases like multi-tenant SaaS platforms, developer marketplaces, and some MCP integrations, it also introduces significant security risks:

  • Attackers can create clients anonymously without audit trails.
  • Malicious clients can use legitimate-sounding names to trick users.
  • Administrative overhead increases substantially for monitoring and cleanup.

In some use cases, dynamic client registration can be a requirement. However, it should only be enabled after carefully evaluating and accepting the associated security risks.

Additionally, when dynamic client registration is enabled, the OAuth consent screen is automatically enforced and cannot be disabled - this prevents dangerous combinations that could lead to CSRF vulnerabilities.

Scopes

Scopes define the level of access and specific user data that will be shared with the client app during authentication. The following scopes are currently available:

ScopeAccess
profileGrant access to the user's personal information, such as first and last name, avatar, and username
emailGrant access to the user's email address
public_metadataGrant access to the user's public and unsafe metadata
private_metadataGrant access to the user's private metadata
openidEnables the OpenID Connect flow

Note

Support for adding custom OAuth scopes is not yet available, but development is underway. The goal is to allow custom scopes to be added, accepted, and checked through Clerk SDKs. An update will be provided when this feature is available.

For early access to custom OAuth scopes, please vote or provide feedback on the roadmap here.

Public clients and PKCE

In OAuth flows where a private server environment is available, the server can store and use a client_secret to securely exchange an authorization code for an access token. However, in mobile apps or single-page apps (SPAs), there is often no private server-side environment available to store the client_secret and run this exchange. These are called public clients because they cannot securely store secrets. 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 anyone who opens the developer tools, as well as browser extensions, etc.

To support these use cases, Clerk's OAuth implementation allows public clients to exchange an authorization code without requiring a client_secret. This makes it easier for mobile and browser-based apps to complete the token exchange and retrieve access and refresh tokens, without the risk of exposing long-lived secrets.

However, this process means public clients often need an additional layer of security to protect against interception attacks. This is where PKCE (Proof Key for Code Exchange) comes in - PKCE helps secure public clients by replacing 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, and later, during the token exchange, sends the original code verifier to prove it initiated the flow. The authorization service can then hash the code verifier to ensure it matches the code challenge.

This ensures that even if an attacker intercepts the authorization code, they can't complete the token exchange without the original code verifier. Only the client that started the flow has that value.

Quiz

Couldn't an attacker just steal the code_verifier from the browser (like from sessionStorage)?

To enable PKCE for a public client, you can enable the Public option for each OAuth app in the Clerk Dashboard.

The OAuth consent screen shows users a confirmation dialog before they authorize OAuth applications. It ensures users understand exactly what permissions they're granting before completing the OAuth flow.

The consent screen displays:

  • The requesting app's name and logo.
  • The name and logo of the app receiving the request.
  • The specific access scopes that are being requested, in user-friendly language.
  • Clear accept/deny options.
OAuth Consent Screen

The consent screen is enabled by default for all OAuth apps. You can enable or disable the consent screen for each OAuth app in the Clerk Dashboard.

Important

Enabling the consent screen for all OAuth apps is strongly recommended. Without a consent screen, any logged-in user who visits an OAuth authorization URL automatically grants access to any requested scopes. The consent screen acts as a critical security checkpoint, preventing malicious apps from silently gaining access to user accounts.

Token expiration and management

  • OAuth access tokens expire after 1 day.
  • Refresh tokens never expire.
  • Authorization codes expire after 10 minutes.
  • OIDC id_tokens expire after 1 day.

Authorization server metadata

An important feature of modern OAuth 2.0 and OpenID Connect (OIDC) implementations is authorization server metadata. This is a standardized JSON document published by the authorization server that describes its configuration, supported features and endpoints. This metadata makes it easier for clients to automatically discover important endpoints and features without needing manual configuration.

You can easily retrieve this metadata by loading your Clerk Frontend API URL with /.well-known/oauth-authorization-server appended to it. For example:

  • https://verb-noun-00.clerk.accounts.dev/.well-known/oauth-authorization-server for a development environment.
  • https://clerk.<INSERT_YOUR_APP_DOMAIN>.com/.well-known/oauth-authorization-server for a production environment.

Your Frontend API URL can be found on the API keys page in the Clerk Dashboard.

This endpoint exposes all relevant details in a standardized JSON format. A sample metadata document might look like this:

{
  "issuer": "https://well-hagfish-71.clerk.accounts.dev",
  "authorization_endpoint": "https://well-hagfish-71.clerk.accounts.dev/oauth/authorize",
  "token_endpoint": "https://well-hagfish-71.clerk.accounts.dev/oauth/token",
  "jwks_uri": "https://well-hagfish-71.clerk.accounts.dev/.well-known/jwks.json",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["client_secret_basic", "none"],
  "scopes_supported": ["openid", "profile", "email", "public_metadata", "private_metadata"],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "claims_supported": ["sub", "iss", "aud", "exp", "iat", "email", "name"],
  "service_documentation": "https://clerk.com/docs",
  "ui_locales_supported": ["en"],
  "op_tos_uri": "https://clerk.com/legal/standard-terms",
  "code_challenge_methods_supported": ["S256"]
}

Building a Model Context Protocol service

The Model Context Protocol (MCP) is an open standard that lets large language model (LLM) apps, like Claude, ChatGPT, or Cursor, securely access user data from external services with the user's permission. Instead of asking users to sign in separately, MCP allows these AI apps to request permission to access specific data, such as emails or private Github repositories, directly through the app being used.

There are two parties involved in MCP - "client" and "server". In web development, the terms "client" and "server" often refer to the frontend (browser) and backend (web server). However, in this context, these terms have different meanings:

  • The "client" is the LLM application that wants to access another service on a user's behalf. For example, Claude would be the "client" if it wants to get access to Gmail.
  • The "server" is the system that hosts the protected resources the client wants to access. In this example, this would be Gmail. This is sometimes referred to as the "resource server" or "MCP server".

MCP servers often need to access user data from various sources on behalf of AI apps. This requires robust OAuth flows with proper consent management, token verification, and security controls, which Clerk supports. The combination of dynamic client registration (for registering MCP servers programmatically), the consent screen (for secure user authorization), and comprehensive SDK support makes Clerk an ideal authorization server for MCP implementations.

For step-by-step guides on working with MCP in Next.js using Clerk (more framework-specific guides coming soon):

Feedback

What did you think of this content?

Last updated on