Most OAuth and OpenID Connect mistakes in mature systems are not exotic. They come from old defaults that stayed in place after the guidance moved on: implicit flow in browser clients, loose redirect matching, refresh tokens without replay handling, and clients that assume the issuer instead of checking it.
That is the useful way to think about the 2026 baseline. It is not a list of advanced options. It is the minimum shape a new OIDC integration should have before teams start arguing about higher-assurance profiles, sender-constrained access tokens, or regulated-industry requirements.
The standards landscape is also worth naming carefully. RFC 9700 is the IETF Best Current Practice for OAuth 2.0 security. OAuth 2.1 is still an IETF Internet-Draft, so it should be treated as work in progress, not as a final RFC. Its practical value is that it gathers many of the security expectations that teams already need to apply when they build new systems.
The baseline is smaller than the conversation
A secure baseline does not mean every client needs the same architecture. A server-side web application, a native mobile app, a browser-based application, and a machine client all have different token-handling constraints.
The shared baseline is narrower:
- use authorization code flow with PKCE
- register redirects precisely and compare them exactly
- stop using implicit flow as the default for new browser integrations
- do not use the resource owner password credentials grant
- validate issuer identity during the OIDC and OAuth response handling
- protect refresh tokens against replay, especially for public clients
Those items are not independent preferences. They work together. PKCE makes stolen authorization codes harder to redeem. Exact redirects reduce opportunities to send codes or tokens to the wrong place. Issuer validation protects clients that can talk to more than one authorization server. Refresh-token rotation or sender constraint turns reuse into a detectable security event.
sequenceDiagram
autonumber
participant Client as OIDC client
participant Browser as Browser
participant ProAuth as Authorization server
participant API as Resource server
Client->>Client: Create PKCE verifier
Client->>Browser: Redirect with code challenge
Browser->>ProAuth: Authorization request
ProAuth-->>Browser: Code and issuer signal
Browser-->>Client: Callback to exact redirect URI
Client->>Client: Validate state and issuer
Client->>ProAuth: Redeem code with verifier
ProAuth-->>Client: Tokens
Client->>Client: Apply refresh token replay controls
Client->>API: Call API with audience-bound token
PKCE is the default for code flow
RFC 7636 defines Proof Key for Code Exchange. The short version is simple: the client creates a high-entropy code_verifier, sends a derived code_challenge in the authorization request, and later presents the original verifier when it redeems the authorization code at the token endpoint.
If an attacker only sees the authorization code, the code is not enough. The token endpoint also expects the verifier that belongs to the original challenge.
RFC 9700 makes this part of the baseline. Public clients must use PKCE for authorization code flows, and confidential clients are recommended to use it as well. For new integrations, the practical rule is easier than the standards history: use authorization code flow with PKCE, and use S256 as the challenge method.
This is also why older guidance that treated PKCE as mostly a native-app concern is dated. PKCE started there, but modern OAuth security guidance applies it much more broadly.
Redirects need exactness, not convenience
Redirect URI handling is one of those areas where developer convenience and security pull in opposite directions. Wildcards, partial matching, and open redirectors are attractive during setup because they reduce registration friction. They are also a common way to leak authorization codes or tokens.
RFC 9700 says authorization servers must use exact string matching when comparing client redirection URIs against pre-registered URIs, with a specific exception for localhost port numbers in native app redirects. That exception should not become a general habit.
For application teams, the review question is concrete:
- Are all redirect URIs explicitly registered?
- Are wildcard redirects removed?
- Are environment-specific redirects separated instead of hidden behind broad patterns?
- Does the client reject callbacks that do not match the transaction it started?
- Are open redirectors kept out of both the client and authorization server redirect path?
The point is not to make configuration unpleasant. The point is to keep authorization artifacts from being routed to a place the client did not intend.
Implicit and password grants should not be copied into new systems
Older OAuth tutorials often used the implicit grant for browser applications because it returned tokens directly through the authorization response. That design avoided a back-channel token request, but it also put access tokens into the browser front channel.
RFC 9700 is direct about the risk: response types that issue access tokens in the authorization response are vulnerable to token leakage and replay. Its recommendation is to avoid the implicit grant unless the specific injection and leakage risks are addressed. For new systems, the safer default is authorization code flow with PKCE and a careful decision about where tokens live.
The password grant has even less room in a modern baseline. RFC 9700 says the resource owner password credentials grant must not be used. It asks the client application to collect the user’s password, increases the number of places credentials can leak, and does not fit well with modern authentication experiences such as MFA or origin-bound credentials.
There may be legacy systems where these flows still exist. The baseline decision is not to pretend they disappeared. It is to stop designing new systems around them, and to put existing use on a migration path.
Issuer validation is a real control
OIDC already depends on issuer identity. OpenID Connect Core 1.0 defines OIDC as an identity layer on top of OAuth 2.0 and uses the issuer as part of the trust relationship between the relying party and the OpenID Provider.
The practical issue becomes sharper when one client can interact with more than one authorization server. That is common in SaaS products, multi-tenant systems, white-label deployments, and integrations where customers bring their own identity provider.
In a mix-up attack, the client begins a transaction with one authorization server but processes the response as if it came from another. RFC 9207 standardizes the iss authorization response parameter so the authorization server can identify itself in the response. RFC 9700 requires clients that interact with more than one authorization server to defend against mix-up attacks, and using iss according to RFC 9207 is one of the preferred mitigations.
The client-side rule is boring in the best possible way: store the issuer expected for the login transaction, then compare the returned issuer before exchanging the code or accepting the response. Distinct redirect URIs per issuer can also help, but they should not replace response validation when iss is available.
Refresh tokens need replay handling
Refresh tokens are useful because they let clients obtain new access tokens without sending the user through a full sign-in every time. That usefulness is also why they need stricter handling than short-lived access tokens.
RFC 9700 requires refresh tokens for public clients to be sender-constrained or to use refresh-token rotation. With rotation, the authorization server issues a new refresh token each time the client refreshes. The old token is invalidated, but the server keeps enough relationship state to detect reuse. If an old refresh token appears again, that is evidence that the token chain may have leaked.
That changes the operational model. Refresh-token reuse should not be treated like a harmless retry. It should be logged, tied to the grant or session, and handled as a compromise signal. Depending on the risk model, the response may include revoking the token family, forcing re-authentication, or alerting operations.
For confidential clients, client authentication already changes the risk profile. For public clients, the server cannot rely on a secret that the client can keep confidential. That is why replay detection is part of the baseline rather than an optional hardening step.
What to review in existing applications
The easiest way to use the baseline is to review one client at a time. Start with the clients that handle production user sessions, then move to older internal tools and lower-traffic integrations.
For each client, check:
- Which grant types are enabled, and which are actually used?
- Does every authorization-code flow use PKCE with
S256? - Are redirect URIs registered exactly?
- Can the client interact with more than one issuer?
- Does the client validate the issuer it expected for that transaction?
- Are implicit and password grants disabled for new integrations?
- If refresh tokens are issued, how is replay detected?
- Are refresh-token reuse events visible to operations?
That review often exposes a useful split. Some applications only need configuration cleanup. Others need a design change, especially browser applications that still store tokens directly in places exposed to JavaScript. Do not flatten those findings into one backlog item called “OAuth hardening.” The baseline is a set of specific controls, and each control has a different owner.
Keep product choices behind the standard boundary
Standards give teams a common language for the security boundary. Product choices still matter, but they should not replace the baseline itself.
In ProAuth-based systems, the application should continue to trust ProAuth as its OIDC issuer and keep tenant-specific identity complexity out of application code. The same baseline review still applies: code flow with PKCE, exact redirects, issuer validation, and careful refresh-token behavior.
For implementation guidance, start with the ProAuth OIDC client documentation. Treat the standards as the checklist and the product configuration as the place where that checklist becomes enforceable.
The important shift is cultural as much as technical. Implicit flow, password grant, loose redirects, and unmanaged refresh tokens are not harmless defaults that happen to be old. They are design choices with known failure modes. A modern OIDC baseline makes those choices visible before they become incident notes.