/
Contact usSee pricingStart building
Node
​

    About Stytch

    Introduction
    Integration Approaches
      Full-stack overview
      Frontend (pre-built UI)
      Frontend (headless)
      Backend
    Migrations
      Migration overview
      Migrating users statically
      Migrating users dynamically
      Additional migration considerations
      Zero-downtime deployment
      Defining external IDs for users
      Exporting from Stytch
    Custom Domains
      Overview

    Authentication

    DFP Protected Auth
      Overview
      Setting up DFP Protected Auth
      Handling challenges
    Magic Links
    • Email Magic Links

      • Getting started with the API
        Getting started with the SDK
        Replacing your password reset flow
        Building an invite user flow
        Add magic links to an existing auth flow
        Adding PKCE to a Magic Link flow
        Magic Link redirect routing
    • Embeddable Magic Links

      • Getting started with the API
    MFA
      Overview
      Backend integration
      Frontend integration
    Mobile Biometrics
      Overview
    M2M Authentication
      Authenticate an M2M Client
      Rotate client secrets
      Import M2M Clients from Auth0
    OAuth
    • Identity providers

      • Overview
        Provider setup
      Getting started with the API (Google)
      Add Google One Tap via the SDK
      Email address behavior
      Adding PKCE to an OAuth flow
    Connected AppsBeta
      Setting up Connected Apps
      About Remote MCP Servers
    • Resources

      • Integrate with AI agents
        Integrate with MCP servers
        Integrate with CLI Apps
    Passcodes
      Getting started with the API
      Getting started with the SDK
    • Toll fraud

      • What is SMS toll fraud?
        How you can prevent toll fraud
      Unsupported countries
    Passkeys & WebAuthn
    • Passkeys

      • Passkeys overview
        Set up Passkeys with the frontend SDK
    • WebAuthn

      • Getting started with the API
        Getting started with the SDK
    Passwords
      Getting started with the API
      Getting started with the SDK
      Password strength policy
    • Email verification

      • Overview
        Email verification before password creation
        Email verification after password creation
    Sessions
      How to use sessions
      Backend integrations
      Frontend integrations
      Custom claims
      Custom claim templates
      Session tokens vs JWTs
      How to use Stytch JWTs
    TOTP
      Getting started with the API
      Getting started with the SDK
    Web3
      Getting started with the API
      Getting started with the SDK

    Authorization

    Implement RBAC with metadata

    3rd Party Integrations

    Planetscale
    Supabase
    Feathery
    Unit

    Testing

    E2E testing
    Sandbox values
Get support on SlackVisit our developer forum

Contact us

Consumer Authentication

/

Guides

/

Authentication

/

OAuth

/

Adding PKCE to an OAuth flow

Adding PKCE to an OAuth flow

Proof Key for Code Exchange (PKCE) is a specification that builds on OAuth 2.0 to create a more secure login flow.

In the original OAuth authorization code flow, an IdP issues a one-time use code to the app, which is exchanged along with a client_id and client_secret for an auth token. For certain types of apps (i.e. SPAs, mobile apps, or native apps) there was no mechanism to hide the client_secret, which must be included in the app's source code and reused on every request. This meant that an attacker would be able to impersonate a user if the attacker gained access to the one-time use code. Codes in a URL can be leaked in many different ways - either through improper logging, HTTP Referrer headers, or even through another exploit like XSS.

PKCE introduces a one-time secret for each authorization flow. This secret, called a code_verifier, is generated and stored by the app on the user's device. Before the user is redirected to the IdP, the verifier is hashed to produce a code_challenge. This challenge is passed as a query parameter during the authentication flow, e.g. /oauth/authenticate?code_challenge=E9Mel....

After the IdP has validated the user's credentials, the user is redirected back to the app with a secure code. Now, the app must submit the code along with the original code_verifier. The IdP hashes the submitted code_verifier and validates that it matches the code_challenge passed as a query parameter earlier. This check ensures that the token has not been leaked, and that it is being handled by the same user that was seen at the start of the flow.

This guide will show you how to add PKCE support to your Stytch OAuth integration for added security. A PKCE integration is required for applications that use native callback URLs, for example appname://some/callback on mobile. Stytch's support for PKCE differs slightly from the original specification - for example we do not permit plain verifiers to be used - but the original idea of strengthening app security with a unique secret on every request remains the same.

If you get stuck while working through this guide, feel free to ask questions in our forum, via support@stytch.com, or in our Slack community.


Choose integration method

JavaScript SDK
​

Step 1: Enable PKCE for your app

Enable PKCE for all OAuth logins performed by the JavaScript SDK. By flipping this setting, you tell the Javascript SDK to use PKCE for all UI components, and to reject any OAuth logins that do not use PKCE. In order to do so, toggle the option in the SDK Configuration page of the Stytch dashboard. This makes your application resistant to attacks like Login CSRF in which an attacker can trick a victim into logging in to the wrong account.

Note: The legacy oauth.$provider.getURL() method is not compatible with PKCE. If you are using this method already in your codebase, complete Step 2 before enabling PKCE.

Step 2: Start the OAuth flow

Call one of the oauth.$provider.start() methods to start the OAuth flow. The SDK will generate a code_verifier and store it in local storage before redirecting the user to Stytch's OAuth start endpoint. If you use the SDK's built in OAuth UI this step is already handled for you.

function OAuthStartButton() {
  const stytch = useStytch();
  const startOAuthFlow = useCallback(() => stytch.oauth.google.start({
    login_redirect_url: 'https://example.com/callback',
    signup_redirect_url: 'https://example.com/callback'
  }), [stytch])

  return (
    <button onClick={startOAuthFlow}>
      Click here to log in with Google
    </button>
  );   
}

Step 3: Authenticate the OAuth token

At the route that the user is redirected to after the OAuth flow, pull the token out of the URL params and call oauth.authenticate(token) to exchange the token for a Stytch session. The SDK will automatically retrieve the code_verifier created earlier from local storage and pass it on to the Stytch servers.

function OAuthCallback() {
  const stytch = useStytch();
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const token = params.get('token');
    if(!token) {
      // TODO: Handle redirect back to logged-out view 
    }
    stytch.oauth.authenticate(token, {
      session_duration_minutes: 60,
    }).then(() => {
      // TODO: Redirect to final logged-in view
    })
  }, [stytch]);

  return (
    <div>
      Authenticating....
    </div>
  );   
}

Step 1: Enable PKCE for your app

Step 2: Start the OAuth flow

Step 3: Authenticate the OAuth token