# How do I implement social login for my web app?

Implement [social login](https://clerk.com/glossary.md#social-login) by integrating [OAuth 2.0](https://clerk.com/glossary.md#oauth) and OpenID Connect (OIDC) through an [identity provider](https://clerk.com/glossary.md#identity-provider-sso-idp-sso) like Google, GitHub, or Apple. Rather than building the OAuth flow, token storage, and account linking from scratch, use an auth service such as Clerk, Auth0, or Firebase Auth — Clerk provides pre-built components that add social login to React, Next.js, and Expo apps with minimal configuration, including automatic account linking and secure token management.

This guide covers the full implementation path: from understanding the protocols to shipping production-ready social login across multiple frameworks.

> This article was updated March 11, 2026. The updates and changes reflect the major [Core 3](https://clerk.com/changelog/2026-03-03-core-3.md) release from March 3, 2026 and Clerk's [new pricing](https://clerk.com/changelog/2026-02-05-new-plans-more-value.md) launched February 5, 2026

## What Is Social Login? OAuth 2.0 and OIDC Fundamentals

Social login is an authentication pattern where users prove their identity through a trusted third-party identity provider (IdP) like Google, GitHub, or Apple. Instead of managing passwords directly, your application delegates the authentication step to the provider and receives verified identity information in return.

### Why Social Login Matters

Creating new accounts is one of the highest-friction points in user onboarding. A [2012 Janrain/Blue Research study](https://www.mediapost.com/publications/article/165832/social-login-preferred-to-site-registration.html) (616 respondents) found that 86% of users reported being bothered by having to create new accounts on websites. This friction persists, as research from the [Baymard Institute](https://baymard.com/lists/cart-abandonment-rate) found 19% of users abandon purchases specifically because a site required them to create an account, making forced account creation one of the top reasons for checkout abandonment behind unexpected costs (39%) and slow delivery (21%). Social login eliminates this friction, as users tap a button, confirm consent, and they're in.

Beyond convenience, social login gives developers access to verified identity data (email, name, profile photo) without building email verification flows from scratch, and shifts the password security burden to providers with dedicated security teams.

### OAuth 2.0: The Authorization Framework

OAuth 2.0 ([RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749)) is the authorization framework that powers social login. For web and mobile applications, the recommended implementation is the **[Authorization Code flow](https://clerk.com/glossary.md#authorization-code-flow) with [PKCE](https://clerk.com/glossary.md#code-exchange-pkce)**. It works like this:

1. Your app redirects the user to the provider's authorization endpoint.
2. The user authenticates and grants consent,
3. The provider redirects back to your app with a short-lived **authorization code**.
4. Your backend exchanges that code (plus your [client secret](https://clerk.com/glossary.md#client-secret)) for tokens.
5. The authorization code is exchanged for tokens, using PKCE. For server-rendered web apps, your backend performs the exchange and authenticates the client (typically with a client secret). For mobile apps and SPAs (public clients), the app performs the PKCE exchange without a client secret.

OAuth 2.0 is fundamentally about [**authorization**](https://clerk.com/glossary.md#authorization), granting your app permission to access the user's data at the provider. It does not, by itself, tell you _who_ the user is.

### OIDC: Adding Authentication on Top

([OpenID Connect (OIDC)](https://clerk.com/glossary/openid-connect.md)) is a thin identity layer built on top of OAuth 2.0. It adds a standardized **ID token**, a signed [JWT](https://clerk.com/glossary.md#json-web-token) containing identity claims, so your application can verify who the user is, not just what they've authorized.

Key OIDC claims returned in the ID token:

| Claim            | Description                                   |
| ---------------- | --------------------------------------------- |
| `sub`            | Unique, stable user identifier                |
| `email`          | User's email address                          |
| `email_verified` | Whether the provider has confirmed this email |
| `name`           | Full display name                             |
| `picture`        | Profile photo URL                             |

The standard OIDC scopes that control which claims are returned:

| Scope     | Returns                                                          |
| --------- | ---------------------------------------------------------------- |
| `openid`  | Required for OIDC; returns `sub`                                 |
| `email`   | Returns `email` and `email_verified`                             |
| `profile` | Returns `name`, `picture`, `given_name`, `family_name`, `locale` |

**The key distinction:** OAuth 2.0 answers "what can this app access?" while OIDC answers "who is this user?" For social login, you need both — OAuth 2.0 for the flow mechanics, and OIDC for the identity data.

The upcoming OAuth 2.1 draft specification consolidates current best practices by making [PKCE](#pkce-proof-key-for-code-exchange) mandatory for all clients and formally removing the implicit flow. For a deeper dive into the protocols, see the [OAuth 2.0 specification](https://datatracker.ietf.org/doc/html/rfc6749) and the [OpenID Connect Core spec](https://openid.net/specs/openid-connect-core-1_0.html).

## Choosing the Right Identity Providers

Not all social providers are equal. They differ in setup complexity, the data they return, their protocol compliance, and the audiences they serve. The table below covers 15 common providers, but is not an exhaustive list. Clerk supports [28+ social providers](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview.md) with custom OIDC support for any provider not listed. Other auth services vary in coverage; see the [auth service support matrix](#provider-support-by-auth-service) below.

### Provider Comparison

| Provider    | Scopes for Login                        | Setup Complexity                             | OIDC    | Unique Considerations                                                                                                                                                 |
| ----------- | --------------------------------------- | -------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Google      | `openid email profile`                  | Low (free, Google Cloud Console)             | Yes     | FedCM mandatory for GIS since Aug 2025, One Tap available, 100-user testing limit                                                                                     |
| GitHub      | `user:email`                            | Low (free, Developer Settings)               | No      | No ID token, single callback URL per OAuth app, classic tokens don't expire                                                                                           |
| Apple       | `name email`                            | High ($99/yr, ES256 client secret JWT)       | Partial | Name only on first auth, Hide My Email relay, `response_mode=form_post` required                                                                                      |
| Microsoft   | `openid profile email`                  | Low (free, Microsoft Entra admin center)     | Yes     | Multi-tenant config required, [add `xms_edov` optional claim](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference) for verified email |
| Facebook    | `public_profile`, `email`               | Low (free, Meta Developer Portal)            | Partial | `public_profile` auto-granted, App Review required for advanced scopes only                                                                                           |
| Discord     | `identify`, `email`                     | Low (free, Discord Developer Portal)         | Partial | No full OIDC discovery endpoint, popular for gaming/community apps                                                                                                    |
| LinkedIn    | `openid profile email`                  | Low (free, LinkedIn Developer Portal)        | Yes     | Migrated to standard OIDC in 2023, pairwise subject identifiers                                                                                                       |
| GitLab      | `openid profile email`                  | Low (free, works with self-hosted instances) | Yes     | Group/role claims available, supports self-hosted GitLab                                                                                                              |
| Slack       | `openid email profile`                  | Low (free, Slack API portal)                 | Yes     | "Sign in with Slack" (OIDC) is separate from "Add to Slack" (bot install)                                                                                             |
| Twitch      | `openid`, `user:read:email`             | Low (free, Twitch Developer Console)         | Yes     | Email requires explicit `claims` parameter in auth request                                                                                                            |
| X (Twitter) | `users.read`, `users.email`             | Low (free tier supports OAuth login)         | No      | Email available via `users.email` scope (since April 2025), PKCE mandatory, "Request email from users" must be enabled in Developer dashboard                         |
| Coinbase    | `wallet:user:read`, `wallet:user:email` | Limited (new apps require partner approval)  | No      | Web3/crypto niche, scopes use comma separation                                                                                                                        |
| Linear      | `read`                                  | Low (free, Linear settings)                  | No      | GraphQL-only API for user data, project management niche                                                                                                              |
| Notion      | Capability-based                        | Low (free, Notion integrations portal)       | No      | Page-level access consent, not traditional scope-based auth                                                                                                           |
| Vercel      | `openid email profile`                  | Low (free, Vercel dashboard)                 | Yes     | Developer-focused, relatively new feature                                                                                                                             |

### Google

Google is the recommended starting provider. According to the [Okta/Auth0 "Going Deep with Social Login" report](https://www.okta.com/sites/default/files/2023-06/GoingDeepwithSocialLogin-Whitepaper-20230601-Final.pdf) (June 2023), Google accounts for approximately 75% of social logins across their platform. In Google's published case studies, individual implementations of Google One Tap saw significant signup improvements:

- [Reddit](https://developers.google.com/identity/sign-in/case-studies/reddit) reported a 90% desktop signup uplift
- [Pinterest](https://developers.google.com/identity/sign-in/case-studies/pinterest) saw a 47% improvement on web and mobile web

Google's [Federated Credential Management (FedCM)](https://developers.google.com/identity/gsi/web/guides/fedcm-migration) API [became mandatory](https://developers.google.com/identity/sign-in/web/gsi-with-fedcm) specifically for Google Identity Services (GIS) in August 2025, replacing the previous cross-origin iframe approach. After this date, any `use_fedcm` settings are ignored and FedCM is always used. If you're implementing Google login directly, your integration must support FedCM. Clerk's [`<GoogleOneTap />`](https://clerk.com/docs/nextjs/reference/components/authentication/google-one-tap.md) component has FedCM support enabled by default (`fedCmSupport` defaults to `true`).

Apps in Google's "testing" mode are limited to 100 test users. You'll need to set your app to "In production" and complete the [consent screen](https://clerk.com/glossary.md#consent-screen) verification before launch.

### GitHub

GitHub is essential for developer-facing applications. Unlike Google and Apple, GitHub's OAuth implementation is **not OIDC-compliant**, it does not return an ID token. Instead, you must call the GitHub `/user` API endpoint to retrieve profile data after obtaining an [access token](https://clerk.com/glossary.md#access-token). (Note: GitHub does support OIDC for GitHub Actions, but that's a separate use case unrelated to social login.)

Token expiration varies by app type: classic OAuth App tokens have [no time-based expiry](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation), meaning are revoked after one year of inactivity, manual revocation, or if exposed in a public repository. GitHub App user tokens expire after 8 hours by default and include [refresh tokens](https://clerk.com/glossary.md#refresh-token).

One important limitation: GitHub [OAuth Apps support only a single callback URL](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) per app, meaning you'll need separate OAuth app registrations for development, staging, and production environments.

### Apple

In January 2024, Apple revised [App Store Guideline 4.8](https://developer.apple.com/app-store/review/guidelines/) so that apps using third-party login no longer must specifically offer sign-in with Apple, but must provide an equivalent privacy-focused login option that limits data collection to name and email and allows private email usage.

Apple Sign in has the most complex setup of the three providers, requiring an Apple Developer Program membership ($99/year), a primary App ID, a Services ID for web, domain verification, and a private key for generating ES256-signed JWT client secrets.

A critical implementation detail: Apple returns the user's name and email **only during the first authorization**. If you fail to capture and persist this data on the initial sign-in, it cannot be retrieved again without the user revoking and re-authorizing your app. Apple's Hide My Email feature generates unique `@privaterelay.appleid.com` addresses per user per app, which won't match existing account emails.

### Microsoft

Microsoft is the top choice for enterprise and B2B applications. It's fully OIDC-compliant via the [Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc) and supports three tenant configurations: single-tenant (one org only), multi-tenant (any Azure AD org), or personal Microsoft accounts. Microsoft Entra External ID provides 50,000 free MAU for consumer-facing apps.

Since June 2023, Microsoft [removes email addresses with unverified domain owners](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference) from tokens for multi-tenant apps by default (see the [breaking changes documentation](https://learn.microsoft.com/en-us/entra/identity-platform/reference-breaking-changes) for details on this policy change). To get verified email status, add the [`xms_edov` optional claim](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims) in your app's Token Configuration. This boolean indicates whether the email domain owner has been verified. Clerk's [Microsoft configuration guide](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/microsoft.md) walks through this setup and recommends enabling `xms_edov` to protect against nOAuth [account takeover](https://clerk.com/glossary.md#account-takeover) exploits. Not all Microsoft accounts have an email address — your app should handle the missing `email` claim gracefully.

### Facebook

Facebook remains one of the most widely used social login providers globally, particularly outside the US. The `public_profile` scope is auto-granted to all apps, and basic login with `email` does not require Meta's App Review process. However, requesting permissions beyond `public_profile` and `email` triggers a review that includes a video walkthrough and usage justification. Facebook's web OAuth flow is standard OAuth 2.0, not full OIDC (Limited Login with OIDC tokens is available only on iOS).

### Discord

Discord is the standard social login for gaming, community, and creator-focused applications. Setup is free via the [Discord Developer Portal](https://docs.discord.com/developers/topics/oauth2). Discord uses standard OAuth 2.0 with `identify` and `email` scopes but does not provide a full OIDC discovery endpoint. It's widely supported: Clerk, Auth0 (Marketplace), Supabase, and Auth.js all have built-in support. Firebase Auth and WorkOS do not.

### LinkedIn OIDC

LinkedIn completed its migration from proprietary OAuth scopes to [standard OIDC](https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2) in 2023. The old `r_liteprofile` and `r_emailaddress` scopes are deprecated — use `openid profile email` instead. LinkedIn uses pairwise subject identifiers (the `sub` claim is unique per application). It's a strong choice for professional networking and B2B SaaS. Supported by Clerk, Auth0, Supabase, WorkOS, and Auth.js. Firebase Auth does not have native LinkedIn support.

### GitLab

GitLab functions as both a social provider and an OIDC identity provider with [full discovery support](https://docs.gitlab.com/integration/openid_connect_provider/). A unique advantage: it works with self-hosted GitLab instances, letting teams use their own GitLab server as the identity provider. GitLab returns group/role claims (`owner`, `maintainer`, `developer`), making it useful for [RBAC](https://clerk.com/glossary.md#role-based-access-control-rbac) in developer tools. Supported by Clerk, Supabase, WorkOS, and Auth.js. Auth0 requires a custom social connection. Firebase Auth does not have native support.

### Slack

Slack offers a fully [OIDC-compliant "Sign in with Slack"](https://docs.slack.dev/authentication/sign-in-with-slack/) flow, separate from the "Add to Slack" bot installation flow — these two flows cannot be combined in a single OAuth request. The OIDC flow uses `openid email profile` scopes and returns Slack-specific claims like `team_id` and workspace context. Useful for workplace and productivity tools where users already have Slack accounts. Supported by Clerk, Auth0 (Marketplace), Supabase, WorkOS, and Auth.js. Firebase Auth does not have native support.

### Twitch

Twitch provides [OIDC support](https://dev.twitch.tv/docs/authentication/getting-tokens-oidc/) with standard claims, but email retrieval requires both the `user:read:email` scope and an explicit `claims` parameter in the authorization request, which is not included by default. Best suited for streaming, gaming, and creator platforms. Twitch enforces scope minimalism: requesting unnecessary scopes may result in API access suspension. Supported by Clerk, Auth0 (Marketplace), Supabase, and Auth.js. Firebase Auth and WorkOS do not have native support.

### X (Twitter)

As of [April 2025](https://devcommunity.x.com/t/announcing-support-for-email-address-retrieval-with-oauth-2-0-in-the-x-api-v2/240555), X supports email retrieval via the `confirmed_email` field on the `/2/users/me` endpoint using the `users.email` scope. To enable this, you must turn on "Request email from users" in the X Developer dashboard under your app's User authentication settings. The free API tier supports the `/2/users/me` endpoint for OAuth social login (with rate limits); the $200/month Basic tier is required for general API read access, not for OAuth login itself. PKCE is mandatory for all OAuth 2.0 flows. X uses OAuth 2.0 only (not OIDC). Supported by Clerk, Auth0, Firebase Auth, Supabase, and Auth.js. WorkOS does not have native support.

### Coinbase

Coinbase is a niche provider for Web3 and cryptocurrency applications. It uses [OAuth 2.0 with PKCE](https://docs.cdp.coinbase.com/coinbase-app/authentication-authorization/oauth2/oauth2) and returns basic profile data. One major limitation: new OAuth client creation is currently limited to approved partners. Existing integrations continue to work, but new apps must apply through Coinbase's partner request process. Scopes use comma separation instead of spaces. Supported by Clerk and Auth.js only; other auth services require custom OAuth configuration.

### Linear

Linear is a project management tool popular with engineering teams. Its OAuth flow grants a `read` scope by default and uses a [GraphQL-only API](https://linear.app/developers/oauth-2-0-authentication) — there is no REST userinfo endpoint. You must query the `viewer` field via GraphQL to retrieve user data after authentication. Access tokens are valid for 24 hours. Clerk is the only major auth service with built-in Linear support.

### Notion

Notion's OAuth is designed primarily for workspace and page access, not user authentication. Instead of traditional scopes, it uses a [capability-based model](https://developers.notion.com/docs/authorization) where users choose which pages and databases to share during consent. User profile data (name, email, avatar) is returned in the token exchange response rather than a separate userinfo endpoint. Supported by Clerk, Supabase, and Auth.js.

### Vercel

Vercel offers [Sign in with Vercel](https://vercel.com/docs/sign-in-with-vercel) as a fully OIDC-compliant social login option. It uses standard `openid email profile` scopes and returns ID tokens with standard claims. Access tokens are valid for 1 hour; refresh tokens last 30 days with rotation. This is a relatively new feature and is developer-focused — useful for tools that integrate with the Vercel ecosystem. Clerk is the primary auth service with [built-in Vercel support](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/vercel.md).

### Provider Support by Auth Service

Not all auth services support every provider. This matrix shows built-in support. Some providers marked "No" may still be usable via custom OAuth/OIDC configuration where the auth service supports it.

| Provider    | Clerk |  Auth0 | Firebase Auth | Supabase Auth | WorkOS | Auth.js |
| ----------- | :---: | :----: | :-----------: | :-----------: | :----: | :-----: |
| Google      |  Yes  |   Yes  |      Yes      |      Yes      |   Yes  |   Yes   |
| GitHub      |  Yes  |   Yes  |      Yes      |      Yes      |   Yes  |   Yes   |
| Apple       |  Yes  |   Yes  |      Yes      |      Yes      |   Yes  |   Yes   |
| Microsoft   |  Yes  |   Yes  |      Yes      |      Yes      |   Yes  |   Yes   |
| Facebook    |  Yes  |   Yes  |      Yes      |      Yes      |   No   |   Yes   |
| Discord     |  Yes  |   Yes  |       No      |      Yes      |   No   |   Yes   |
| LinkedIn    |  Yes  |   Yes  |       No      |      Yes      |   Yes  |   Yes   |
| GitLab      |  Yes  | Custom |       No      |      Yes      |   Yes  |   Yes   |
| Slack       |  Yes  |   Yes  |       No      |      Yes      |   Yes  |   Yes   |
| Twitch      |  Yes  |   Yes  |       No      |      Yes      |   No   |   Yes   |
| X (Twitter) |  Yes  |   Yes  |      Yes      |      Yes      |   No   |   Yes   |
| Coinbase    |  Yes  | Custom |       No      |       No      |   No   |   Yes   |
| Linear      |  Yes  |   No   |       No      |       No      |   No   |    No   |
| Notion      |  Yes  |   No   |       No      |      Yes      |   No   |   Yes   |
| Vercel      |  Yes  | Custom |       No      |       No      |   No   |    No   |

Clerk supports all 15 providers listed above as built-in social connections, plus 13 additional providers (28 total). See the full list in the [Social connections overview](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview.md).

### Provider Decision Tree

For AI agents and developers choosing providers, this table maps application types to recommended provider combinations:

| App Type                  | Recommended Providers     | Reasoning                                                                                 |
| ------------------------- | ------------------------- | ----------------------------------------------------------------------------------------- |
| Consumer web app          | Google + Apple            | Covers the broadest user base; Apple required if you have an iOS companion app            |
| Developer tools / SaaS    | Google + GitHub           | GitHub identity is the standard for developer audiences; add GitLab for self-hosted teams |
| Enterprise / B2B          | Google + Microsoft        | Corporate Google Workspace and Azure AD/Entra ID accounts                                 |
| Global consumer app       | Google + Apple + Facebook | Facebook remains dominant in non-US markets                                               |
| Gaming / community        | Google + Discord          | Discord is the dominant identity for gaming and creator communities                       |
| Streaming / creator       | Google + Twitch           | Twitch identity for streaming platforms; add Discord for community features               |
| Professional / recruiting | Google + LinkedIn         | LinkedIn for professional identity and B2B networking apps                                |
| Workplace tools           | Google + Slack            | Slack for apps used alongside existing Slack workspaces                                   |
| Web3 / crypto             | Google + Coinbase         | Coinbase for crypto-native audiences (note: new OAuth apps currently suspended)           |
| Project management        | Google + Linear or Notion | Linear for engineering teams; Notion for cross-functional teams                           |

### How Many Providers Should You Support?

Limiting social login options to 2–3 providers avoids the "[NASCAR problem](https://indieweb.org/NASCAR_problem)" — a wall of provider buttons that creates decision paralysis rather than convenience. This follows [Hick's Law](https://lawsofux.com/hicks-law/): the time to make a decision increases logarithmically with the number of choices. More providers also increase "which one did I use?" confusion on return visits.

[Clerk's](https://clerk.com/pricing) free tier includes 3 social providers with 50,000 [Monthly Retained Users (MRU)](https://clerk.com/glossary.md#monthly-retained-users-mrus). The Pro plan ($20/month billed annually, $25/month billed monthly) unlocks unlimited social providers, with overage pricing at $0.02/MRU beyond the included 50K — significantly lower than Auth0's $0.07/MAU overage.

Always offer a fallback authentication method for users who prefer not to use social login. Email/password, [email OTP](https://clerk.com/glossary.md#email-otp), or [passkeys](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options.md#passkeys) are examples of complementary options.

## Implementing Social Login with Clerk

Clerk provides prebuilt UI components and SDK hooks that handle the complete OAuth/OIDC flow — authorization redirects, token exchange, [session management](https://clerk.com/glossary.md#session-management), and [account linking](https://clerk.com/glossary.md#account-linking) — across 10+ frameworks. The `<SignIn />` component renders provider-specific buttons with appropriate logos and labeling.

### Dashboard Configuration

Before writing any code, configure your social providers in the Clerk Dashboard under the [**SSO connections**](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview.md) page. This page includes both social and enterprise connections since October 2024.

In **development mode**, Clerk provides pre-configured shared OAuth credentials for all social providers, enabling zero-config testing — you can even use [keyless mode](https://clerk.com/docs/deployments/overview.md#keyless-mode) to skip API key setup entirely during initial development. For production, you'll need to create your own OAuth app credentials with each provider and enter the Client ID and Client Secret in the Clerk Dashboard.

Each provider has a dedicated configuration guide in the Clerk docs. The shared development credentials let you start testing social login immediately without any provider setup.

### Next.js 16

Next.js 16 uses [`proxy.ts`](https://nextjs.org/docs/app/getting-started/project-structure#top-level-files) (which replaces and deprecates `middleware.ts`). The Clerk Next.js SDK integrates with this new pattern.

Install the SDK:

```bash
npm install @clerk/nextjs
```

Set up the [environment variables](https://clerk.com/glossary.md#environment-variables) in `.env.local`:

```bash
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
```

Add [`ClerkProvider`](https://clerk.com/glossary.md#clerkprovider) to your root layout to make authentication state available throughout the app:

```tsx
// src/app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <ClerkProvider>{children}</ClerkProvider>
      </body>
    </html>
  )
}
```

Create `proxy.ts` at the project root with Clerk's middleware:

```typescript
// proxy.ts
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware()
```

Now add the `<SignIn />` component. This single component renders all configured social provider buttons plus any other enabled authentication methods:

```tsx
// src/app/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from '@clerk/nextjs'

export default function SignInPage() {
  return <SignIn />
}
```

### React (Vite / SPA)

For client-side React SPAs, the `@clerk/react` package provides the same components and hooks without server-side dependencies.

```bash
npm install @clerk/react
```

Wrap your app with `ClerkProvider` using the publishable key (no secret key needed for SPAs):

```tsx
// src/main.tsx
import { ClerkProvider } from '@clerk/react'

const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY

function App() {
  return <ClerkProvider publishableKey={PUBLISHABLE_KEY}>{/* your app routes */}</ClerkProvider>
}
```

The `<SignIn />` component works identically to the Next.js version:

```tsx
import { SignIn } from '@clerk/react'

export default function SignInPage() {
  return <SignIn />
}
```

### Vue

The `@clerk/vue` package provides Vue-native components and composables.

```bash
npm install @clerk/vue
```

Register the Clerk plugin in your Vue app:

```typescript
// src/main.ts
import { createApp } from 'vue'
import { clerkPlugin } from '@clerk/vue'
import App from './App.vue'

const app = createApp(App)
app.use(clerkPlugin, {
  publishableKey: import.meta.env.VITE_CLERK_PUBLISHABLE_KEY,
})
app.mount('#app')
```

Use the `<SignIn />` component in your template:

```vue
<script setup lang="ts">
import { SignIn } from '@clerk/vue'
</script>

<template>
  <SignIn />
</template>
```

> **Note:** For Vue SSR with Nuxt, Clerk provides [`@clerk/nuxt`](https://clerk.com/docs/quickstarts/nuxt.md). The Nuxt SDK includes `clerkMiddleware()` for server-side API route protection and `defineNuxtRouteMiddleware()` with `useAuth()` for client-side page protection. Note that `clerkMiddleware()` is [not recommended for page protection](https://clerk.com/docs/reference/nuxt/clerk-middleware.md) as it only runs on initial page load — client-side navigation bypasses server middleware. The examples above use `@clerk/vue` for client-side SPAs.

### Astro

The `@clerk/astro` package integrates with Astro's island architecture.

```bash
npm install @clerk/astro
```

Add the Clerk integration to your Astro config:

```typescript
// astro.config.mjs
import { defineConfig } from 'astro/config'
import clerk from '@clerk/astro'

export default defineConfig({
  integrations: [clerk()],
})
```

Create `middleware.ts` at the project root with Clerk middleware (Astro uses its own middleware pattern):

```typescript
// src/middleware.ts
import { clerkMiddleware } from '@clerk/astro/server'

export const onRequest = clerkMiddleware()
```

Astro requires an SSR adapter and both environment keys (`PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY`). The `<SignIn />` component is imported from a dedicated path:

```astro
---
import { SignIn } from '@clerk/astro/components'
---

<SignIn />
```

### React Router

The `@clerk/react-router` package integrates Clerk with React Router 7 in framework mode — the successor to Remix. Since Remix merged into React Router 7, `@clerk/react-router` is now the recommended package for both new React Router projects and migrations from Remix (`@clerk/remix` is in maintenance mode, receiving security updates only).

```bash
npm install @clerk/react-router
```

React Router middleware requires opting in via a future flag. Add it to `react-router.config.ts`:

```typescript
// react-router.config.ts
import type { Config } from '@react-router/dev/config'

export default {
  ssr: true,
  future: {
    v8_middleware: true,
  },
} satisfies Config
```

In `app/root.tsx`, configure `clerkMiddleware()` for route protection and `rootAuthLoader()` to pass authentication state to the client. Then wrap the app with `ClerkProvider`, passing the `loaderData` prop:

```tsx
// app/root.tsx
import { ClerkProvider } from '@clerk/react-router'
import { Outlet } from 'react-router'
import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server'
import type { Route } from './+types/root'

export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]
export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args)

export default function App({ loaderData }: Route.ComponentProps) {
  return (
    <ClerkProvider loaderData={loaderData}>
      <Outlet />
    </ClerkProvider>
  )
}
```

Create a sign-in route using a React Router splat route:

```tsx
// app/routes/sign-in.tsx
import { SignIn } from '@clerk/react-router'

export default function SignInPage() {
  return <SignIn />
}
```

### TanStack Start

The `@clerk/tanstack-react-start` package provides Clerk integration for TanStack Start.

```bash
npm install @clerk/tanstack-react-start
```

Add `ClerkProvider` in your root route (`__root.tsx`), configure `clerkMiddleware()` in `src/start.ts`, and create a splat route for the sign-in page:

```tsx
// src/routes/sign-in.$.tsx
import { SignIn } from '@clerk/tanstack-react-start'

function SignInPage() {
  return <SignIn />
}
```

> **Note:** `@clerk/tanstack-react-start` is currently v0.x. While functional and documented in [Clerk's official guides](https://clerk.com/docs/quickstarts/tanstack-start.md), it may receive breaking changes before reaching v1.0.

### Cross-Framework Pattern Summary

| Framework      | Package                       | Provider Setup        | Middleware          | SignIn Import                 | Secret Key Required |
| -------------- | ----------------------------- | --------------------- | ------------------- | ----------------------------- | ------------------- |
| Next.js 16     | `@clerk/nextjs`               | `ClerkProvider`       | `proxy.ts`          | `@clerk/nextjs`               | Yes                 |
| React (SPA)    | `@clerk/react`                | `ClerkProvider`       | None                | `@clerk/react`                | No                  |
| Vue            | `@clerk/vue`                  | `clerkPlugin`         | None                | `@clerk/vue`                  | No                  |
| Astro          | `@clerk/astro`                | `clerk()` integration | `src/middleware.ts` | `@clerk/astro/components`     | Yes                 |
| React Router   | `@clerk/react-router`         | `ClerkProvider`       | `clerkMiddleware()` | `@clerk/react-router`         | Yes                 |
| TanStack Start | `@clerk/tanstack-react-start` | `ClerkProvider`       | `src/start.ts`      | `@clerk/tanstack-react-start` | Yes                 |

The pattern is consistent across all frameworks: install the SDK, wrap your app with a provider, optionally add middleware for server-side auth, and drop in the `<SignIn />` component. Clerk handles the OAuth redirects, token exchange, session creation, and account linking behind the scenes.

This adapter pattern — where the same authentication API surface (`<SignIn />`, `useSignIn()`, `authenticateWithRedirect()`) is exposed through framework-specific adapters — means authentication logic stays portable across frameworks. Migrating from React to Vue, for example, requires only changing the import paths, not rewriting the auth flow.

### Native Mobile SDKs

Clerk's social login support extends beyond web frameworks to native mobile platforms. Each mobile SDK provides social authentication through platform-appropriate patterns — native system dialogs where available, and browser-based OAuth flows as the fallback.

| Platform            | Package                                  | UI Components                                         | Social Login Pattern                                                                                                                    | Status                                                                     |
| ------------------- | ---------------------------------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| React Native / Expo | `@clerk/expo`                            | Control components (native); UI components (Expo web) | Native Apple Sign-in via `useSignInWithApple()`; browser-based OAuth for other providers                                                | GA                                                                         |
| iOS (Swift)         | `ClerkKit` + `ClerkKitUI`                | `AuthView` (prebuilt sign-in/sign-up)                 | Native Apple Sign-in; browser-based OAuth for Google and others                                                                         | GA                                                                         |
| Android (Kotlin)    | `clerk-android-api` + `clerk-android-ui` | Prebuilt sign-in/sign-up views                        | [Native Google Sign-in](https://clerk.com/docs/guides/configure/auth-strategies/sign-in-with-google.md); browser-based OAuth for others | GA ([Sept 2025](https://clerk.com/changelog/2025-09-11-android-sdk-ga.md)) |
| Flutter (Dart)      | `clerk_flutter` + `clerk_auth`           | In development                                        | Social login supported                                                                                                                  | [Beta](https://clerk.com/changelog/2025-03-26-flutter-sdk-beta.md)         |

**Expo (React Native):** The `@clerk/expo` package provides the same `useSignIn()` hook pattern as the web SDKs. For Apple, the dedicated [`useSignInWithApple()`](https://clerk.com/docs/expo/guides/configure/auth-strategies/sign-in-with-apple.md) hook uses Apple's native authorization to provide the OpenID token directly to Clerk — no browser redirect needed.

```typescript
// Expo - Native Apple Sign-in
import { useSignInWithApple } from '@clerk/expo'

export function AppleSignInButton() {
  const { signInWithApple, isLoaded } = useSignInWithApple()

  const onPress = async () => {
    if (!isLoaded) return
    try {
      const { createdSessionId, setActive } = await signInWithApple()
      if (createdSessionId) await setActive({ session: createdSessionId })
    } catch (err: any) {
      if (err.errors?.[0]?.code === 'ERR_REQUEST_CANCELED') return
      console.error(err)
    }
  }

  return <Button title="Sign in with Apple" onPress={onPress} />
}
```

The `useSignInWithApple()` hook automatically manages the transfer flow between sign-up and sign-in, so a single component handles both scenarios.

**iOS (Swift):** The [Clerk iOS SDK](https://clerk.com/docs/ios/getting-started/quickstart.md) uses `ClerkKit` and `ClerkKitUI` via Swift Package Manager. The prebuilt [`AuthView`](https://clerk.com/docs/ios/reference/views/authentication/auth-view.md) component handles the complete sign-in/sign-up flow including social providers.

```swift
// iOS - Prebuilt AuthView with social login
import SwiftUI
import ClerkKit
import ClerkKitUI

struct ContentView: View {
  @Environment(Clerk.self) private var clerk
  @State private var showSignIn = false

  var body: some View {
    if clerk.session != nil {
      UserButton()
    } else {
      Button("Sign in") { showSignIn = true }
        .sheet(isPresented: $showSignIn) {
          AuthView()
        }
    }
  }
}
```

The `AuthView` component renders all configured social providers automatically based on your Clerk Dashboard settings — no per-provider code needed.

**Android (Kotlin):** The [Clerk Android SDK](https://clerk.com/docs/android/getting-started/quickstart.md) provides native [Google Sign-in support](https://clerk.com/docs/guides/configure/auth-strategies/sign-in-with-google.md) and prebuilt authentication views.

```kotlin
// Android - Google Sign-in with Clerk
import com.clerk.android.api.models.OAuthProvider
import com.clerk.android.api.models.SignIn

// Trigger Google OAuth flow
scope.launch {
  SignIn.authenticateWithRedirect(
    SignIn.AuthenticateWithRedirectParams.OAuth(
      provider = OAuthProvider.GOOGLE
    )
  )
}
```

The Android SDK reached GA in [September 2025](https://clerk.com/changelog/2025-09-11-android-sdk-ga.md), built with Kotlin and following modern Android development standards including Jetpack Compose support.

**Flutter (Dart):** The official [Clerk Flutter SDK](https://github.com/clerk/clerk-sdk-flutter) entered [public beta in March 2025](https://clerk.com/changelog/2025-03-26-flutter-sdk-beta.md) and is actively maintained (v0.0.14-beta as of February 2026). It includes `clerk_flutter` for Flutter apps and `clerk_auth` for Dart backends, with cross-platform support for iOS, Android, and web.

```dart
// Flutter - Initialize Clerk
import 'package:clerk_flutter/clerk_flutter.dart';

// Clerk initialization happens in your app setup
// Social login is configured through the Clerk Dashboard
// and available through the SDK's authentication methods
```

While the Flutter SDK is in beta and its API surface may change, it demonstrates Clerk's commitment to native mobile platforms. For production Flutter apps requiring a stable SDK, Firebase Auth and Supabase Auth offer GA-level Flutter support today.

### Google One Tap

Google One Tap displays a non-intrusive prompt to users who are already signed into their Google account, allowing them to sign up or sign in with a single click — no redirect, no popup, no password. Google's own case studies report meaningful improvements to conversion:

- [Reddit](https://developers.google.com/identity/sign-in/case-studies/reddit) saw a 90% increase in desktop sign-ups
- [Pinterest](https://developers.google.com/identity/sign-in/case-studies/pinterest) saw a 47% increase in web sign-ups after implementing One Tap

With most authentication providers, adding One Tap means manually loading Google's Identity Services script, initializing the client, handling credential callbacks, managing cryptographic nonces, and wiring everything to your auth backend. Supabase's implementation runs roughly 70 lines of TypeScript including SHA-256 nonce handling. Auth0 and Auth.js lack official One Tap support entirely, requiring community-driven workarounds of 40–80 lines.

Clerk reduces this to a single component. Place [`<GoogleOneTap />`](https://clerk.com/docs/nextjs/reference/components/authentication/google-one-tap.md) in your root layout and it appears on every page:

```tsx
// app/layout.tsx
import { ClerkProvider, GoogleOneTap } from '@clerk/nextjs'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <ClerkProvider>
          <GoogleOneTap />
          {children}
        </ClerkProvider>
      </body>
    </html>
  )
}
```

The component handles the full lifecycle: it loads the Google Identity Services script, manages the FedCM handshake (enabled by default since August 2025), deals with ITP browser quirks in Safari and Firefox, and automatically hides when the user is already authenticated. For additional control, the component accepts configuration props:

```tsx
<GoogleOneTap cancelOnTapOutside={true} fedCmSupport={true} signUpForceRedirectUrl="/onboarding" />
```

The `fedCmSupport` prop (default: `true`) enables the Federated Credential Management API, which Google now requires for all Identity Services integrations. The `itpSupport` prop (also default: `true`) handles Safari and Firefox's Intelligent Tracking Prevention automatically. These are details that competitors leave to the developer to implement manually.

**Prerequisites:** Enable Google as a [social connection](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/google.md) in the Clerk Dashboard with custom Google credentials from the Google Cloud Console.

## The OAuth Callback: Architecting a Secure Backend

When a user clicks "Sign in with Google," a carefully orchestrated exchange begins between your application, the user's browser, and the identity provider. Understanding this flow is essential for debugging issues and making informed security decisions.

### The Authorization Code Flow

1. **Authorization request** — Your app redirects the browser to the provider's `/authorize` endpoint with parameters: `client_id`, `redirect_uri`, `scope`, `state`, `nonce`, and `code_challenge` (PKCE)
2. **User authentication** — The provider authenticates the user and displays a consent screen
3. **Authorization code** — The provider redirects the browser back to your `redirect_uri` with a short-lived authorization code and the `state` parameter
4. **Token exchange** — Your backend sends the authorization code, `client_secret`, and `code_verifier` (PKCE) to the provider's `/token` endpoint
5. **Token response** — The provider returns an access token, ID token (OIDC), and optionally a refresh token
6. **Session creation** — Your application validates the **ID token** (verifying its signature and claims), extracts the user's identity, and creates a **separate application session** — typically an [HttpOnly](https://clerk.com/glossary.md#httponly-cookies) session cookie or a JWT issued by your auth service. The provider's access and refresh tokens are stored server-side only if your app needs ongoing access to the provider's APIs. The application session credential, not the provider tokens, is what keeps the user logged in going forward

### Three Layers of Security

The Authorization Code flow uses three complementary mechanisms to prevent different attack vectors:

| Mechanism                                                      | Prevents                                                                      | How It Works                                                                                                                                                                          |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **State parameter**                                            | [CSRF](https://clerk.com/glossary.md#cross-site-request-forgery-csrf) attacks | A cryptographically random value generated before the redirect, validated on callback. Ensures the callback originated from your app's authorization request.                         |
| **[Nonce](https://clerk.com/glossary.md#cryptographic-nonce)** | Token replay attacks                                                          | A random value embedded in the ID token request, verified after token receipt. Ensures the ID token was issued for this specific authentication attempt.                              |
| **PKCE**                                                       | Authorization code interception                                               | A code verifier/challenge pair. The challenge is sent with the authorization request; the verifier is sent during token exchange. Only the original client can complete the exchange. |

### PKCE (Proof Key for Code Exchange)

PKCE ([RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)) was originally designed for mobile and public clients. The [OAuth 2.0 Security Best Current Practice (RFC 9700)](https://datatracker.ietf.org/doc/html/rfc9700) now **requires** PKCE for public clients (MUST) and **recommends** it for confidential (server-side) clients as well (RECOMMENDED), making it a best practice for all OAuth 2.0 deployments.

The mechanism is straightforward:

1. Generate a random `code_verifier` (43–128 characters)
2. Compute `code_challenge = BASE64URL(SHA256(code_verifier))`
3. Send `code_challenge` with the authorization request
4. Send `code_verifier` with the token exchange request
5. The provider verifies `SHA256(code_verifier) == code_challenge`

Even if an attacker intercepts the authorization code, they cannot exchange it without the `code_verifier`.

Clerk implements all three mechanisms — state, nonce, and PKCE — automatically for all social login flows. Clerk added [PKCE support for custom OAuth providers](https://clerk.com/changelog/2025-11-12-pkce-support-custom-oauth.md) on November 12, 2025. Per OAuth 2.0 ([RFC 6749 Section 4.1.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2)), authorization codes "MUST expire shortly after" issuance, with a recommended maximum lifetime of 10 minutes.

### Redirect Flow vs. Popup Flow

**Always use the redirect flow for social login in web applications.** The popup flow has fundamental reliability and security problems that make it unsuitable for production use. Auth0's guidance on their Lock component states that "there are almost no reasons not to use redirect mode when writing a regular web application" ([Auth0 blog](https://auth0.com/blog/getting-started-with-lock-episode-3-redirect-vs-popup-mode/)).

| Concern                         | Redirect Flow                                        | Popup Flow                                                                                                             |
| ------------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Mobile browsers                 | Works on all mobile browsers                         | Blocked by default on most mobile browsers (iOS Safari, Android Chrome)                                                |
| Popup blockers                  | Not affected                                         | Blocked when any async operation (API call, analytics event, state setup) runs before `window.open()`                  |
| PWAs and WebViews               | Works natively                                       | Breaks in PWAs, Electron, React Native WebViews, and in-app browsers (Instagram, TikTok, Facebook)                     |
| Cross-origin communication      | Not needed — callback is a standard HTTP redirect    | Requires fragile `postMessage` between popup and opener window                                                         |
| Tab/window management           | Single tab throughout                                | Users may close the popup thinking it's an ad, or the opener tab may be garbage-collected by the browser               |
| Third-party cookie restrictions | Unaffected — uses first-party redirects              | May fail in Safari and Firefox with strict third-party cookie blocking, since the popup is a separate browsing context |
| Server-side token exchange      | Natural — the callback hits your server directly     | Requires the popup to relay the authorization code back to the opener, adding complexity and attack surface            |
| FedCM compatibility             | Coexists with FedCM (separate browser-mediated flow) | FedCM uses `navigator.credentials.get()`, a browser-mediated API — it is neither redirect-based nor popup-based        |

The popup approach introduces a class of bugs that are difficult to reproduce and diagnose: the flow silently fails when a popup blocker intervenes, leaving the user on the sign-in page with no error message. On mobile devices — which account for over half of web traffic — popup-based OAuth is effectively broken.

Clerk, Auth0, WorkOS, and Supabase all default to the redirect flow in their SDKs. Firebase Auth offers both `signInWithPopup()` and `signInWithRedirect()` — while neither is deprecated, `signInWithRedirect()` avoids the popup reliability issues described above and is the better choice for production applications.

### OAuth Data Flow (TypeScript Reference)

For developers implementing custom OAuth flows (without an auth service handling the details), these TypeScript interfaces illustrate the data exchanged at each step:

```typescript
// Step 1: Authorization Request
interface AuthorizationRequest {
  client_id: string
  redirect_uri: string
  response_type: 'code'
  scope: string // e.g., 'openid email profile'
  state: string // CSRF token (cryptographically random)
  nonce: string // Replay prevention
  code_challenge: string // PKCE: BASE64URL(SHA256(code_verifier))
  code_challenge_method: 'S256'
}
```

After the user authenticates, the provider redirects back with the authorization code:

```typescript
// Step 3: Authorization Response (query parameters on redirect)
interface AuthorizationResponse {
  code: string // Short-lived authorization code
  state: string // Must match the original state value
}
```

Your backend then exchanges the code for an access token, ID token, and optionally a refresh token:

```typescript
// Step 4: Token Exchange Request (server-side POST)
interface TokenRequest {
  grant_type: 'authorization_code'
  code: string // From the authorization response
  redirect_uri: string // Must match the original request
  client_id: string
  client_secret: string // Server-side only — never in browser code
  code_verifier: string // PKCE: original random string
}

// Step 5: Token Response
interface TokenResponse {
  access_token: string // For calling provider APIs
  id_token: string // OIDC: signed JWT with identity claims
  token_type: 'Bearer'
  expires_in: number // Access token lifetime in seconds
  refresh_token?: string // Optional, for long-lived access
  scope: string
}
```

When using Clerk, you don't implement these TypeScript interfaces directly, the SDK handles the entire flow. These types are useful for understanding what happens under the hood and for debugging.

In a custom implementation: extract the identity claims you need from the `id_token`, create or find the user in your database, and issue your own session credential (such as an HttpOnly session cookie). Only persist the `access_token` and `refresh_token` server-side if your application needs to call the provider's APIs on behalf of the user (e.g., reading GitHub repositories or Google Calendar events). Discard them otherwise.

### Redirect Handling Patterns

During the OAuth redirect, your application must persist certain values across the round trip:

- **State and nonce**: Store in a server session or a secure, short-lived cookie — not in `localStorage`, which is vulnerable to [XSS](https://clerk.com/glossary.md#cross-site-scripting-xss)
- **PKCE code verifier**: `sessionStorage` is acceptable here because the verifier is a short-lived, single-use cryptographic challenge, not an access token or refresh token. It's cleared when the tab closes and is only used once during token exchange.
- **Return URL**: Preserve the user's intended destination (the page they were on before sign-in) by encoding it in the OAuth `state` parameter or storing it in the server session

## Managing User Profiles and Account Linking

Account linking is one of the most security-sensitive aspects of social login. When a user signs in with Google using `alice@example.com` and later returns with GitHub using the same email, your application must decide: is this the same person, or an attacker?

### The Duplicate Account Problem

Without account linking, users who sign in with different providers create separate accounts with fragmented data, duplicated billing, and confused access. With naive automatic linking (merging accounts based solely on email match), you open the door to account takeover attacks.

### Three Linking Patterns

1. **Automatic (email-based)** — The system merges accounts when the OAuth email matches an existing account's verified email. No user interaction required. Used by Clerk, Supabase, and WorkOS.
2. **Link-on-login (prompted)** — The system detects a matching email and asks the user to prove ownership of the existing account (enter password, complete [MFA](https://clerk.com/glossary.md#multi-factor-authentication-mfa)) before linking. Used by Ory Kratos and Zitadel (Zitadel also supports automatic linking, configurable per identity provider).
3. **Manual (user-initiated)** — Users explicitly link accounts through profile settings while already authenticated. Used by Auth0 and Firebase.

### Pre-Hijacking Attacks

Researchers found that at least 35 of 75 popular services were vulnerable to pre-hijacking attacks (Sudhodanan & Paverd, [USENIX Security 2022](https://www.usenix.org/conference/usenixsecurity22/presentation/sudhodanan)). Andrew Paverd conducted this research at the Microsoft Security Response Center; Avinash Sudhodanan was an independent researcher at the time of publication. In these attacks, an adversary creates an account with a victim's email before the victim registers, then exploits automatic account linking to gain access when the victim eventually signs up via a social provider.

The critical defense — validated by this independent peer-reviewed research — is the `email_verified` claim in OIDC tokens. Automatic linking should only occur when **both** the existing account's email and the incoming OAuth email are verified by their respective providers.

### How Clerk Handles Account Linking

Clerk implements [automatic email-based linking](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/account-linking.md) with strict verification requirements:

- **Both emails verified**: The OAuth account is automatically linked to the existing account. The user is signed in seamlessly.
- **OAuth email unverified**: Clerk initiates email verification before linking, preventing pre-hijacking.
- **Existing account email unverified**: Clerk requires enhanced security steps, such as password change or additional validation, before allowing the link.
- **Different emails**: Users can manually link accounts through the [`<UserProfile />`](https://clerk.com/docs/reference/components/user/user-profile.md) component by adding alternative email addresses. See the [account linking guide](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/account-linking.md) for full details on how Clerk manages linked social accounts.

Clerk audits all supported SSO providers for OIDC compliance and `email_verified` accuracy. Non-compliant providers that return non-standard claim names (such as `verified` instead of `email_verified`) are handled automatically (source: ["How We Roll — Chapter 4: Email Verification"](https://clerk.com/blog/how-we-roll-email-verification.md) blog post).

### Handling Email-Absent and Private-Email Providers

Not all providers return an email address by default:

- **GitHub**: User email may be private. Request the `user:email` scope and call the `/user/emails` endpoint. If the user has no public email, you may receive `null` from the profile endpoint.
- **Apple**: Hide My Email generates unique `@privaterelay.appleid.com` addresses per app. These relay addresses won't match existing account emails, preventing automatic linking.

**Resolution**: Allow users to add and verify their real email address post-signup via profile settings. Store the provider's stable identifier (`sub` for OIDC providers, `id` for GitHub) as the primary key rather than relying solely on email.

### How Competitors Handle Account Linking

| Provider     | Linking Approach                                         | Developer Effort                                                                   |
| ------------ | -------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| **Clerk**    | Automatic with verified email                            | None — handled by SDK                                                              |
| **Auth0**    | Manual by default; requires custom Actions for automatic | High — but intentional: gives developers full control over linking security policy |
| **Firebase** | Throws `account-exists-with-different-credential` error  | High — catch error, prompt user, call `linkWithCredential()`                       |
| **Supabase** | Automatic with verified email                            | Low — similar to Clerk                                                             |
| **WorkOS**   | Automatic with verified email                            | None — handled by SDK                                                              |

### Account Recovery When a Provider Is Unavailable

If a user loses access to their social provider (e.g., a deactivated Google account), they need a way to regain access. Best practices:

- Always offer a fallback authentication method (email/password, email OTP, [passkeys](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options.md#passkeys), or email magic links)
- Allow users to link multiple social providers to their account so they have alternatives
- Clerk's [`<UserProfile />`](https://clerk.com/docs/reference/components/user/user-profile.md) component lets users manage linked accounts and add new authentication methods from their profile settings

## Security Best Practices: Token Storage and Session Management

There are two distinct categories of tokens to consider after social login. **Provider tokens** — the access token, ID token, and optional refresh token issued by Google, GitHub, or other identity providers — are received during the OAuth token exchange and are typically handled entirely by your auth service's backend. You never see or store these directly. **Application session credentials** — session cookies, JWTs, or other tokens issued by your auth service — represent the user's authenticated session in your application and are what the browser stores and sends on each request. The storage recommendations in this section apply to application session credentials. If your application also needs provider tokens (e.g., to call the Google Calendar API on behalf of the user), those should be stored server-side and never exposed to the browser.

### Token Storage Comparison

The following table compares mechanisms for storing **application session credentials** — the tokens or cookies your auth service issues to maintain the user's logged-in state. Provider tokens (Google/GitHub access tokens and refresh tokens) should always be stored server-side by your auth service, not in the browser.

| Mechanism       | XSS Safe | CSRF Safe            | Persists | Recommended By   |
| --------------- | -------- | -------------------- | -------- | ---------------- |
| HttpOnly Cookie | Yes      | Mitigated (SameSite) | Yes      | OWASP, IETF      |
| localStorage    | No       | Yes                  | Yes      | Not recommended  |
| sessionStorage  | No       | Yes                  | No       | Not recommended  |
| IndexedDB       | No       | Yes                  | Yes      | Not recommended  |
| In-Memory       | Partial  | Yes                  | No       | Auth0 (for SPAs) |

The consensus from [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html) and the [IETF Browser-Based Apps BCP](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps) is clear: **HttpOnly cookies are the recommended mechanism for token storage in web applications.** The IETF BCP recommends that browser-based applications use a backend component (the BFF pattern) to keep tokens out of the browser entirely, and advises against storing tokens in `localStorage` or similar browser-accessible storage.

Any JavaScript running on your page can access `localStorage`, `sessionStorage`, and `IndexedDB`. A single XSS vulnerability means an attacker can exfiltrate tokens and use them from any device for their entire lifetime.

### The Backend for Frontend (BFF) Pattern

The [IETF Browser-Based Apps BCP](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps) (currently an Internet-Draft, not yet an RFC) recommends the Backend for Frontend pattern as the preferred architecture for browser-based OAuth applications. Note: [RFC 9700](https://datatracker.ietf.org/doc/html/rfc9700) ("OAuth 2.0 Security Best Current Practice") is a separate, finalized document covering general OAuth security; it does not specifically define the BFF pattern. In the BFF model:

1. The browser communicates with a backend component using secure, HttpOnly session cookies
2. The backend handles all OAuth token exchange as a confidential client
3. **Provider tokens** (access tokens, refresh tokens) are stored server-side — they never reach the browser
4. The backend proxies API requests to the provider, attaching the **provider's access token** before forwarding

Clerk, Auth0, and WorkOS all implement variants of this pattern, keeping provider tokens server-side. In Clerk's variant, the provider's OAuth tokens stay on Clerk's servers and are never sent to the browser, while Clerk issues its own purpose-built session cookies to the browser with short lifetimes and revocation support.

### Clerk's Hybrid Token Architecture

Clerk uses a dual-cookie system designed to combine the security of server-side sessions with the performance of stateless JWTs. This architecture is documented in detail in the [How Clerk Works](https://clerk.com/docs/guides/how-clerk-works/overview.md) guide.

Neither of these cookies contains the OAuth provider's tokens. The Google (or GitHub, Apple, etc.) access token and refresh token are held on Clerk's servers and never sent to the browser. The `__client` and `__session` cookies described below are Clerk's own session credentials — entirely separate from the provider's OAuth tokens:

**`__client` cookie** — A long-lived, HttpOnly, SameSite=Lax cookie set on the Clerk FAPI (Frontend API) domain. This cookie handles session revocation and is the primary authentication state. Because it's HttpOnly, JavaScript cannot access it. In development mode, Clerk uses `__clerk_db_jwt` via querystring instead of cookies.

**`__session` cookie** — A short-lived JWT (60-second TTL) set on the application's root domain **by the client-side SDK** (not via `Set-Cookie` from the FAPI). This cookie carries the JWT claims your application reads for authorization decisions.

**Why is `__session` not HttpOnly?** The Clerk client SDK needs to set this cookie via JavaScript on the app domain. The security tradeoff is mitigated by:

- **60-second TTL**: Tokens expire before attackers can meaningfully exploit them
- **50-second refresh interval**: The SDK refreshes 10 seconds before expiry, ensuring a buffer for network latency
- **Stateful revocation**: Even if a `__session` token is stolen, revoking the `__client` session invalidates it on the next SDK renewal cycle

From the [How Clerk Works](https://clerk.com/docs/guides/how-clerk-works/overview.md) documentation: "For an XSS attack to succeed, the developer would need to ship a vulnerability on their site, and the attacker would need to exfiltrate users' tokens and use them to take over accounts in an average of less than 30 seconds."

This architecture combines the best of both models — **stateful revocation** (via `__client`) for security, and **stateless JWT verification** (via `__session`) for performance. Your backend can verify the session JWT locally using cryptographic signature validation, with no network call to Clerk required on each request — enabling sub-millisecond auth checks at the edge or in serverless functions.

### How Competitors Store Session Credentials

Each auth service issues its own session credentials after completing the OAuth exchange with the identity provider. The provider's OAuth tokens (e.g., Google's access and refresh tokens) are consumed during this exchange and are typically not stored in the browser. Here is how each service stores its own credentials:

- **Auth0**: Defaults to in-memory storage (JavaScript closures) for Auth0-issued tokens (Auth0's own access and refresh tokens for your application — distinct from the Google/GitHub provider tokens, which Auth0 handles server-side). With [Rotating Refresh Tokens](https://auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation) enabled, sessions restore silently after page refreshes. When `useRefreshTokens: true` is enabled with the default in-memory cache, the SDK uses a [Web Worker](https://auth0.com/docs/secure/security-guidance/data-security/token-storage) to isolate Auth0 refresh token operations from the main thread. Auth0 tokens themselves are not persisted in the browser, but the SDK re-obtains them automatically via refresh token exchange — so users stay logged in. Optional `localStorage` fallback stores tokens directly at the cost of XSS exposure.
- **Firebase Auth**: Uses IndexedDB to persist Firebase authentication state (Firebase ID tokens and Firebase refresh tokens — not the identity provider's OAuth tokens). IndexedDB has the same XSS vulnerability profile as `localStorage` — any JavaScript on the page can access stored tokens.
- **Supabase Auth**: Defaults to `localStorage` for Supabase-issued session tokens. The `@supabase/ssr` package provides cookie-based storage for server-rendered applications.
- **WorkOS**: Uses [encrypted session cookies](https://github.com/workos/authkit-session) (AES-256-CBC + SHA-256 HMAC via `iron-webcrypto`). WorkOS-issued access tokens have a [5-minute TTL](https://workos.com/blog/session-management-for-frontend-apps-with-authkit); WorkOS-issued refresh tokens expire after 7 days. Refresh token rotation is enforced.

### OWASP Recommendations

The key recommendations from OWASP for token storage in web applications:

- Do not store tokens in `localStorage` or `sessionStorage`
- Use `Secure`, `HttpOnly`, and `SameSite` cookie flags for authentication tokens
- Keep your application's session token lifetimes short — under 15 minutes is a widely adopted industry practice. [NIST SP 800-63B-4](https://pages.nist.gov/800-63-4/sp800-63b.html) specifies reauthentication limits that vary by assurance level: AAL2 SHOULD reauthenticate within 24 hours (with 1-hour inactivity timeout), while AAL3 SHALL reauthenticate within 12 hours (with 15-minute inactivity timeout)
- Implement application-level refresh token rotation with reuse detection
- Use a [Content Security Policy (CSP)](https://clerk.com/glossary.md#content-security-policy-csp) as defense-in-depth against XSS

### Privacy and GDPR Considerations

Social login collects personal data — email addresses, names, profile pictures — that is subject to regulations like [GDPR](https://clerk.com/glossary.md#data-privacy) and [CCPA](https://clerk.com/glossary.md#california-consumer-privacy-act-ccpa).

**Data minimization**: Only request the OAuth scopes you need. For authentication, `openid email` is sufficient. Avoid requesting `profile` unless your application actually uses the user's name and photo.

**Explicit consent**: Users must understand what data is shared before clicking a social login button. The OAuth consent screen provided by each identity provider serves this purpose, but your privacy policy should also document what data you collect and why.

**How Clerk helps**: Clerk acts as the data processor, handling the OAuth token exchange server-side so user identity data (emails, names, profile photos) never touches your infrastructure directly. Clerk provides data export and deletion APIs for GDPR right-to-erasure requests, and the prebuilt `<SignIn />` component only requests the scopes you configure in the Dashboard.

## Comparing Social Login Providers

Choosing an authentication service affects your development velocity, security posture, pricing at scale, and the login experience your users see. This section compares six options across the dimensions that matter most for social login.

### Feature Comparison

| Feature              | Clerk                                                    | Auth0                             | Firebase Auth | Supabase Auth                                      | WorkOS                                           | Auth.js                                       |
| -------------------- | -------------------------------------------------------- | --------------------------------- | ------------- | -------------------------------------------------- | ------------------------------------------------ | --------------------------------------------- |
| Social Providers     | 28 built-in + any OIDC provider\*\*                      | 70+ (Marketplace)\*\*             | 7             | 20+                                                | 8 social + enterprise providers                  | 80+ (community)\*\*                           |
| Free Tier\*          | 50K MRU, 3 social                                        | 25K MAU, unlimited social         | 50K MAU       | 50K MAU                                            | 1M MAU                                           | Unlimited (self-hosted)                       |
| Prebuilt UI          | Embeddable components                                    | Hosted redirect (Universal Login) | FirebaseUI    | Archived (Feb 2024 maintenance, Oct 2025 archived) | Hosted redirect (AuthKit)                        | None                                          |
| Account Linking      | Automatic (verified email)                               | Manual (API/Actions)              | Manual (SDK)  | Automatic (verified email)                         | Automatic (verified email)                       | Opt-in per provider                           |
| Framework SDKs       | Next.js, React, React Router, Vue, Astro, TanStack, Expo | Any (redirect-based)              | Any (SDK)     | Any (SDK)                                          | Next.js, Remix, TanStack, Node, Python, Ruby, Go | Next.js, SvelteKit, Qwik, Express, SolidStart |
| Hosted / Self-hosted | Hosted                                                   | Hosted                            | Hosted        | Hosted (self-hostable)                             | Hosted                                           | Self-hosted                                   |

\* **MRU vs MAU:** Clerk uses Monthly Retained Users (MRU) — users who return during the billing period. Other providers use Monthly Active Users (MAU) — users who authenticate at least once during the calendar month. These metrics are not directly equivalent; MRU excludes one-time visitors who never return.

\*\* **Provider counts:** Auth0's 70+ and Auth.js's 80+ include Marketplace partner integrations and community-maintained adapters, respectively. Clerk's 28 built-in providers cover the most common social and developer identity platforms. Clerk's [custom OIDC](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/custom-provider.md) supports any OIDC-compliant provider without adapter code — configure the discovery endpoint and credentials in the Dashboard, with attribute mapping for non-standard claims. See [Any OIDC Provider, No Adapter Code](#any-oidc-provider-no-adapter-code) for a detailed comparison with competitor approaches.

### Time to First Social Login

The fastest way to evaluate an authentication platform is to measure how quickly you go from an empty project to a working social login button. This comparison targets a Next.js application with Google social login.

| Dimension                                | Clerk                 | Auth0                        | Firebase Auth              | Supabase Auth          | Auth.js                      |
| ---------------------------------------- | --------------------- | ---------------------------- | -------------------------- | ---------------------- | ---------------------------- |
| **Account signup required**              | No (keyless mode)     | Yes                          | Yes (Firebase project)     | Yes (Supabase project) | No (but need provider creds) |
| **OAuth app at provider required (dev)** | No (shared dev creds) | default Google/GitHub        | Yes (except Google toggle) | Yes                    | Yes                          |
| **Environment variables to configure**   | 0 (keyless)           | 5                            | 4                          | 3+                     | 3                            |
| **Files to create/modify**               | 2                     | 8–11                         | 3–4                        | 3–4                    | 3–4                          |
| **Prebuilt sign-in UI**                  | Yes                   | No                           | No                         | No                     | No                           |
| **Provider console setup required**      | Not for dev           | Not for dev (Auth0 dev keys) | Google toggle only         | Full Google Console    | Full Google Console          |

**Clerk** requires exactly two files: a `proxy.ts` middleware file and a modified `app/layout.tsx` that wraps the application in `<ClerkProvider>` and adds prebuilt `<SignInButton>` and `<UserButton>` components. In [keyless mode](https://clerk.com/docs/nextjs/guides/ai/prompts.md#next-js-app-router), no account signup, no dashboard visit, and no `.env.local` file is needed to see a working sign-in flow. Shared development OAuth credentials for all 28+ built-in social providers mean Google, GitHub, Apple, and others work immediately without registering an OAuth app at each provider's console. To enable a specific social provider, the process is three clicks in the Clerk Dashboard — no code changes needed.

**Auth0** requires creating an Auth0 account, configuring an application in the dashboard (including callback URLs, logout URLs, and web origins), and building out 8–11 files including a client configuration, middleware, and custom login/logout/profile components. Five environment variables are needed, including an `AUTH0_SECRET` generated with `openssl rand -hex 32`. Auth0 does provide default Google and GitHub connections on new tenants, reducing initial OAuth app setup.

**Firebase Auth** requires creating a Firebase project, enabling Google Sign-In in the console, and implementing the sign-in flow across 3–4 files. Google Sign-In can be enabled with a single console toggle, but adding other providers requires creating OAuth apps at each provider's console. No prebuilt sign-in UI ships with the core SDK.

**Supabase Auth** requires creating a Supabase project, then creating OAuth credentials at each provider's console. For Google, this means navigating the Google Cloud Console to configure the OAuth consent screen, create an OAuth 2.0 Client ID, and set up authorized redirect URIs. The sign-in call itself is concise, but a callback route handler and custom UI must be built.

**Auth.js** has the most concise auth configuration code (\~6 lines), but the developer must still create an OAuth app at each provider's console, obtain credentials, and build all UI from scratch.

The fundamental DX difference is where each platform places the configuration burden. Clerk eliminates the two highest-friction steps: creating OAuth apps at provider consoles and building sign-in UI. Other platforms require at least one of these, and most require both.

### Auth0

Auth0 offers the widest selection of social providers (70+ in the [Auth0 Marketplace](https://marketplace.auth0.com/features/social-connections)) and an extensive marketplace of pre-built connections. Authentication happens through Universal Login, a hosted redirect-based page — embeddable components are not available. The [free tier](https://auth0.com/pricing) provides 25,000 MAU with unlimited social connections — increased from 7,500 MAU in September 2024, reflecting Auth0's ongoing investment in their free offering. Overage pricing on the B2C Essentials plan starts at $0.07/MAU.

```typescript
// Auth0 SPA SDK - trigger Google login
loginWithRedirect({ authorizationParams: { connection: 'google-oauth2' } })
```

Auth0's [in-memory token storage](https://auth0.com/docs/secure/security-guidance/data-security/token-storage) provides strong default security for SPAs. When refresh tokens are enabled (`useRefreshTokens: true`), the SDK adds Web Worker isolation for the refresh token operations.

Auth0's extensibility ecosystem is a significant differentiator. [Actions](https://auth0.com/docs/customize/actions/actions-overview) are serverless functions that execute on auth triggers (post-login, pre-registration, credential exchange, and more), with a drag-and-drop flow editor and a [Marketplace](https://marketplace.auth0.com/) of pre-built integrations. For applications requiring complex permission models beyond RBAC — document-level access, hierarchical permissions, or relationship-based authorization — Auth0 offers [Fine Grained Authorization (FGA)](https://docs.fga.dev/), an open-source system based on Google's Zanzibar paper (also available as the standalone [OpenFGA](https://openfga.dev/) project). Actions replaced the legacy Rules and Hooks systems, which became read-only in November 2024 and will stop executing in November 2026.

### Firebase Auth

Firebase Auth is tightly integrated with Google Cloud and provides 7 built-in social providers (Google, Apple, Facebook, GitHub, Microsoft, X/Twitter, and Yahoo). It uses IndexedDB for auth state persistence, which has the same XSS profile as `localStorage`. The prebuilt FirebaseUI library is functional but has limited modern framework support.

```typescript
// Firebase Auth - Google redirect sign-in
signInWithRedirect(auth, new GoogleAuthProvider())
```

Firebase offers both `signInWithPopup()` and `signInWithRedirect()` — neither is deprecated. However, `signInWithRedirect()` has [known issues on browsers that block third-party storage access](https://firebase.google.com/docs/auth/web/redirect-best-practices) (Chrome 115+, Safari 16.1+, Firefox 109+), requiring developers to update their `authDomain` configuration to match the application's serving domain. Firebase's own documentation lists `signInWithPopup()` as a workaround for these redirect issues — an ironic tension given that the redirect flow is generally more reliable across devices.

Account linking requires manual implementation — Firebase throws an error when a user attempts social login with an email that exists under a different provider.

### Supabase Auth

Supabase provides deep Postgres integration and is open-source and self-hostable. It supports 20+ social providers with automatic email-based account linking. The Auth UI component library entered [maintenance mode in February 2024 and was archived in October 2025](https://github.com/supabase-community/auth-ui), meaning new applications should build custom UIs. Per-MAU [pricing](https://supabase.com/pricing) is competitive at $0.00325/MAU beyond the 50K free tier.

Supabase's strongest value proposition is the integrated platform — auth is one piece of a full backend that includes a Postgres database, instant REST and GraphQL APIs, Edge Functions, Storage, and Realtime subscriptions, all under a single subscription and dashboard. [Row Level Security (RLS)](https://supabase.com/docs/guides/database/postgres/row-level-security) policies connect auth directly to data access at the database layer, eliminating an entire class of authorization bugs. The $25/mo Pro plan covers all of these services (with 100K MAU included), making Supabase Auth most cost-effective when adopted as part of the full Supabase platform rather than as a standalone auth service.

```typescript
// Supabase Auth - Google OAuth sign-in
supabase.auth.signInWithOAuth({ provider: 'google' })
```

One limitation: Supabase [does not manage refreshing the provider token](https://supabase.com/docs/guides/auth/social-login) after the initial sign-in — your application must handle provider token refresh separately.

### WorkOS

WorkOS offers the most generous [free tier](https://workos.com/pricing) for social login — 1 million MAU at no cost. However, its core strength is enterprise [SSO](https://clerk.com/glossary.md#single-sign-on-sso), which is priced separately at [$125/month per connection](https://workos.com/pricing) (with volume discounts starting at 16 connections). Social login is a secondary feature with 8 social providers (Apple, GitHub, Google, LinkedIn, Microsoft, GitLab, Slack, and Facebook). In early 2025, WorkOS expanded its broader authentication coverage with enterprise-oriented providers like ADP, Bitbucket, Intuit, Rippling, and Xero — these are B2B/workforce providers rather than consumer social login options. AuthKit's UI is hosted and redirect-based.

```typescript
// WorkOS - generate authorization URL
const authorizationUrl = workos.userManagement.getAuthorizationUrl({
  provider: 'authkit',
  redirectUri: 'https://example.com/callback',
  clientId: process.env.WORKOS_CLIENT_ID!,
})
```

Laravel 12 starter kits offer WorkOS AuthKit as an official authentication option alongside Laravel's built-in auth. The `authkit-nextjs` SDK supports Next.js 16 `proxy.ts`.

### Auth.js

Auth.js (formerly NextAuth.js) supports the most providers — over 80 — through community-maintained adapters. It's fully open-source and self-hosted, meaning no per-user pricing, but full infrastructure management responsibility. There is no prebuilt UI; you build your own sign-in page.

```typescript
// Auth.js - configure providers
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Google from 'next-auth/providers/google'

export const { handlers, auth } = NextAuth({ providers: [GitHub, Google] })
```

Account linking is opt-in per provider using the `allowDangerousEmailAccountLinking` flag — the name itself signals that automatic linking carries security risks without proper verification.

### Emerging Alternatives

The auth landscape continues to evolve. [Better Auth](https://github.com/better-auth/better-auth) (\~25K GitHub stars) is a TypeScript-first, framework-agnostic auth library with built-in social login and a plugin system. [Stack Auth](https://github.com/stack-auth/stack) (YC S24, \~6.7K GitHub stars) offers an open-source alternative with dashboard UI and social providers. Both are newer projects — evaluate their maturity and community support before adopting for production.

### Choosing by Constraints

Different projects have different priorities. This table maps common constraints to the auth service best suited for each:

| Constraint                      | Best Choice         | Why                                       |
| ------------------------------- | ------------------- | ----------------------------------------- |
| Maximum free tier users         | WorkOS (1M MAU)     | 20x more than next closest                |
| Most built-in social providers  | Auth0 (75+)         | Largest marketplace                       |
| Any OIDC provider, no code      | Clerk (custom OIDC) | Dashboard config only, no adapters needed |
| Self-hosted / open source       | Auth.js or Supabase | Full control, no vendor lock-in           |
| Enterprise SSO + social login   | WorkOS              | SSO-first architecture                    |
| Embeddable UI across frameworks | Clerk               | Components for 10+ frameworks             |
| Lowest per-user cost at scale   | Auth.js (free)      | No per-user pricing                       |

### Vendor Lock-in and Data Portability

Switching auth providers is one of the most disruptive migrations an application can undergo — it touches every authenticated endpoint, session management, and often the user table schema. Understanding the lock-in profile of each service before committing is critical.

| Service       | Self-host option           | User data export                                              | SDK coupling                            | Migration difficulty                                                           |
| ------------- | -------------------------- | ------------------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------ |
| Clerk         | No (hosted only)           | Yes (Dashboard CSV export with hashed passwords, Backend API) | Framework-specific SDKs                 | Moderate — webhook sync means your DB has user data copies                     |
| Auth0         | Private cloud (Enterprise) | Yes (Management API, import/export)                           | Framework-specific SDKs + Actions logic | High — Actions, Rules, and tenant config are platform-specific                 |
| Firebase Auth | No (Google Cloud hosted)   | Yes (Admin SDK)                                               | Tightly coupled to Google ecosystem     | High — Firestore rules, Cloud Functions, and auth triggers are platform-locked |
| Supabase Auth | Yes (fully self-hostable)  | Yes (direct Postgres access)                                  | Lightweight client libraries            | Low — GoTrue is open-source, user data is in your Postgres database            |
| WorkOS        | No (hosted only)           | Yes (API)                                                     | Framework-specific SDKs                 | Moderate — SSO connections may require reconfiguration with new provider       |
| Auth.js       | N/A (it is self-hosted)    | Full control (your database)                                  | Library, not a service                  | Lowest — no vendor dependency, swap adapters                                   |

All six services use standard JWTs, so token verification patterns are portable across providers. The primary migration cost is replacing SDK-specific hooks, components, and server-side helpers — not the underlying auth protocol. For teams where portability is a priority, Supabase (open-source, self-hostable) and Auth.js (library, your database) offer the lowest switching costs. Clerk's Dashboard offers a self-service [CSV export of all users including hashed passwords](https://clerk.com/docs/deployments/exporting-users.md), enabling migration to any provider that accepts password hash imports — no support ticket required. Auth0 similarly provides user import/export through its Management API.

### Pricing at Scale

At low volumes, most services are free or nearly free. The differences become meaningful as your user base grows. This table shows estimated monthly costs at common scale milestones:

| Monthly Users | Clerk (Pro)\*     | Auth0               | Firebase Auth | Supabase (Pro) | WorkOS AuthKit | Auth.js    |
| ------------- | ----------------- | ------------------- | ------------- | -------------- | -------------- | ---------- |
| **25,000**    | $0 (Hobby plan)   | $0 (Free plan)      | $0            | $25            | $0             | $0 + infra |
| **50,000**    | $0 (Hobby plan)   | Enterprise (custom) | $0            | $25            | $0             | $0 + infra |
| **100,000**   | $1,025 (Pro plan) | Enterprise (custom) | $275          | $25            | $0             | $0 + infra |
| **250,000**   | $3,725 (Pro plan) | Enterprise (custom) | $965          | $512           | $0             | $0 + infra |
| **500,000**   | $8,225 (Pro plan) | Enterprise (custom) | $2,115        | $1,325         | $0             | $0 + infra |

\* Clerk uses graduated overage pricing on the Pro plan ($25/mo monthly billing): the first 50K MRU are included, then $0.02/MRU up to 100K, $0.018/MRU up to 1M, $0.015/MRU up to 10M, and $0.012/MRU above 10M. Annual billing reduces the base to $20/mo.

**Key notes:**

- **MRU vs MAU:** Clerk's MRU metric (users who return at least one day after signup) typically yields a lower count than MAU (any user who authenticates), making the comparison not directly 1:1. For stable apps with low churn, the metrics converge closely.
- **Auth0:** The free plan covers 25K MAU. Self-serve paid B2C plans cap at \~20K MAU. Above the free tier threshold, Auth0 requires Enterprise contracts with negotiated (non-public) pricing — there is no self-serve path from 25K to Enterprise scale.
- **WorkOS:** The 1M free MAU tier is remarkably generous for social login, though WorkOS's primary revenue model is enterprise SSO connections ($125/connection/month). Social auth is effectively subsidized to drive enterprise adoption.
- **Supabase:** The $25/mo Pro plan includes 100K MAU (not just 50K), making it cost-effective at moderate scale. The plan also includes database, storage, and edge functions beyond just auth.
- **Firebase:** No base subscription — you pay only for MAUs above 50K on the Blaze (pay-as-you-go) plan, with graduated tiers ($0.0055 up to 100K, $0.0046 up to 1M).
- **Auth.js:** Free and self-hosted, but infrastructure, security patching, and operational costs are your responsibility.

### Any OIDC Provider, No Adapter Code

The provider comparison table above covers 15 common providers, but real-world applications often need to integrate with niche or industry-specific identity systems — a corporate Keycloak instance, a healthcare OIDC provider, or a regional social network. This is where provider ecosystems diverge sharply.

Clerk's [custom OIDC support](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/custom-provider.md), [launched in August 2024](https://clerk.com/changelog/2024-08-20-custom-oauth-providers.md), lets you add any OIDC-compliant provider as a social connection through the Dashboard — no adapter code, no SDK plugin, no deployment required. You enter the discovery endpoint (or manually configure endpoints), provide a client ID and secret, and optionally map non-standard claims to Clerk's user profile fields. [PKCE is supported](https://clerk.com/changelog/2025-11-12-pkce-support-custom-oauth.md) for custom providers as of November 2025. For providers with non-standard user info responses, Clerk supports attribute mapping in the Dashboard and, for more complex cases, a lightweight proxy pattern.

The difference matters in practice. Auth0 requires writing a custom JavaScript `fetchUserProfile` script for each unsupported provider — and for some providers, a separate backend proxy as well. Firebase Auth gates custom OIDC behind an [Identity Platform upgrade](https://firebase.google.com/docs/auth/web/openid-connect) that changes the pricing model (the free tier drops from 50K MAU to 3K DAU). Supabase still lacks native support for arbitrary OIDC providers entirely — a feature [requested since 2022](https://github.com/orgs/supabase/discussions/6547) — forcing workarounds like routing through a Keycloak proxy. Auth.js supports custom providers through code configuration, which is straightforward but requires a code change and deployment for each new provider rather than a Dashboard toggle.

On Clerk's Pro plan ($20/month billed annually), custom OIDC providers are unlimited alongside all other social connections. On the free tier, custom providers count against the 3-provider social connection limit.

### Why Clerk for Social Login

Clerk differentiates on five fronts: custom OIDC support for any standards-compliant provider without adapter code (see [above](#any-oidc-provider-no-adapter-code)), pre-built embeddable UI components across 10+ frameworks (where most competitors use hosted redirect pages), automatic account linking with verified email enforcement (no custom code required), a hybrid token architecture that combines stateful revocation with stateless JWT verification, and native mobile SDKs for [Expo](https://clerk.com/docs/quickstarts/expo.md), [iOS (Swift)](https://clerk.com/docs/ios/getting-started/quickstart.md), [Android (Kotlin)](https://clerk.com/docs/android/getting-started/quickstart.md), and [Flutter](https://github.com/clerk/clerk-sdk-flutter) (beta). The consistent SDK pattern — the same `<SignIn />` component and `useSignIn()` hook across web frameworks, plus platform-native authentication views on mobile — reduces the cost of switching or supporting multiple platforms.

**Known limitations and considerations:** Clerk is a hosted-only authentication service with no self-hosted deployment option — organizations requiring on-premises or air-gapped deployments should evaluate alternatives like Auth.js or Supabase. Compliance certifications including [SOC2](https://clerk.com/glossary.md#soc-2) reports and [HIPAA](https://clerk.com/glossary.md#health-insurance-portability-accountability-act-hipaa) compliance are available exclusively on the [Business and Enterprise plans](https://clerk.com/pricing) — teams on the Hobby or Pro tiers do not have access to these compliance artifacts. Because Clerk serves as the source of truth for user identity, applications that need user data in their own database must implement synchronization, typically through [webhooks](https://clerk.com/glossary.md#webhook) ([sync guide](https://clerk.com/docs/guides/development/webhooks/syncing.md)). Clerk provides comprehensive guidance on this pattern, including a detailed [data sync guide](https://clerk.com/articles/how-to-sync-clerk-user-data-to-your-database.md) covering PostgreSQL schemas, idempotent handlers, and retry strategies. Note that webhook-based sync is eventually consistent — there is a brief delay between a Clerk event and its reflection in your database.

**A note on independent validation:** No independent third-party security audit of Clerk's specific account linking implementation is publicly available as of February 2026. The USENIX 2022 pre-hijacking paper validates the general approach (verified email enforcement) that Clerk uses, but has not specifically audited Clerk's implementation.

## Troubleshooting Common Integration Errors

Most social login errors fall into three categories: configuration mismatches, security mechanism failures, and provider-specific quirks. The difference between a frustrating debugging session and a quick fix usually comes down to knowing which category you're dealing with.

### Provider Setup Checklist

Before debugging errors, verify these configuration basics for each provider:

- **App ID / Client ID and Client Secret**: Correctly entered in your auth service dashboard
- **Redirect URIs**: Exact string match including protocol, domain, port, and path (no trailing slash mismatch)
- **Apple-specific**: Key ID, Team ID, Services ID, and the ES256 `.p8` private key
- **Dev/staging/prod separation**: Separate OAuth app registrations or separate redirect URIs per environment
- **Verification status**: Google consent screen published (not in "testing" mode), Apple domain verified

### Common Errors and Fixes

**1. `redirect_uri_mismatch`**

The most common social login error. The redirect URI in your authorization request must be an exact string match with the URI registered in the provider's console — including protocol (`https` vs `http`), domain, port, path, and trailing slashes. A single character difference causes this error.

**2. State mismatch / CSRF error**

The `state` cookie was lost during the OAuth redirect. This typically happens when the state is stored in a cookie with `SameSite=Strict`, which blocks the cookie from being sent on cross-origin redirects. Fix: use `SameSite=Lax` for OAuth state cookies.

**3. CORS errors on OAuth endpoints**

OAuth uses browser redirects, not API calls. You cannot call an authorization or token endpoint via `fetch()` or `XMLHttpRequest`. If you're seeing CORS errors, your code is attempting to call these endpoints from the browser — use a server-side token exchange instead.

**4. PKCE `code_verifier` mismatch**

The code verifier was not persisted across the redirect. This happens when the session or storage containing the verifier expires during the OAuth flow. Note: storing the PKCE verifier in `sessionStorage` is acceptable — it's a short-lived, single-use cryptographic challenge, not an access token or refresh token.

**5. Popup blocked by browser**

Any asynchronous operation before `window.open()` — such as an API call — triggers the browser's popup blocker. Use the redirect flow instead. Redirect flows are more reliable and work consistently across all browsers and devices.

**6. Apple: authorization code expired**

[Apple's authorization codes](https://developer.apple.com/documentation/signinwithapplerestapi) are single-use and expire in 5 minutes (see [TN3107](https://developer.apple.com/documentation/technotes/tn3107-resolving-sign-in-with-apple-response-errors) for common Sign in with Apple error resolution). Your backend must exchange the code immediately upon receiving it. If you're queuing the exchange or the callback route has latency, the code may expire before the token request.

**7. Google: "This app isn't verified" warning**

Your app is still in "Testing" mode on the Google Cloud Console, which limits access to 100 test users. For production launch, set the app to "In production" status and complete the consent screen verification process.

**8. Account linking conflicts**

The "email already in use" error occurs when a user signs in with a new social provider using an email that exists under a different authentication method. Clerk handles this automatically through verified email-based linking. For other services, implement the appropriate conflict resolution flow for your chosen auth provider.

## Frequently Asked Questions

## FAQ

### What is social login and how does it work?

Social login uses the OAuth 2.0 Authorization Code flow with OIDC for identity verification. The user is redirected to an identity provider (Google, GitHub, Apple), authenticates there, and is redirected back to your application with an authorization code. Your backend exchanges this code for tokens containing verified identity information.

### Which social login providers should I support?

Start with Google — it accounts for approximately 75% of social logins according to the [Okta/Auth0 2023 report](https://www.okta.com/sites/default/files/2023-06/GoingDeepwithSocialLogin-Whitepaper-20230601-Final.pdf). Add GitHub for developer-facing apps, Apple for iOS apps or when a privacy-focused option is needed. Limit to 2–3 providers to avoid UX confusion.

### Is social login secure?

Yes, when implemented correctly. The Authorization Code flow with PKCE, state parameter validation, and proper token storage (HttpOnly cookies) is recommended by the [OAuth 2.0 Security BCP (RFC 9700)](https://datatracker.ietf.org/doc/html/rfc9700). Social login can be more secure than password-based auth because it eliminates the risk of [credential stuffing](https://clerk.com/glossary.md#credential-stuffing) and weak passwords.

### What happens when a user signs in with different social providers using the same email?

This triggers account linking. Best practice: automatically link accounts when both the existing and incoming emails are verified (as Clerk, Supabase, and WorkOS do). Auth0 takes a manual-by-default approach, giving developers explicit control over linking policy. Firebase throws an error requiring developers to implement linking logic. Manual linking is required when emails differ across providers.

### Where should I store authentication tokens in a web app?

There are two layers to consider. **Provider tokens** (the access token and refresh token issued by Google, GitHub, etc.) should never reach the browser — the recommended BFF pattern keeps them server-side. **Application session credentials** (the tokens or cookies your auth service issues to keep the user logged in) should prefer HttpOnly cookies, as recommended by both OWASP and the IETF. Some managed providers use a hybrid model — pairing an HttpOnly cookie with a short-lived, client-readable token for edge/client use — which is acceptable when the client-readable token has a brief TTL and is not a long-lived secret. Never store session credentials in localStorage.

### Do I need PKCE for server-side web apps?

Yes. The OAuth 2.0 Security Best Current Practice (RFC 9700) requires PKCE for public clients and recommends it for confidential (server-side) clients as well. PKCE prevents authorization code interception attacks and is already required by some providers (e.g., X mandates PKCE for all OAuth 2.0 flows). Clerk, Auth0, and most managed auth services implement PKCE automatically.

### What is the difference between OAuth 2.0 and OpenID Connect?

OAuth 2.0 is an authorization framework — it grants your app access to resources. OIDC adds an authentication layer on top, providing a standardized ID token (JWT) with identity claims like `sub`, `email`, and `name`. For social login, you use both: OAuth 2.0 for the flow, OIDC for the identity.

### How do I handle Apple's Hide My Email in social login?

Apple generates unique `@privaterelay.appleid.com` addresses per user per app. These relay addresses won't match existing account emails, so automatic linking won't work for users who hide their email. Store Apple's stable `sub` identifier as the primary key and allow users to add their real email later via profile settings (Clerk provides this through the `<UserProfile />` component).

### Can I add social login to an existing app without rebuilding authentication?

Yes. Managed services provide different integration approaches: Clerk offers drop-in `<SignIn />` components across Next.js, React, Vue, Astro, and TanStack Start. Auth0 uses hosted Universal Login (redirect-based). Supabase provides `signInWithOAuth()`. Auth.js requires building your own UI but handles the OAuth flow. Each requires minimal code — typically a provider wrapper and configuration.

### What is the cheapest way to add social login?

Auth.js is free (self-hosted, infrastructure costs only). For managed services: [WorkOS](https://workos.com/pricing) offers 1 million MAU free, [Clerk](https://clerk.com/pricing)/[Supabase](https://supabase.com/pricing)/[Firebase](https://firebase.google.com/pricing) provide 50K MAU/MRU free tiers, and [Auth0](https://auth0.com/pricing) provides 25K MAU free. Each has different tradeoffs in features, UI components, and developer experience.

***

_All pricing information in this article is accurate as of February 23rd, 2026. Pricing for all services mentioned may change — verify current rates on each provider's pricing page before making purchasing decisions._
