Skip to main content
Articles

How do I implement social login for my web app?

Author: Roy Anger
Published:

Social login lets users sign in to your web application using credentials they already have — a Google account, a GitHub identity, or an Apple ID, among others — instead of creating yet another username and password. For developers, implementing social login means integrating with OAuth 2.0 and OpenID Connect (OIDC) protocols, choosing the right identity providers, and handling the security concerns that come with delegated authentication.

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

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 (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 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) is the authorization framework that powers social login. For web and mobile applications, the recommended implementation is the Authorization Code flow with 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) 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, 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)) is a thin identity layer built on top of OAuth 2.0. It adds a standardized ID token, a signed JWT 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:

ClaimDescription
subUnique, stable user identifier
emailUser's email address
email_verifiedWhether the provider has confirmed this email
nameFull display name
pictureProfile photo URL

The standard OIDC scopes that control which claims are returned:

ScopeReturns
openidRequired for OIDC; returns sub
emailReturns email and email_verified
profileReturns 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 mandatory for all clients and formally removing the implicit flow. For a deeper dive into the protocols, see the OAuth 2.0 specification and the OpenID Connect Core spec.

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 25+ social providers with custom OIDC support for any provider not listed. Other auth services vary in coverage; see the auth service support matrix below.

Provider Comparison

ProviderScopes for LoginSetup ComplexityOIDCUnique Considerations
Googleopenid email profileLow (free, Google Cloud Console)YesFedCM mandatory for GIS since Aug 2025, One Tap available, 100-user testing limit
GitHubuser:emailLow (free, Developer Settings)NoNo ID token, single callback URL per OAuth app, classic tokens don't expire
Applename emailHigh ($99/yr, ES256 client secret JWT)PartialName only on first auth, Hide My Email relay, response_mode=form_post required
Microsoftopenid profile emailLow (free, Microsoft Entra admin center)YesMulti-tenant config required, add xms_edov optional claim for verified email
Facebookpublic_profile, emailLow (free, Meta Developer Portal)Partialpublic_profile auto-granted, App Review required for advanced scopes only
Discordidentify, emailLow (free, Discord Developer Portal)PartialNo full OIDC discovery endpoint, popular for gaming/community apps
LinkedInopenid profile emailLow (free, LinkedIn Developer Portal)YesMigrated to standard OIDC in 2023, pairwise subject identifiers
GitLabopenid profile emailLow (free, works with self-hosted instances)YesGroup/role claims available, supports self-hosted GitLab
Slackopenid email profileLow (free, Slack API portal)Yes"Sign in with Slack" (OIDC) is separate from "Add to Slack" (bot install)
Twitchopenid, user:read:emailLow (free, Twitch Developer Console)YesEmail requires explicit claims parameter in auth request
X (Twitter)users.read, users.emailLow (free tier supports OAuth login)NoEmail available via users.email scope (since April 2025), PKCE mandatory, "Request email from users" must be enabled in Developer dashboard
Coinbasewallet:user:read, wallet:user:emailLimited (new apps require partner approval)NoWeb3/crypto niche, scopes use comma separation
LinearreadLow (free, Linear settings)NoGraphQL-only API for user data, project management niche
NotionCapability-basedLow (free, Notion integrations portal)NoPage-level access consent, not traditional scope-based auth
Vercelopenid email profileLow (free, Vercel dashboard)YesDeveloper-focused, relatively new feature

Google

