OAuth and OpenID Connect flows cross application, browser and authorization-server boundaries. An intercepted authorization code should not be useful to an attacker who cannot prove possession of the verifier associated with the original request. PKCE is designed to protect that exchange.
Understand what PKCE protects
A client creates a verifier, sends a derived challenge with the authorization request, then presents the verifier when exchanging the code. The authorization server checks that the verifier matches the original challenge. The code is tied to the initiating transaction rather than acting as a standalone bearer value.
PKCE is particularly important for public clients, which cannot protect a static client secret in distributed software. It does not solve every OAuth risk: redirect URI validation, state and nonce handling where applicable, token audience, scopes and secure token storage still matter.
Make the safe path the only path
WebFrolic implements OAuth 2.1, where PKCE is required for authorization-code flows rather than offered as an option, and applies the same requirement to its OIDC flows. A requirement in the specification removes the chance that an application owner quietly selects a weaker configuration. Any exception should be explicit, justified and visible to the administrator.
Document the client type and flow supported by each application. Public clients cannot protect a secret like a confidential server-side application can. The label “server client” should match the deployed architecture, not substitute for reviewing where the secret is actually stored.
Validate the whole authorization journey
Test verifier mismatch, replay, missing challenge and invalid redirect URI. Check that negative responses do not leak sensitive details. Integration tests should include the real client libraries and deployment setup, not only a mocked token endpoint.
Also check how PKCE interacts with session creation, step-up authentication and network evaluation. A successful token exchange is only one stage in a broader identity transaction, and resulting access must still match the user or machine identity’s permissions.
Make secure integration maintainable
Provide clear configuration guidance and errors that point developers toward a missing parameter or incompatible flow. Avoid burying security-critical settings in an obscure advanced section. If an exception is approved, record its reason and revisit it when the application or library changes.
The goal of secure-by-default design is not to remove engineering judgement; it is to reserve that judgement for cases where it is genuinely needed. Defaults should handle the safe common path while tests catch regressions.
What this comes down to
Treat PKCE as required for every authorization-code flow, which is what OAuth 2.1 mandates.
Validate verifier mismatch, replay, missing parameters and redirects.
Treat public-client and confidential-client secrets differently.
Retest when client libraries or deployment patterns change.
