OAuth 2.0 OAuth Public Clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack. Proof Key for Code Exchange by OAuth Public Clients specification describes the attack as well as a technique to mitigate against the threat through the use of Proof Key for Code Exchange (PKCE, pronounced "pixy").
Proof Key for Code Exchange by OAuth Public Clients extension utilizes a dynamically created cryptographically random key called "code_verifier".
A unique Code_verifier is created for every authorization Request, and its transformed value, called "code_challenge", is sent to the Authorization Server to obtain the Authorization Code. The Authorization Code obtained is then sent to the Token_endpoint with the "code verifier", and the Authorization Server compares it with the previously received request code so that it can perform the Proof-of-Possession of the "code verifier" by the client. This works as the mitigation since the attacker would not know this one-time key, since it is sent over TLS and cannot be intercepted.
Typically, the code_challenge and code_challenge_method values are stored in encrypted form in the "code" itself, but could alternatively be stored on the server, associated with the code. The server MUST NOT include the code_challenge value in client requests in a form that other entities can extract.
4.4.1. Error Response If the server requires Proof Key for Code Exchange by OAuth Public Clients (PKCE) by OAuth Public Clients, and the OAuth Client does not send the code_challenge in the request, the Authorization_endpoint MUST return the authorization error response with "error" value set to "invalid_request". The "error_description" or the response of "error_uri" SHOULD explain the nature of error, e.g., Code_challenge required.
If the server supporting PKCE does not support the requested transform, the authorization endpoint MUST return the authorization error response with "error" value set to "invalid_request". The "error_description" or the response of "error_uri" SHOULD explain the nature of error, e.g., transform algorithm not supported.
If the code_challenge_method from Section 4.2 was "S256", the received code_verifier is hashed by SHA-256, then base64url encoded, and then compared to the "code_challenge". i.e.,
code_challenge = base64url.encode(crypto.createHash('sha256').update(code_verifier).digest());
If the "code_challenge_method" from Section 4.2 was "plain", they are compared directly. i.e.,
"code_verifier" == "code_challenge".
As the OAuth 2.0 RFC 6749 server responses are unchanged by this specification, client implementations of this specification do not need to know if the server has implemented this specification or not, and SHOULD send the additional parameters as defined in Section 3. to all servers.
PKCE solves two problems:
The section of the OAuth 2.0 Security Best Current Practice BCP (4.5.3). Proposed Countermeasures) which says clients can do PKCE or use the nonce, is only talking about preventing authorization_code injection.
The nonce parameter solves authorization code injection if the client requests an Id_token. OAuth Public Clients using the nonce parameter are still susceptible to stolen authorization_codes so they still need to do PKCE as well.
The only case where OpenID Connect clients do not benefit from PKCE is if they are also OAuth Confidential Clients. OAuth Public Clients (even OIDC clients) still need to do PKCE even if they check the nonce.