Google is the recommended starting provider. According to the Okta/Auth0 "Going Deep with Social Login" report (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 reported a 90% desktop signup uplift
  • Pinterest saw a 47% improvement on web and mobile web

Google's Federated Credential Management (FedCM) API became mandatory 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 /> 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 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. (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, 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.

One important limitation: GitHub OAuth Apps support only a single callback URL 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 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 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 from tokens for multi-tenant apps by default (see the breaking changes documentation for details on this policy change). To get verified email status, add the xms_edov optional claim in your app's Token Configuration. This boolean indicates whether the email domain owner has been verified. Clerk's Microsoft configuration guide walks through this setup and recommends enabling xms_edov to protect against nOAuth 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. 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 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. 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 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" 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 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, 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 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 — 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 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 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.

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.

ProviderClerkAuth0Firebase AuthSupabase AuthWorkOSAuth.js
Google
GitHub
Apple
Microsoft
Facebook
Discord
LinkedIn
GitLabCustom
Slack
Twitch
X (Twitter)
CoinbaseCustom
Linear
Notion
VercelCustom

Clerk supports all 15 providers listed above as built-in social connections, plus additional providers. See the full list in the Social connections overview.

Provider Decision Tree

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

App TypeRecommended ProvidersReasoning
Consumer web appGoogle + AppleCovers the broadest user base; Apple required if you have an iOS companion app
Developer tools / SaaSGoogle + GitHubGitHub identity is the standard for developer audiences; add GitLab for self-hosted teams
Enterprise / B2BGoogle + MicrosoftCorporate Google Workspace and Azure AD/Entra ID accounts
Global consumer appGoogle + Apple + FacebookFacebook remains dominant in non-US markets
Gaming / communityGoogle + DiscordDiscord is the dominant identity for gaming and creator communities
Streaming / creatorGoogle + TwitchTwitch identity for streaming platforms; add Discord for community features
Professional / recruitingGoogle + LinkedInLinkedIn for professional identity and B2B networking apps
Workplace toolsGoogle + SlackSlack for apps used alongside existing Slack workspaces
Web3 / cryptoGoogle + CoinbaseCoinbase for crypto-native audiences (note: new OAuth apps currently suspended)
Project managementGoogle + Linear or NotionLinear 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" — a wall of provider buttons that creates decision paralysis rather than convenience. This follows Hick's 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 free tier includes 3 social providers with 50,000 Monthly Retained Users (MRU). 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, or 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, and 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 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 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 (which replaces and deprecates middleware.ts). The Clerk Next.js SDK integrates with this new pattern.

Install the SDK:

npm install @clerk/nextjs

Set up the environment variables in .env.local:

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...

Add ClerkProvider to your root layout to make authentication state available throughout the app:

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

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

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

// 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:

// 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/clerk-react package provides the same components and hooks without server-side dependencies.

npm install @clerk/clerk-react

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

// src/main.tsx
import { ClerkProvider } from '@clerk/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:

import { SignIn } from '@clerk/clerk-react'

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

Vue

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

npm install @clerk/vue

Register the Clerk plugin in your Vue app:

// 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:

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

<template>
  <SignIn />
</template>

Note: For Vue SSR with Nuxt, Clerk provides @clerk/nuxt. 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 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.

npm install @clerk/astro

Add the Clerk integration to your Astro config:

// 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):

// 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:

---
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).

npm install @clerk/react-router

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

// 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:

// 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:

// 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.

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:

// 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, it may receive breaking changes before reaching v1.0.

Cross-Framework Pattern Summary

FrameworkPackageProvider SetupMiddlewareSignIn ImportSecret Key Required
Next.js 16@clerk/nextjsClerkProviderproxy.ts@clerk/nextjsYes
React (SPA)@clerk/clerk-reactClerkProviderNone@clerk/clerk-reactNo
Vue@clerk/vueclerkPluginNone@clerk/vueNo
Astro@clerk/astroclerk() integrationsrc/middleware.ts@clerk/astro/componentsYes
React Router@clerk/react-routerClerkProviderclerkMiddleware()@clerk/react-routerYes
TanStack Start@clerk/tanstack-react-startClerkProvidersrc/start.ts@clerk/tanstack-react-startYes

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.

PlatformPackageUI ComponentsSocial Login PatternStatus
React Native / Expo@clerk/clerk-expoControl components (native); UI components (Expo web)Native Apple Sign-in via useSignInWithApple(); browser-based OAuth for other providersGA
iOS (Swift)ClerkKit + ClerkKitUIAuthView (prebuilt sign-in/sign-up)Native Apple Sign-in; browser-based OAuth for Google and othersGA
Android (Kotlin)clerk-android-api + clerk-android-uiPrebuilt sign-in/sign-up viewsNative Google Sign-in; browser-based OAuth for othersGA (Sept 2025)
Flutter (Dart)clerk_flutter + clerk_authIn developmentSocial login supportedBeta

