/
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
      Migrating from Stytch Consumer to B2B
      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
      Remembered device flow
    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
      Client types
      OAuth scopes
    • Integration Guides

      • Integrate with AI agents
        Integrate with MCP servers deployed on Cloudflare
        Integrate with MCP servers on Vercel
        Integrate with CLI Apps
    • Resources

      • About Remote MCP Servers
        Consent Management
    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
    Trusted Auth Tokens
      How to use Trusted Auth Tokens

    RBAC

    Resources
      Overview
      Role assignment
    Integration Guides
      Start here
      Backend integration
      Headless frontend integration
      (Legacy) 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

/

Connected Apps

/

OAuth scopes

Understanding OAuth Scopes in Stytch Connected Apps

During an OAuth flow, a Connected App requests authorization from a user via the scope parameter - a space-delimited list of capabilities that Connected App wishes to be granted. Clients should request access tokens with the minimal set of capabilities required for that client to function. For example, a client may need "read" access to a user's resources, but doesn't need to update resources. This client should request only read-only access, and obtain an access token that cannot be used to update resources.

A Connected App Client can request a token with the scope of openid profile read:data as shown:

const scopes = ['openid', 'profile', 'read:data']
const params = new URLSearchParams({
  client_id: 'connected-app-test-d731954d-dab3-4a2b-bdee-07f3ad1be888',
  redirect_uri: 'https://app.example/oauth/callback',
  response_type: 'code',
  scope: scopes.join(' '), // space-delimited string
  state: 'some-state-value',
});

// Pass these params to the Authorization URL page in your application 
window.location.href = `https://idp.example/oauth2/authorize?${params.toString()}`;

Access Tokens granted to Stytch Connected Apps will contain a scope field representing the capabilities granted to the token.

Built-in Scopes

Built-in scopes are preconfigured scopes within the Stytch platform.

Built-in OIDC Scopes

The Stytch platform supports all scopes defined by the OpenID Connect Core specification with no prior configuration required.

These scopes control whether an ID Token is granted to the Connected App, and what claims are present in the ID Token and in the UserInfo API response.

NameEffect when granted
openidAn OIDC id_token will be returned from the /oauth2/token endpoint
emailThe email and email_verified claims will be returned in the id_token
phoneThe phone_number and phone_number_verified claims will be returned in the id_token
profileThe name, given_name, middle_name, family_name, picture, and locale claims will be returned in the id_token

The Stytch platform also supports scopes defined for Offline Access of user data.

NameEffect when granted
offline_accessA refresh_token will be returned from the /oauth2/token endpoint for use in subsequent refresh token flows

Built-in Stytch Platform Scopes

The Stytch platform supports additional scopes used for specific actions in the Stytch API. First Party OAuth clients can request the full_access scope to enable access to the Exchange Access Token endpoint.

NameEffect when granted
full_accessEnables the Exchange Access Token API call

Custom Scopes

Your application can additionally define custom scopes tied to your RBAC (Role-Based Access Control) system. Create custom scopes to support the different use-cases

Defining Custom Scopes

Your application's RBAC policy is managed in the Stytch RBAC dashboard. Before defining custom scopes, you must define Resources for those scopes to act on. Each resource has a set of actions that can be performed upon that resource. Scopes are then composed of permissions, where each permission is an action applied to a resource.

For example, given the resources of:

[
  {
    'resource': 'images',
    'actions': ['read', 'write', 'delete']
  },
  {
    'resource': 'documents',
    'actions': ['read', 'write', 'comment', 'delete']
  }
];

You could define the corresponding scopes:

[
  {
    'scope': 'read:data',
    'description': 'Read access to company data',
    'permissions': [
      { 'resource': 'documents', 'actions': ['read'] },
      { 'resource': 'images', 'actions': ['read'] }
    ]
  },
  {
    'scope': 'write:data',
    'description': 'Write access to company data',
    'permissions': [
      { 'resource': 'documents', 'actions': ['write'] },
      { 'resource': 'images', 'actions': ['write'] }
    ]
  },
  {
    'scope': 'data:*',
    'description': 'Full access to company data',
    'permissions': [
      { 'resource': 'documents', 'actions': ['*'] },
      { 'resource': 'images', 'actions': ['*'] }
    ]
  }
];

Stytch supports flexible naming conventions for scopes (e.g., readonly:content, read:*, and content:readonly are all allowed).


What Scopes Can be Granted?

The end user authorizing the application can only grant scopes that correspond to permissions they themselves possess:

  • Permissions can be spread across multiple roles.
  • Grantable scopes are determined by checking if the union of the user's roles covers all required permissions.

Example:

  • Scope write:data requires write permission on both documents and images

  • End User Roles:

    • document_manager ➔ grants write on documents
    • image_uploader ➔ grants write on images

✅ Scope is grantable because combined roles satisfy all requirements.

Unmet permissions will result in the scope being filtered out during the OAuth consent flow.


Consent Screen Customization

By default, the consent screen will show the description of each scope being requested. The consent screen can be tailored using the getIDPConsentManifest SDK callback. Use this callback to group scopes by logical domain, provide additional documentation, or otherwise customize the language displayed to the end user.

const isProfileScope = (scope) => scope === 'openid' || scope === 'email' || scope === 'profile';
const isDataScope = (scope) => !isProfileScope(scope);

const getScopeDescription = (scope) => {
  // ...
};

const getManifest = ({ scopes, clientName }) => [
  {
    header: `${clientName} wants to view your Profile`,
    items: [
      {
        text: 'View information stored in your Profile about your user.',
        details: scopes.filter(isProfileScope).map(getScopeDescription),
      },
    ],
  },
  {
    header: `${clientName} wants to access your Data`,
    items: scopes.filter(isDataScope).map(getScopeDescription),
  },
];

This allows branding-friendly and understandable displays of scope groupings to end users.


Access Token Scope Validation

Many Access Token JWT libraries will check for a particular scope within the token before letting the call proceed. While this approach is satisfactory for many applications, applications with complex RBAC requirements should check for the underlying permission that the scope grants instead. Authorizing at the permission level instead of the scope level creates a level of abstraction that makes it easier to introduce new scopes, or change the permissions attached to a scope over time.

For example, both the write:data and the data:* scope grant permission to write to a document. Checking for the underlying permission makes it easier to add new scopes (e.g. documents:*) in the future without requiring application code changes.

Use the Stytch SDK Authenticate Access Token method:

stytch.idp.introspectTokenLocal({
  access_token: 'eyJ...',
  authorization_check: {
    resource: 'documents',
    action: 'write'
  }
});

If the token lacks the required permission, this call will fail.

Built-in Scopes

Built-in OIDC Scopes

Built-in Stytch Platform Scopes

Custom Scopes

Defining Custom Scopes

What Scopes Can be Granted?

Consent Screen Customization

Access Token Scope Validation