OAuth server setup
How to configure your OAuth 2.0/OIDC server for Plaid Core Exchange
Quick reference
| Component | Requirement | Where to find details |
|---|---|---|
| Discovery endpoint | /.well-known/openid-configuration | Configuration requirements |
| JWKS endpoint | Public HTTPS endpoint | JWKS section |
| Consistency key | 7+ character unique ID | Unique user identifier |
| Credentials | Client ID + Secret | Credentials section |
| Required scopes | openid, offline_access | Well-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.
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
| Configuration | Notes |
|---|---|
| Server domain | Pick 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 endpoint | Plaid 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. |
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:
| Property | Description | Required | Type |
|---|---|---|---|
authorization_endpoint | Where users authenticate. This endpoint hosts your login page. Must support TLS and be publicly accessible. | Yes | URL |
token_endpoint | Where Plaid exchanges authorization codes for access tokens. Backend-to-backend only. | Yes | URL |
userinfo_endpoint | Where 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_supported | How Plaid authenticates when requesting tokens. If omitted, Plaid defaults to basic authentication. | No | Array of strings |
response_types_supported | Supported response types. Must include code. | Yes | Array of strings |
scopes_supported | For OIDC implementations, must include openid and offline_access at a minimum. Plain OAuth 2.0 only requires offline_access. See details below. | Yes | Array 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/currentinstead, 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.
Example
curl -X GET 'https://auth.firstplatypusbank.com/.well-known/openid-configuration'{
"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/jsonand well-formed JSON. - Required properties in the table above are present and correctly populated (including
authorization_endpoint,token_endpoint,response_types_supportedwithcode, andscopes_supportedwithoffline_accessand, for OIDC,openid). - For OIDC,
jwks_uriis 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 Permissions Manager
- 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. Use the first available method:
subfield in the ID token (recommended) - Include the user identifier as thesubclaim when issuing theid_tokenduring token exchange. Plaid reads this automatically.userinfoendpoint - Return the identifier in the response from youruserinfo_endpoint. Plaid will query this endpoint if thesubfield isn't available./customers/currentendpoint - Return the identifier in the Core Exchange/customers/currentresponse. Plaid requires this endpoint for plain OAuth 2.0 implementations that don't use OIDC.
Most integrations use option 1. Including the sub field in the ID token is the standard OIDC approach and requires no additional endpoint calls.
Example
When Plaid requests a token, your /token endpoint responds with an id_token that includes:
{
"access_token": "agstynmdygjdghabrgraeh...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "dhcsrtjsrgayvkdisfdgntshstu..."
}{
"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.
{
"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:
Publish the new key - Add your new key to the JWKS with a unique
kid. Keep the old key in the response.Wait for cache refresh - Plaid caches JWKS for 24 hours to reduce load on your endpoint. Wait at least 24 hours (preferably 48 hours) before proceeding to ensure Plaid has fetched and cached the new keys.
Start signing with new key - Update your ID tokens to use the new
kidin the JWT header. Plaid will use the cached JWKS to verify tokens signed with either the old or new key.Monitor for errors - Watch for signature validation errors (
invalid_signature,kid_not_found) in your logs and Plaid's error reports. If everything's clean for 48+ hours, proceed.Remove the old key - After confirming no errors for at least 48 hours, remove the old key from your JWKS. Plaid will remove the old key from its cache the next time it fetches your JWKS (within 24 hours).
JWKS caching behavior:
- Cache duration: 24 hours
- 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.
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.
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