Expo (React Native): The @clerk/clerk-expo package provides the same useSignIn() hook pattern as the web SDKs. For Apple, the dedicated useSignInWithApple() hook uses Apple's native authorization to provide the OpenID token directly to Clerk — no browser redirect needed.

// Expo - Native Apple Sign-in
import { useSignInWithApple } from '@clerk/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 uses ClerkKit and ClerkKitUI via Swift Package Manager. The prebuilt AuthView component handles the complete sign-in/sign-up flow including social providers.

// 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 provides native Google Sign-in support and prebuilt authentication views.

// 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, built with Kotlin and following modern Android development standards including Jetpack Compose support.

Flutter (Dart): The official Clerk Flutter SDK entered public beta in March 2025 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.

// 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 saw a 90% increase in desktop sign-ups
  • 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 /> in your root layout and it appears on every page:

// 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:

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

MechanismPreventsHow It Works
State parameterCSRF attacksA cryptographically random value generated before the redirect, validated on callback. Ensures the callback originated from your app's authorization request.
NonceToken replay attacksA random value embedded in the ID token request, verified after token receipt. Ensures the ID token was issued for this specific authentication attempt.
PKCEAuthorization code interceptionA 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) was originally designed for mobile and public clients. The OAuth 2.0 Security Best Current Practice (RFC 9700) 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 on November 12, 2025. Per OAuth 2.0 (RFC 6749 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).

ConcernRedirect FlowPopup Flow
Mobile browsersWorks on all mobile browsersBlocked by default on most mobile browsers (iOS Safari, Android Chrome)
Popup blockersNot affectedBlocked when any async operation (API call, analytics event, state setup) runs before window.open()
PWAs and WebViewsWorks nativelyBreaks in PWAs, Electron, React Native WebViews, and in-app browsers (Instagram, TikTok, Facebook)
Cross-origin communicationNot needed — callback is a standard HTTP redirectRequires fragile postMessage between popup and opener window
Tab/window managementSingle tab throughoutUsers may close the popup thinking it's an ad, or the opener tab may be garbage-collected by the browser
Third-party cookie restrictionsUnaffected — uses first-party redirectsMay fail in Safari and Firefox with strict third-party cookie blocking, since the popup is a separate browsing context
Server-side token exchangeNatural — the callback hits your server directlyRequires the popup to relay the authorization code back to the opener, adding complexity and attack surface
FedCM compatibilityCoexists 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:

// 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:

// 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:

// 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
  • 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) 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). 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 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 /> component by adding alternative email addresses. See the account linking guide 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" 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

ProviderLinking ApproachDeveloper Effort
ClerkAutomatic with verified emailNone — handled by SDK
Auth0Manual by default; requires custom Actions for automaticHigh — but intentional: gives developers full control over linking security policy
FirebaseThrows account-exists-with-different-credential errorHigh — catch error, prompt user, call linkWithCredential()
SupabaseAutomatic with verified emailLow — similar to Clerk
WorkOSAutomatic with verified emailNone — 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, or email magic links)
  • Allow users to link multiple social providers to their account so they have alternatives
  • Clerk's <UserProfile /> 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.

MechanismXSS SafeCSRF SafePersistsRecommended By
HttpOnly CookieYesMitigated (SameSite)YesOWASP, IETF
localStorageNoYesYesNot recommended
sessionStorageNoYesNoNot recommended
IndexedDBNoYesYesNot recommended
In-MemoryPartialYesNoAuth0 (for SPAs)

