How PKCE protects the authorization code flow for native apps

This article explains how PKCE protects the authorization code flow for native apps, using unique code verifiers and code challenges to prevent potential attacks.
July 28, 20235 min read
How PKCE protects the authorization code flow for native apps

Proof Key for Code Exchange (PKCE) is an extension to the Authorization Code flow, it was originally designed to secure the authorization code flow in mobile apps, and now it is recommended to be used by single-page apps as well.

Currently, PKCE is widely used by many identity service providers for native apps. It has become so common that we often overlook the theories behind it.

This article aims to explore the reasons behind the existence of PKCE and how PKCE safeguards the authorization code flow for native apps.

Susceptible authorization code flow

As shown in the authorization code grant flow attack diagram for native apps, the authentication process is susceptible.

Auth ServerSystem/BrowserAppMalicious AppAuth ServerSystem/BrowserAppMalicious Apppar[Malicious App token request flow][App token request flow](1) Authorization Request(2) Authorization Request(3) Authorization Code(4.a) Authorization Code(5.a) Authorization Grant(6.a) Access Token(4.b) Authorization Code(5.b) Authorization Grant(6.b) Access Token

Step (1): The app performs a auth request through a secure API that cannot be intercepted. In this step, the requester also provides a redirect URI.

Step (2): The request then gets forwarded to the OAuth 2.0 authorization server. Because OAuth requires the use of TLS, this communication is protected by TLS and cannot be intercepted.

Step (3): The authorization server returns the authorization code.

Step (4.a): the authorization code is returned to the requester via the redirect URI that was provided in step (1). In this step, if the malicious app has registered itself as a handler for the redirect URI, and then the malicious app can intercept the authrization code. With the authoriaztion code, the attacker can request and obtain an access token in steps (5.a) and (6.a), respectively.

Protect authorization code flow by PKCE

As mentioned above, if we want to prevent being attacked, we need to ensure that only the app that initiated the request can request and obtain the access token. This is where PKCE comes into play.

With PKCE, a unique code verifier is created for every authorization request, and its transformed value "code challenge", caculated by "code challenge method", is sent to the authorization server with the "code challenge method" to obtain the authorization code.

The authorization code obtained by the app is then sent to the token endpoint with the "code verifier", and the server transforms the "code verifier" by the previously received "code challenge method" and compares the result with the previously received "code challenge" so that it can perform the proof of possession of the "code verifier" by the client.

Auth ServerSystem/BrowserAppMalicious AppAuth ServerSystem/BrowserAppMalicious Apppar[App token request flow][Malicious App token request flow](1) Generate code verifier and code challenge(2) Authorization request with code challenge & method(3) Authorization request(4) Store code challenge & method(5) Authorization Code(6.a) Authorization Code(7.a) Authorization Code & code verifier(8.a) Verify code verifier(9.a) Access Token(6.b) Authorization Code(7.b) Authorization Code without code verifier(8.b) Verify code verifier

Step (1-3): The app creates and records a secret named the "code verifier" and derives a transformed version ""code challenge", which is sent in the OAuth 2.0 Authorization Request along with the transformation method "code challenge method".

Step (3-6): The Auth Server responds as usual but records "code challenge" and the "code challenge method".

Step (7.a): The app then sends the authorization code in to the token endpoint as usual but includes the "code verifier" secret generated at step (1).

Step (8.a-9.a): The authorization server transforms "code verifier" to "code challenge" and compares it to "code challenge" from step (1-3). Access is denied if they are not equal.

In this case, even though the malicious app have intercepted the authorization code at step (6.b), it is unable to redeem it for an access token, as it is not in possession of the "code_verifier" secret, and because the "code verifier" is sent over TLS, it cannot be intercepted.


This is how PKCE protects the authorization code flow for native apps, I believe you now have a better understanding of its mechanism.

It's worth mentioning that Logto Cloud has officially released its product, and it provides PKCE functionality by default for native apps, further enhancing security. With PKCE, your app gains additional protection during the authentication process, ensuring a more secure user sign in flow.

No need for complex implementation processes; by using Logto provided native SDKs with just a few lines of code, you can immediately enjoy the security benefits of PKCE and also enjoy a reliable, ready-to-used, and user-friendly sign-in experience for your app.