Plaid logo
Core Exchange
ALL DOCS

Building your integration

  • Authentication
  • User experience
  • Security best practices
  • Mock server
Core Exchange
Close search modal
Ask Bill!
Ask Bill!
Hi! I'm Bill! You can ask me all about Core Exchange. Try asking questions like:
    Note: Bill isn't perfect. He's just a robot platypus that reads our docs for fun. You should treat his answers with the same healthy skepticism you might treat any other answer on the internet. This chat may be logged for quality and training purposes. Please don't send Bill any PII -- he's scared of intimacy. All chats with Bill are subject to Plaid's Privacy Policy.
    Plaid.comGet Started
    Open nav

    OAuth server setup

    How to configure your OAuth 2.0/OIDC server for Plaid Core Exchange

    Quick reference

    ComponentRequirementWhere to find details
    Discovery endpoint/.well-known/openid-configurationConfiguration requirements
    JWKS endpointPublic HTTPS endpointJWKS section
    Consistency key7+ character unique IDUnique user identifier
    CredentialsClient ID + SecretCredentials section
    Required scopesopenid, offline_accessWell-known configuration

    OAuth server setup

    Your OAuth 2.0 server needs to issue standards-compliant tokens. Plaid strongly recommends implementing OpenID Connect with the OIDC Discovery Specification. OIDC enables a better user experience for returning users, centralized consent management, and optimal API performance through the ID token's sub field. Plain OAuth 2.0 is supported but requires providing the unique user identifier via the /customers/current endpoint instead.

    For a working reference you can run locally, see FDX Explorer and linked source code on the Authentication overview.

    Implementation options

    Use an existing identity provider - Okta, Auth0, Ping Identity, Azure AD, or AWS Cognito all work, and provide the fastest implementation path.

    Plaid recommends Okta's Plaid integration.

    Build your own - Use an OIDC-certified library. This option gives you more control but takes more time.

    Don't roll your own OAuth implementation from scratch. Custom, non-standard implementations create headaches for everyone. Stick to certified tools and established identity providers.

    Configuration requirements

    ConfigurationNotes
    Server domainPick a domain for your OAuth server, usually something like auth.yourbank.com or yourbank.okta.com. Keep it separate from your API server. HTTPS is required.
    Discovery endpointPlaid pulls your server config from /.well-known/openid-configuration. If your domain is https://auth.firstplatypusbank.com, Plaid will hit https://auth.firstplatypusbank.com/.well-known/openid-configuration. Make sure it returns the details in the next section, and that it is reachable from the public internet. If the Validator reports Error getting OIDC configuration, see Validator end-to-end failures.

    Well-known configuration

    Your /.well-known/openid-configuration endpoint is public (no auth required). When Plaid hits it, the response should include at minimum these values:

    PropertyDescriptionRequiredType
    authorization_endpointWhere users authenticate. This endpoint hosts your login page. Must support TLS and be publicly accessible.YesURL
    token_endpointWhere Plaid exchanges authorization codes for access tokens. Backend-to-backend only.YesURL
    userinfo_endpointWhere Plaid retrieves the user identifier. Required for OIDC. Plain OAuth 2.0 implementations that do not use OIDC must instead provide the identifier via /customers/current.Yes (OIDC)URL
    token_endpoint_auth_methods_supportedHow Plaid authenticates when requesting tokens. If omitted, Plaid defaults to basic authentication.NoArray of strings
    response_types_supportedSupported response types. Must include code.YesArray of strings
    scopes_supportedFor OIDC implementations, must include openid and offline_access at a minimum. Plain OAuth 2.0 only requires offline_access. See details below.YesArray of strings

    Scope details:

    • openid - Enables OIDC features. Lets Plaid read the user identifier from the ID token, enabling returning user experience, optimized API traffic, and centralized consent management. (If this scope is unavailable, Plaid will query /customers/current instead, but OIDC-specific features won't be available.)
    • offline_access - Gives Plaid a refresh token to fetch data in the background. Required for use cases that need data access beyond the initial OAuth session, such as balance checks for stored bank accounts (recurring transfers, saved payment methods), transaction subscriptions, and ongoing data updates.

    FDX scopes: Plaid may also request FDX data-scope values in the scope parameter (for example fdx:accountbasic:read or fdx:transactions:read) to describe the categories of data it's accessing:

    ScopeData category
    fdx:accountbasic:readBasic account information
    fdx:accountdetailed:readDetailed account information
    fdx:paymentsupport:readFull account number and payment network routing number
    fdx:customercontact:readCustomer contact information
    fdx:investments:readInvestment holdings and transactions
    fdx:statements:readAccount statements
    fdx:transactions:readTransaction history

    FDX deprecated fdx:accountpayments:read in favor of fdx:paymentsupport:read in FDX 6.1.0, and will remove it in FDX 7. Plaid requests fdx:paymentsupport:read.

    Your OAuth server does not need to enforce access control per FDX scope. Plaid requests them descriptively, and it's fine to grant a token every FDX scope your implementation supports without gating individual endpoints on them.

    Example

    Plaid's request
    curl -X GET 'https://auth.firstplatypusbank.com/.well-known/openid-configuration'
    Your response
    {
      "authorization_endpoint": "https://auth.firstplatypusbank.com/oauth2/v1/authorize",
      "token_endpoint": "https://auth.firstplatypusbank.com/oauth2/v1/token",
      "userinfo_endpoint": "https://auth.firstplatypusbank.com/oauth2/v1/userinfo",
      "token_endpoint_auth_methods_supported": ["client_secret_basic"],
      "response_types_supported": ["code"],
      "scopes_supported": ["openid", "offline_access"]
    }

    What to validate

    When Plaid (or your own tooling) fetches your well-known configuration, confirm:

    • All endpoint URLs use HTTPS, resolve via DNS, and are reachable from the public internet with valid Production TLS certificates.
    • The endpoint returns HTTP 200 with Content-Type: application/json and well-formed JSON.
    • Required properties in the table above are present and correctly populated (including authorization_endpoint, token_endpoint, response_types_supported with code, and scopes_supported with offline_access and, for OIDC, openid).
    • For OIDC, jwks_uri is present, publicly accessible, and returns at least one signing key in a valid JWKS document.

    Note for multi-institution deployments: If you serve multiple financial institutions, the sub field in the ID token must be unique per institution (but doesn't need to be unique across all institutions).

    Unique user identifier (consistency key)

    Plaid needs a stable identifier for each user, something that never changes, is unique across your user base, and stays consistent when that user connects multiple apps. This identifier (often called the "consistency key") enables Plaid to:

    • Show users all their active connections in one place (my.plaid.com)
    • Maintain a user-level view in Consent management
    • Optimize API traffic by recognizing returning users
    • Enable the returning user experience

    Requirements

    The identifier must be:

    • Stable - Never changes for the duration of the user's relationship with you
    • Unique - No two users share the same identifier
    • Consistent - Same value across all apps a user connects to via Plaid
    • Non-sensitive - Should not expose personally identifiable information (avoid SSN, email, phone)
    • Sufficient length - Minimum seven characters

    In practice, the best option is usually an internal customer identifier from your core system (for example, a GUID or numeric customer ID) as long as it is never reused, never changes, and does not expose login names or account numbers. Avoid using online banking usernames, email addresses, phone numbers, or account numbers as the identifier.

    How to provide it

    You have three options. Plaid tries them in this order and uses the first one available:

    1. sub field in the ID token (recommended) - Include the user identifier as the sub claim when issuing the id_token during token exchange. Plaid uses this whenever you return an id_token and publish a jwks_uri, since it needs the key set to verify the token's signature. No extra request is needed beyond fetching your keys, which Plaid caches.

    2. userinfo endpoint - Return the identifier as the sub field in the response from your userinfo_endpoint. Plaid uses this when there's no ID token or no jwks_uri, and it costs one additional request to your authorization server per login.

    3. /customers/current endpoint - Return the identifier as customerId in the Core Exchange /customers/current response. Plaid uses this only when neither of the above is available. It's the least preferred option because it isn't part of OIDC, it makes login depend on your FDX API being reachable, and it forgoes the OIDC features described above.

    Most integrations use option 1. Including the sub field in the ID token is the standard OIDC approach and requires no additional endpoint calls.

    Selection happens once, based on what you've configured, and does not use a fallback flow. For example, publishing a jwks_uri and returning an id_token commits you to option 1. If Plaid can't verify your id_token against your JWKS, it reports that verification error and stops; it will not fall back to options 2 or 3. So a login failing in an integration that publishes a jwks_uri indicates an error with your keys or your token signing, not with the userinfo or /customers/current endpoints.

    Whichever option you use, the value must satisfy the requirements above. A value shorter than seven characters, or absent entirely, fails the login with an invalid consistency key error rather than falling through to another option.

    Example

    When Plaid requests a token, your /token endpoint responds with an id_token that includes:

    Token response example
    {
      "access_token": "agstynmdygjdghabrgraeh...",
      "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refresh_token": "dhcsrtjsrgayvkdisfdgntshstu..."
    }
    Decoded ID token payload example
    {
      "sub": "user_12345678",
      "iss": "https://auth.firstplatypusbank.com",
      "aud": "dc1fe34ae9e5e98147f2fd76060016a4",
      "exp": 1672444800,
      "iat": 1672441200
    }

    The sub value (user_12345678) is the consistency key. It's 13 characters, unique to this user, and will never change.

    JWKS (JSON Web Key Set)

    Only OIDC implementations that issue ID tokens need to expose a JWKS endpoint. Plain OAuth 2.0 implementations don't need to provide one.

    JWKS is how Plaid verifies your tokens are valid. The jwks_uri points to your public keys. Plaid uses them to verify the digital signatures on JWTs, such as the id_token. Plaid automatically fetches and caches your JWKS.

    Example JWKS response
    {
      "keys": [
        {
          "kty": "RSA",
          "use": "sig",
          "kid": "ABCD1234",
          "n": "4MZuI7r3F5y...",
          "e": "AQAB",
          "alg": "RS256"
        }
      ]
    }

    Keep your JWKS healthy:

    • Host it over HTTPS (non-negotiable).
    • Rotate signing keys at least annually, or immediately if compromised.
    • Include a kid (Key ID) in every key for quick lookup.
    • Use strong algorithms: RSA-2048 or better (RS256, ES256, etc.).

    Key rotation process

    Rotating JWKS keys without breaking Plaid's token verification:

    1. Publish the new key - Add your new key to the JWKS with a unique kid. Keep the old key in the response.

    2. Wait for cache refresh - Plaid caches JWKS for up to 10 minutes to reduce load on your endpoint. Wait at least that long before proceeding to ensure Plaid has fetched and cached the new keys.

    3. Start signing with new key - Update your ID tokens to use the new kid in the JWT header. Plaid will use the cached JWKS to verify tokens signed with either the old or new key.

    4. Monitor for errors - Watch for signature validation errors (invalid_signature, kid_not_found) in your logs and Plaid's error reports. Monitor for a period that covers your typical traffic patterns before proceeding.

    5. Remove the old key - After confirming no errors over a reasonable monitoring period, remove the old key from your JWKS. Plaid will remove the old key from its cache the next time it fetches your JWKS (within 10 minutes).

    JWKS caching behavior:

    • Cache duration: Up to 10 minutes
    • Fetch trigger: Cache miss or expiration
    • During rotation: Both old and new keys remain cached and valid during the transition period

    Emergency rotation: If a key is compromised, rotate immediately. Coordinate with your Plaid contact to minimize disruption. Be prepared for some token validation failures during emergency rotations.

    Recommended schedule: Annually at a minimum. Schedule rotations during low-traffic periods when possible.

    Credentials

    Plaid needs credentials to authenticate when requesting access tokens. You'll issue us a client ID (public identifier) and client secret (password).

    Client ID

    The client ID is public, but don't make it easy to guess. Length: 8-256 characters.

    How to create one

    Generate a random string using your language's cryptographically secure random number generator (CSPRNG), then encode as hexadecimal. Target 16 random bytes, which produces a 32-character hex string.

    Generate a random client ID
    import crypto from 'crypto';
    const randomString = crypto.randomBytes(16).toString('hex');
    console.log(randomString);
    

    Client secret

    The client secret is Plaid's password. Treat it like one. Follow these security best practices:

    • Generate it securely - Use a CSPRNG (cryptographically secure pseudo random number generator). Don't use UUIDs, as many libraries leak timestamps or MAC addresses.
    • Never store it in plain text - Encrypt or hash it. Always.
    • Make it visually distinct - Different format from the client ID helps prevent copy/paste errors.

    Length: 8-256 characters.

    How to generate one

    Generate a 256-bit value using your language's CSPRNG, then encode as hexadecimal. This produces a 64-character hex string.

    Generate a random client secret
    import crypto from 'crypto';
    const randomString = crypto.randomBytes(32).toString('hex');
    console.log(randomString);
    

    Token expiration

    Configure your token lifetimes to balance security with usability.

    Access tokens: 15 minutes

    Short-lived access tokens limit exposure if a token is compromised. Plaid checks expiration before each API call and automatically refreshes when needed.

    Refresh tokens: 13+ months

    Compliance requires users to reauthorize every 12 months, which Plaid handles automatically. Rotating refresh tokens or setting their expiration to 13+ months ensures they remain valid through this cycle. The extra buffer (13+ months is common) accounts for high-traffic periods like tax season, when users may not reauthorize immediately.

    Setup checklist

    • OAuth server configured with OIDC support
    • Discovery endpoint returns valid configuration
    • JWKS endpoint publicly accessible with valid keys
    • Client ID and secret generated securely
    • Consistency key implemented (via sub, userinfo, or /customers/current)
    • TLS certificates valid (not self-signed for Production)
    • Token expiration configured (15 min for access, 13+ months for refresh)
    • All endpoints are tested and responding correctly