The consensus from OWASP and the IETF Browser-Based Apps BCP 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 (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 ("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 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 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 enabled, sessions restore silently after page refreshes. When useRefreshTokens: true is enabled with the default in-memory cache, the SDK uses a Web Worker 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 (AES-256-CBC + SHA-256 HMAC via iron-webcrypto). WorkOS-issued access tokens have a 5-minute TTL; 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 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) 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 and 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

FeatureClerkAuth0Firebase AuthSupabase AuthWorkOSAuth.js
Social Providers25 built-in + any OIDC provider**70+ (Marketplace)**720+8 social + enterprise providers80+ (community)**
Free Tier*50K MRU, 3 social25K MAU, unlimited social50K MAU50K MAU1M MAUUnlimited (self-hosted)
Prebuilt UIEmbeddable componentsHosted redirect (Universal Login)FirebaseUIArchived (Feb 2024 maintenance, Oct 2025 archived)Hosted redirect (AuthKit)None
Account LinkingAutomatic (verified email)Manual (API/Actions)Manual (SDK)Automatic (verified email)Automatic (verified email)Opt-in per provider
Framework SDKsNext.js, React, React Router, Vue, Astro, TanStack, ExpoAny (redirect-based)Any (SDK)Any (SDK)Next.js, Remix, TanStack, Node, Python, Ruby, GoNext.js, SvelteKit, Qwik, Express, SolidStart
Hosted / Self-hostedHostedHostedHostedHosted (self-hostable)HostedSelf-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 custom OIDC 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 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.

DimensionClerkAuth0Firebase AuthSupabase AuthAuth.js
Account signup requiredNo (keyless mode)YesYes (Firebase project)Yes (Supabase project)No (but need provider creds)
OAuth app at provider required (dev)No (shared dev creds)Partial (default Google/GitHub)Yes (except Google toggle)YesYes
Environment variables to configure0 (keyless)543+3
Files to create/modify28–113–43–43–4
Prebuilt sign-in UIYesNo (build your own)No (FirebaseUI separate)NoNo
Provider console setup requiredNot for devNot for dev (Auth0 dev keys)Google toggle onlyFull Google ConsoleFull 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, 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 30+ 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) 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 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.

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

Auth0's in-memory 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 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 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), an open-source system based on Google's Zanzibar paper (also available as the standalone OpenFGA 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.

// 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 (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, meaning new applications should build custom UIs. Per-MAU 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) 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.

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

One limitation: Supabase does not manage refreshing the provider token after the initial sign-in — your application must handle provider token refresh separately.

WorkOS

WorkOS offers the most generous free tier for social login — 1 million MAU at no cost. However, its core strength is enterprise SSO, which is priced separately at $125/month per connection (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.

// 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.

// 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 (~25K GitHub stars) is a TypeScript-first, framework-agnostic auth library with built-in social login and a plugin system. Stack Auth (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:

ConstraintBest ChoiceWhy
Maximum free tier usersWorkOS (1M MAU)20x more than next closest
Most built-in social providersAuth0 (75+)Largest marketplace
Any OIDC provider, no codeClerk (custom OIDC)Dashboard config only, no adapters needed
Self-hosted / open sourceAuth.js or SupabaseFull control, no vendor lock-in
Enterprise SSO + social loginWorkOSSSO-first architecture
Embeddable UI across frameworksClerkComponents for 10+ frameworks
Lowest per-user cost at scaleAuth.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.

ServiceSelf-host optionUser data exportSDK couplingMigration difficulty
ClerkNo (hosted only)Yes (Dashboard CSV export with hashed passwords, Backend API)Framework-specific SDKsModerate — webhook sync means your DB has user data copies
Auth0Private cloud (Enterprise)Yes (Management API, import/export)Framework-specific SDKs + Actions logicHigh — Actions, Rules, and tenant config are platform-specific
Firebase AuthNo (Google Cloud hosted)Yes (Admin SDK)Tightly coupled to Google ecosystemHigh — Firestore rules, Cloud Functions, and auth triggers are platform-locked
Supabase AuthYes (fully self-hostable)Yes (direct Postgres access)Lightweight client librariesLow — GoTrue is open-source, user data is in your Postgres database
WorkOSNo (hosted only)Yes (API)Framework-specific SDKsModerate — SSO connections may require reconfiguration with new provider
Auth.jsN/A (it is self-hosted)Full control (your database)Library, not a serviceLowest — 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, 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 UsersClerk (Pro)*Auth0Firebase AuthSupabase (Pro)WorkOS AuthKitAuth.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, launched in August 2024, 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 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 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 — 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), 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, iOS (Swift), Android (Kotlin), and 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 reports and HIPAA compliance are available exclusively on the Business and Enterprise plans — 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. Clerk provides comprehensive guidance on this pattern, including a detailed data sync guide 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 are single-use and expire in 5 minutes (see TN3107 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


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.