OAuth 2.0 and OpenID Connect sit underneath almost every modern sign-in, API call, SaaS integration and AI agent connection, and the details that decide whether a deployment is secure are easy to get wrong. OAuth lets an application get limited access to a resource on someone's behalf without handling their password. OpenID Connect (OIDC) adds a standard way to learn who signed in. Identity teams meet both constantly: in single sign-on, in third-party app consent, in service-to-service tokens and in MCP servers. This guide explains the roles, grant types, tokens and client types, what OAuth 2.1 and RFC 9700 change, the mistakes behind most OAuth incidents, and how to map, measure and roll out a sound OAuth programme. It is for identity, platform and application security teams.
Key takeaways
- OAuth is for authorisation, OIDC is for authentication. An access token says what a client may do at an API; an ID token tells the client who the user is and is not meant for calling APIs.
- Authorisation code with PKCE is the default. Use it for every application acting for a user, and client credentials with asymmetric client authentication for services acting as themselves.
- The implicit and password grants are finished. RFC 9700 says the password grant must not be used and steers clients away from the implicit grant; the OAuth 2.1 draft omits both.
- Most OAuth damage comes from what tokens are allowed to do. Over-broad scopes, long-lived refresh tokens, stolen integration tokens and consent phishing cause more harm than flaws in the protocol itself.
- Validation is where deployments fail quietly. Check issuer, audience, signature, algorithm, expiry and token type on every token, and nonce on every ID token.
The roles
RFC 6749 defines four roles. Several attack classes come from mixing them up, such as an API accepting an ID token as an access token.
| Role | What it is | Example | Security responsibility |
|---|---|---|---|
| Resource owner | The person or organisation who owns the data or can grant access | An employee | Grants consent; can be tricked into approving a malicious client |
| Client | The application requesting access | A calendar app, a CI pipeline, an AI agent | Protects its credentials and tokens, requests only the scopes it needs |
| Authorisation server | Authenticates the user, gets consent and issues tokens | The organisation's identity provider | Registers clients, enforces redirect URIs, PKCE and token lifetimes, protects signing keys |
| Resource server | The API that accepts access tokens | A mail API, a CRM API, an MCP server | Validates every token and refuses tokens issued for another audience |
OIDC renames two of these: the client is the relying party (RP) and the authorisation server is the OpenID Provider (OP). See the Identity Provider and SSO Security Guide.
Grant types and when to use them
A grant type is the way a client obtains its token. Choosing the right one removes whole classes of attack.
| Grant | Use it for | Notes |
|---|---|---|
| Authorisation code with PKCE | Web, mobile, desktop and single-page apps acting for a user | The default choice. PKCE (RFC 7636) binds the code to a secret verifier held by the client, so an intercepted code cannot be redeemed by someone else. Use the S256 method: RFC 9700 notes it is currently the only method that does not expose the verifier. |
| Client credentials | A service acting as itself, with no user | Authenticate the client with private key JWT or mutual TLS rather than a shared secret where possible. |
| Device authorisation | Devices without a browser or keyboard, such as TVs and CLIs | RFC 8628. The RFC itself warns that the flow can be started on a device in an attacker's possession, with the user emailed a code to enter. Restrict where it is allowed. |
| Refresh token | Getting new access tokens without re-prompting the user | For public clients, RFC 9700 requires refresh tokens to be sender-constrained or rotated. |
| Token exchange | Swapping one token for another with a different audience or scope | RFC 8693 defines a security token service for impersonation and delegation. Used across service hops and by AI agents acting on behalf of users. |
| Implicit (deprecated) | Nothing new | Access tokens in the authorisation response leak and cannot be sender-constrained. Omitted from OAuth 2.1. |
| Resource owner password (deprecated) | Nothing new | The client handles the user's password. RFC 9700: it "MUST NOT be used". Omitted from OAuth 2.1. |
RFC 9700 makes PKCE mandatory for public clients and recommended for confidential clients, and requires authorisation servers to support it. Turn it on for both.
Tokens
OAuth and OIDC use three kinds of token, each with a different audience, lifetime and blast radius.
| Token | Presented to | Format | Lifetime guidance | Main risk |
|---|---|---|---|---|
| Access token | A resource server (API) | Signed JWT (RFC 9068 profile) or opaque reference checked by introspection | NIST IR 8587 recommends no more than one hour | Replay by anyone holding it, unless sender-constrained |
| Refresh token | The authorisation server only | Usually opaque | Longer; RFC 9700 says it should expire after a period of client inactivity | Long-term access if stolen from a client or integration vendor |
| ID token (OIDC) | The client (relying party) | Signed JWT | NIST IR 8587 recommends no more than one hour | Misused as an API credential, or accepted without nonce and audience checks |
Validating a token
- Issuer and signature: fetch the issuer's keys from its published key set, and accept only a fixed list of algorithms. RFC 8725 covers the JWT pitfalls, including the
nonealgorithm and trusting key URLs named in the token header. - Audience: RFC 9700 says a resource server must refuse a token not meant for it. The
audclaim in RFC 9068 tokens, or theresourceparameter from RFC 8707, makes that possible. - Type: RFC 9068 access tokens carry
typat+jwt, which stops an ID token being accepted where an access token is expected. - Expiry and scope: check
expand that the scope covers the operation, every time.
Sender-constrained tokens
A bearer token works for whoever holds it. RFC 9700 says authorisation and resource servers should use sender-constraining, either mutual TLS (RFC 8705) or DPoP (RFC 9449), so a stolen access token is useless without the client's private key. The Token and Session Security Guide covers lifetimes, revocation, theft and binding in detail.
Client types
- Confidential clients run on a server and can hold a credential. RFC 9700 recommends asymmetric client authentication, mutual TLS or private key JWT, over shared client secrets. OIDC Core's default method is
client_secret_basic, so asymmetric authentication has to be chosen deliberately at registration. Shared secrets leak like any other secret: the OneLogin API Flaw 2025 exposed OIDC client secrets in plain text to anyone holding an API key for the tenant. - Public clients, such as mobile and single-page apps, cannot keep a secret. They rely on PKCE, exact redirect URI matching and refresh token rotation or binding.
- Dynamically registered clients: some ecosystems let clients register themselves. The MCP authorisation specification (2025-06-18 revision) says authorisation servers and MCP clients should support Dynamic Client Registration (RFC 7591). Self-registered client IDs prove little, so consent policy and scope limits matter more. See the MCP Security Guide.
Every client is a non-human identity: keep client registrations in the same inventory as service accounts and API keys. See the Secrets Management Guide.
OpenID Connect in brief
OIDC builds on the authorisation code flow. The client requests the openid scope and receives an ID token alongside the access token. OIDC Core defines the required claims:
iss: the issuer, a case-sensitive https URL. It must match the provider the client expected.sub: a subject identifier that is locally unique and never reassigned within the issuer. Key user records onissplussub, not on email address.aud: must contain the relying party's client ID. Reject ID tokens issued for other clients.nonce: a value the client sent in the request to bind the ID token to its session and mitigate replay. If present, the client must check it matches.
OIDC Core also says ID tokens must not use none as the algorithm except in narrow cases the client explicitly registered for. OIDC Discovery publishes the provider's endpoints at /.well-known/openid-configuration, including the jwks_uri where the signing keys live,. For workforce single sign-on, OIDC and SAML do the same job; OIDC is simpler for modern and mobile applications. For consumer sign-in, see the Customer IAM (CIAM) Guide.
Signing keys are the root of trust for every ID and access token. The Microsoft Storm-0558 Key Breach 2023 showed what follows when a key that should have been retired is stolen and a validation flaw lets consumer-signed tokens open enterprise mailboxes.
What OAuth 2.1 changes
OAuth 2.1 consolidates OAuth 2.0 with later security lessons. It is still an Internet-Draft: version 16 was published on 3 September 2026, and the IETF OAuth working group's milestone is to submit it to the IESG in December 2026. Once published it will replace and obsolete RFC 6749 and RFC 6750 (bearer token usage). Most of its changes come straight from RFC 9700, the OAuth 2.0 Security Best Current Practice published in January 2025, so you can adopt them now.
| Change in the OAuth 2.1 draft | What to do now |
|---|---|
PKCE required for the authorisation code grant, and the PKCE plain method removed | Require S256 PKCE for all clients |
| Redirect URIs compared by exact string matching | Remove wildcard and prefix registrations; RFC 9700 allows only localhost port variation for native apps |
| Implicit grant omitted | Move single-page apps to authorisation code with PKCE |
| Resource owner password credentials grant omitted | Retire legacy clients that collect passwords |
| Bearer tokens not sent in URI query strings | Send tokens only in the Authorization header or request body |
| Refresh tokens for public clients sender-constrained or one-time use | Turn on refresh token rotation or DPoP binding |
redirect_uri no longer sent in the token request; client credentials accepted in the request body | Check client libraries and authorisation server settings when upgrading |
RFC 9700 also requires a defence against mix-up attacks where a client talks to more than one authorisation server, recommending the iss response parameter from RFC 9207, and forbids open redirectors on clients and authorisation servers.
Common OAuth mistakes and attacks
These failure patterns sit behind most OAuth incidents. Few involve a flaw in the specifications.
- Over-broad scopes and application permissions: integrations that request full mailbox or full CRM access when they need a fraction. In the Microsoft Midnight Blizzard Breach 2024, the actor compromised a legacy test OAuth application and created its own apps holding the Exchange Online
full_access_as_approle, which let it read senior leaders' mailboxes. - Stolen integration tokens: refresh tokens held by a SaaS vendor used to pull customer data. In the Salesloft Drift Breach 2025, stolen OAuth access and refresh tokens for a chatbot integration were used to export Salesforce data; in the Klue OAuth Breach 2026, an old GitHub token let an attacker push code that collected customers' OAuth tokens.
- Consent phishing: a malicious app tricks a user into granting it access. The Cyberhaven Chrome Extension Breach 2024 began with an employee approving a malicious OAuth app that asked to publish Chrome Web Store extensions. In the ShinyHunters Salesforce Data Theft Campaign 2025, callers posing as IT support talked staff into approving connected apps.
- Device code phishing: the attacker starts a device authorisation flow and persuades the user to enter the code, exactly the remote phishing case described in RFC 8628.
- Loose redirect URI matching and open redirectors: wildcard or prefix matching, or a redirector on a trusted domain, lets attackers steer codes and tokens to their own endpoints.
- Weak token validation: accepting tokens meant for another audience, skipping signature checks, trusting the algorithm named in the token, or accepting an ID token as an API credential.
- Token passthrough: a service forwards the token it received to another API instead of getting its own. The MCP specification describes this as a confused deputy problem, where the downstream API trusts a token as if the intermediary had validated it.
- Client secrets in code: secrets committed to repositories or embedded in mobile apps.
The SaaS and OAuth App Governance Guide covers how to govern third-party OAuth apps once they are connected.
Detection and monitoring
MITRE ATT&CK tracks token theft as Steal Application Access Token (T1528) and token reuse as Use Alternate Authentication Material: Application Access Token (T1550.001). Both need logs from the authorisation server, resource servers and SaaS platforms.
- Consent and registration events: new client registrations, new grants of high-privilege scopes, and new application permissions, especially outside change windows.
- Token use from unexpected places: refresh tokens redeemed from new networks or user agents, or an integration suddenly querying far more records than its baseline.
- Refresh token reuse: with rotation enabled, RFC 9700 explains that a reused, already invalidated refresh token tells the authorisation server a token has been compromised; alert on it rather than only revoking silently.
- Validation failures: spikes in audience, signature or issuer failures at resource servers.
NIST IR 8587 says tokens and assertions themselves must not be stored in logs; log identifiers, claims and outcomes instead. See the ITDR Guide for building these signals into detection.
OAuth for non-human identities and AI agents
Services and workloads
Services should use the client credentials grant with asymmetric client authentication, or workload identity federation, which exchanges a platform-issued token for an access token without any stored secret. See the NHI Authentication Guide and the Workload Identity Federation Guide.
Agents acting for users
- Delegated, narrow tokens: agents should hold delegated tokens with narrow scopes and short lifetimes.
- Token exchange per hop: RFC 8693 lets each service obtain a new token for the next audience. Its delegation semantics, with an actor token and the
actclaim, let a downstream service see both the user and the agent acting for them. See the AI Agent Authorisation Guide and the Multi-Agent and A2A Security Guide. - Audience-bound tokens for MCP: the MCP authorisation specification (2025-06-18 revision) builds on the OAuth 2.1 draft, requires clients to send the RFC 8707
resourceparameter naming the MCP server, and requires MCP servers to validate that tokens were issued for them. - Human approval for high-impact actions: OpenID Connect Client-Initiated Backchannel Authentication (CIBA) separates the consumption device, where the agent or application runs, from the authentication device, where the user authenticates and grants consent.
Framework mapping
The mappings below were checked against the framework texts. ISO/IEC 27001:2022 control titles are taken from published Annex A listings, and CIS Controls v8.1 are mapped at control level.
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev. 5 | ISO/IEC 27001:2022 Annex A | CIS Controls v8.1 | OWASP NHI Top 10 (2025) |
|---|---|---|---|---|---|
| Client registration and inventory | ID.AM-02, PR.AA-01 | CM-8, IA-4 | A.5.16 Identity management | 2, 5 | NHI1 Improper Offboarding |
| Client authentication (private key JWT, mTLS) | PR.AA-03 | IA-9, IA-5(2) | A.5.17 Authentication information, A.8.5 Secure authentication | 6 | NHI4 Insecure Authentication, NHI2 Secret Leakage |
| Token issuance, protection and validation | PR.AA-04 | IA-13, IA-13(2), IA-13(3), SC-23 | A.8.5 Secure authentication, A.8.24 Use of cryptography | 3, 6 | NHI7 Long-Lived Secrets |
| Signing key management | PR.DS-01 | IA-13(1), SC-12 | A.8.24 Use of cryptography | 3 | NHI7 Long-Lived Secrets |
| Scopes, consent and least privilege | PR.AA-05 | AC-6, AC-3 | A.5.15 Access control, A.5.18 Access rights, A.8.3 Information access restriction | 6 | NHI5 Overprivileged NHI |
| Third-party OAuth apps and integrations | GV.SC-07, ID.AM-04, DE.CM-06 | SA-9 | A.5.19 Information security in supplier relationships, A.5.23 Information security for use of cloud services | 15 | NHI3 Vulnerable Third-Party NHI |
| Secure client development | PR.PS-06 | SA-8, SA-11 | A.8.25 Secure development life cycle, A.8.28 Secure coding | 16 | NHI2 Secret Leakage |
| Logging and detection of token abuse | DE.CM-03, DE.AE-02 | AU-2, AU-6, SI-4 | A.8.15 Logging, A.8.16 Monitoring activities | 8 | Not mapped |
IA-13 (Identity Providers and Authorization Servers) is the SP 800-53 control written specifically for this topic, and NIST IR 8587 gives implementation recommendations for it. See the Zero Trust Identity Guide.
Metrics that show progress
| Metric | What it shows | Target direction |
|---|---|---|
| Share of user-facing clients using authorisation code with S256 PKCE | Adoption of the default secure flow | Up, towards 100% |
| Number of clients still enabled for implicit or password grants | Legacy grant exposure | Down, to zero |
| Share of confidential clients using private key JWT or mTLS | Move away from shared client secrets | Up |
| Redirect URIs registered with wildcards or prefixes | Code and token leakage risk | Down, to zero |
| Median access token lifetime | Window for replay of stolen tokens | Down, at or below one hour |
| Share of public clients with refresh token rotation or binding | Protection of long-lived credentials | Up, towards 100% |
| OAuth apps holding high-privilege scopes without a named owner or recent review | Ungoverned third-party access | Down |
| Resource servers that enforce audience and token type checks | Validation coverage | Up, towards 100% |
Implementation roadmap
| Phase | Focus | Actions |
|---|---|---|
| First 30 days | Visibility | Inventory every client registration, grant type, redirect URI and scope across your authorisation servers and major SaaS platforms. Assign an owner to each client and flag legacy grants and high-privilege apps. |
| Days 31 to 90 | Close the obvious gaps | Require S256 PKCE, exact redirect URI matching and short access token lifetimes. Turn on refresh token rotation for public clients. Restrict user consent for high-privilege scopes and the device flow to the users and apps that need them. Fix audience and type validation in your own APIs. |
| Days 91 to 180 | Harden and automate | Move confidential clients to private key JWT or mTLS. Pilot DPoP or mTLS sender-constraining for high-value APIs. Replace token passthrough with token exchange. Add the detection signals above to your monitoring, and schedule recurring OAuth app reviews. |
For integrations holding your tokens, use the Third-Party Access Guide, and keep the Leaked Credential Response Playbook ready for a stolen token or client secret.
Practitioner checklist
- Use authorisation code with S256 PKCE for every user-facing client, and remove implicit and password grants.
- Register exact redirect URIs, reject anything else, and remove open redirectors.
- Authenticate confidential clients with private key JWT or mTLS rather than shared secrets.
- Keep access and ID tokens short-lived and audience-restricted.
- Rotate or sender-constrain refresh tokens, expire them after inactivity and alert on reuse.
- Validate issuer, audience, signature, algorithm, expiry, type and nonce, with a fixed list of allowed algorithms.
- Limit who can consent to which scopes, restrict the device flow, and review high-privilege OAuth apps regularly.
- Never pass a received token through to another service; use token exchange instead.
- Give every OAuth client and AI agent a named owner, and log token events without logging the tokens.
- Follow RFC 9700 now, and track OAuth 2.1 as it progresses.
Standards and references
- IETF: RFC 6749, The OAuth 2.0 Authorization Framework
- IETF: RFC 9700, Best Current Practice for OAuth 2.0 Security
- IETF: The OAuth 2.1 Authorization Framework (Internet-Draft)
- IETF: RFC 7636, Proof Key for Code Exchange by OAuth Public Clients
- OpenID Foundation: OpenID Connect Core 1.0
- IETF: RFC 9068, JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
- IETF: RFC 8693, OAuth 2.0 Token Exchange
- IETF: RFC 9449, OAuth 2.0 Demonstrating Proof of Possession (DPoP)
- IETF: RFC 8628, OAuth 2.0 Device Authorization Grant
- IETF: RFC 8707, Resource Indicators for OAuth 2.0
- NIST: IR 8587, Protecting Tokens and Assertions from Forgery, Theft, and Misuse
- Model Context Protocol: Authorization specification
Related NHI Mgmt Group resources: Token and Session Security Guide · SaaS and OAuth App Governance Guide · Identity Provider and SSO Security Guide · NHI Authentication Guide · API Security Guide