What is PKCE actually protecting?

The reason PKCE is important is that on mobile OS, the OS allows apps to register to handle redirect URIs so a malicious app can register and receive redirects with the authorization code for legitimate apps. This is known as an Authorization Code Interception Attack.

Authorization Code Interception Attack

This is described by WSO2 here:

Since multiple applications can be registered as a handler for the specific redirect URI, the vulnerability of this flow, is that a malicious client could also register itself as a handler for the same URI scheme that a legitimate application handles. If this happens, it is a possibility that the operating system will parse the URI to the malicious client. The flow of this attack is illustrated in the following diagram.

In some operating systems such as Android, in step 5 of the flow, the user is prompted to select the application to handle the redirect URI before it is parsed using a "Complete Action Using" activity. This may avoid a malicious application from handling it, as the user can identify and select the legitimate application. However, some operating systems (such as iOS) do not have any such scheme.

  • Ref: https://docs.wso2.com/display/IS520/Mitigating+Authorization+Code+Interception+Attacks

To understand this better, here is a diagram and discussion from OpenID. You can see that the mobile System Browser has responsibility to receive the redirect URI and route it to the correct app.

Native app Redirect URI routing

  • Ref: http://openid.net/2015/05/26/enhancing-oauth-security-for-mobile-applications-with-pkse/

However, because mobile OSes can allow many apps to register for the same redirect URI, a malicious app can register for and receive a legitimate authorization code as shown in this diagram, also by WSO2:

PKCE Malicious App

Attack Mitigation by PKCE

PKCE mitigates this by requiring shared knowledge between the app initiating the OAuth 2.0 request (request auth code) and the one exchanging the auth code for token. In the case of an Auth Code Interception Attack, the malicious app does not have the verifier to complete the token exchange.


This write-up Okta has on this subject explains this pretty well IMHO.

I believe it's because PKCE is intended for native applications (e.g. Android, iOS, UWP, Electron, etc.) where you leave the security context of your application and go to the browser to authenticate, and rely on the secure return to your application from the browser. You don't necessarily have TLS on the redirect back to your application (in the case of custom schemes, you are relying on the OS to bring the response back to your application) so in the event your authorization code goes somewhere malicious, the receiving app wouldn't be able to get an access token without the dynamic secret.

The merits of a dynamic secret on a public client are obvious here - and the assumption for PKCE is that it is not difficult to intercept the response from the browser to your application.