42

A lot of services today still recommend the implicit flow for an OpenID Connect/Oauth2 token exchange when developing Single-Page Apps. (See Okta - now recommends PKCE w/ implicit fallback, Google, Auth0)

Some newer guidance out there points towards using the Authorization Code Flow without a client_secret in the token exchange step, which I can agree makes sense for the reasons cited in the article (e.g. tokens don't live within browser history, web logs, etc.). Why isn't this concept taken a step further with PKCE? Instead of omitting the client_secret completely, why not utilize a dynamic one as it is recommended for native applications? SPAs and Native Apps are both considered "public clients" where a secret cannot be safely kept, but only Native Apps get the PKCE recommendation while SPAs seem to be kept in the past.

I understand the security implications PKCE is trying to solve doesn't directly relate to a browser only context, but couldn't this be viewed as a defense-in-depth mechanism and utilized anyway? I know from experience Google won't let you generate a Web Application Credential and try to use anything but implicit for a SPA (see this issue), the Authorization Code flow will expect a client_secret and the PKCE exchange only works if you choose a native app credential type but then you can't specify https redirect_uri's for your application.

Other providers allow PKCE with Authorization Code flow in a web-context, but it isn't recommended from them. Am I wrong in wanting this and utilizing it? It seems fairly trivial to add the additional steps of generating and passing around the code challenges with the Web Crypto APIs available (targeting newer browsers and shimming as needed).

7
  • 11
    For all who didn't know what PKCE means (like me), a link to Proof Key for Code Exchange. Apr 3, 2018 at 21:12
  • Some more reading... "In practice, there are only very limited cases in which this is necessary. Several major implementations (Keycloak, Deutsche Telekom, Smart Health IT) have chosen to avoid the Implicit Flow completely and use the Authorization Code flow instead." oauth.com/oauth2-servers/single-page-apps Oct 9, 2018 at 19:41
  • "In the context of browser apps (“single page apps”), the OAuth2 Implicit Flow was designed when most browsers did not allow developers to modify the history via, for example, pushState. Back then, using hashtags was the only way to transmit data via URLs to JavaScript applications running in the browser. Today, as browser APIs improved, it’s possible to use the OAuth2 Authorize Code Flow for browser apps instead, and benefit from, e.g., refresh tokens." ory.sh/oauth2-for-mobile-app-spa-browser Oct 9, 2018 at 19:42
  • 1
    I'm a contributor to some ory projects (not relevant I guess) and that blog post is a bit misleading - refresh tokens must be kept in a secure and safe location which, according to OWASP, is not currently possible in the browser - thus making refresh tokens unsafe in the setting of a SPA.
    – someone1
    Oct 9, 2018 at 20:33
  • 1
    @JW - Yes that'd be considered secure if the plugin works by securely storing secrets in a hardware backed cryptographic storage. You'd just have to be sure your auth logic isn't shared with any web version of the app since you won't have secure storage on a browser. Alternatively, there are AppAuth implementations for mobile that you might be able to load prior to a Cordova app? I'd think it more secure if you left all interactions with tokens and storage native to the platform and build a bridge for Cordova. If security is paramount, better off going native (or similar) vs Cordova IMHO.
    – someone1
    Dec 31, 2018 at 19:54

3 Answers 3

28

While all the other answers are correct, the latest OAuth 2.0 for Browser-Based Apps Best Practices Doc (January 29, 2019) states that (emphasis mine):

  1. Overview

For authorizing users within a browser-based application, the best
current practice is to

o Use the OAuth 2.0 authorization code flow with the PKCE extension

...

As well as

7.1. Initiating the Authorization Request from a Browser-Based Application

Public browser-based apps MUST implement the Proof Key for Code
Exchange (PKCE [RFC7636])
extension to OAuth, and authorization
servers MUST support PKCE for such clients.

The PKCE extension prevents an attack where the authorization code is intercepted and exchanged for an access token by a malicious client, by providing the authorization server with a way to verify the same client instance that exchanges the authorization code is the same one that initiated the flow.

I assume there are some minor benefits that are worth the while of doing it as a defense in depth, even for browser based apps.

6
  • 2
    Great find - it'd be interesting to see how this draft develops!
    – someone1
    Mar 4, 2019 at 3:57
  • 4
    Section 9.8 ( tools.ietf.org/html/… ) have some good arguments for why this is better than implicit flow. Best argument IMO: "Additionally, many browsers now also sync browser history to cloud services and to multiple devices, providing an even wider attack surface to extract access tokens out of the URL." Apr 23, 2019 at 13:52
  • 3
    I understand why Authorization Code is preferred over Implicit. However, for a browser-based app, I don't see why PKCE is any more secure than the normal Authorization Code flow with a randomly-generated state param. The PKCE verifier mitigates attacks where the code has to be passed back to the application over an insecure channel (as in mobile apps). But modern-day browser apps use can use CORS and TLS to talk directly to the authentication server, meaning the code should be securely returned to the app.
    – mamacdon
    Jul 29, 2020 at 14:31
  • 1
    @mamacdon PKCE is to prove to Authorization Server that the guy that is exchanging code with token is the same guy that requested the code. So if "code" is leaked, an attacker won't be able to exchange it for a token. Now, can "code" be intercepted even on TLS? The answer is yes: a Chrome extension can intercept url and grab the "code" part.
    – Alireza
    Nov 5, 2022 at 10:30
  • 1
    @Alireza I think the question more specifically is: Can the code be intercepted, but the code verifier in the SPA's context is safe, because if your Chrome extension can read the code verifier, PKCE won't provide any protection. A malicious chrome extension might be out of scope for PKCE.
    – Basti
    Apr 24 at 6:47
25

@catanman makes excellent points regarding the technical considerations around PKCE in SPAs, however just recently the IETF Oauth working group has published a best current practice document (December 28, 2018) stating:

Note: although PKCE so far was recommended as a mechanism to protect native apps, this advice applies to all kinds of OAuth clients, including web applications.

4
  • Note that as per RFC 6749 an SPA is not a web application. Web applications are confidential applications running on servers. SPA's are classified as user-agent-based application.
    – Priya R
    Feb 15, 2019 at 10:18
  • 8
    Though still incorporated with "all kinds."
    – Brad J
    Feb 16, 2019 at 16:26
  • Great find - it'd be interesting to see how this draft develops!
    – someone1
    Mar 4, 2019 at 4:00
  • 1
    Excellent video June 2019: youtube.com/watch?time_continue=615&v=CHzERullHe8. Note specifically Aaron Perecki's quote: "It [PKCE] was created for mobile apps, but it turns out it is useful for any public client."
    – ryanm
    Jul 3, 2019 at 21:13
17

SPAs would not benefit from PKCE. PKCE solves a different problem than the one you're describing.

First of all, for SPAs the current best practice is still to use the implicit flow, not the authorization code flow. With the implicit flow, the access token is included in the hash fragment (#) of the redirect URI instead of in a query component (?). Since the browser never includes the hash fragment portion of a URI when it makes a request, the token does not appear in browser history, web logs...

When it comes to native apps, rfc8252 section 6 says the following:

Public native app clients MUST implement the Proof Key for Code Exchange (PKCE RFC7636])


On a side note, notice that the PKCE requirement is for public native clients. You may be wondering how it can be possible for a native app to not be public. The answer is in section 8.4:

Except when using a mechanism like Dynamic Client Registration [RFC7591] to provision per-instance secrets, native apps are classified as public clients

I'm not exactly sure how to refer to these clients since I've never seen confidential native client mentioned anywhere. Maybe non-public native client works :)


To answer your question: So why is the authorization code flow with PKCE required for public native apps and not SPAs?

The logic is as follows:

  1. One of the main purposes of OAuth2 is to prevent the exposure of user credentials to clients.
  2. For native apps to meet this requirement, the user must not enter credentials anywhere that the native app has access. Therefore native login screens and webviews must be avoided.
  3. The solution is for native apps to launch an external user-agent (see rfc8252 appendix B) where the user will authenticate with the authorization server and authorize the application. Native apps have no access to the external user-agent, so the user's credentials are safe.
  4. However, a new complication has been introduced which did not exist with SPAs: The external user-agent must now communicate the authorization server's response back to the native app? The answer is inter-app communication (see section 5 and section 7)
  5. Unfortunately, several types of inter-app communication can be intercepted by malicious 3rd party apps! With the implicit flow, this means that the access token could be stolen by a malicious app, which would be a very bad thing. This is one of the main reasons why the implicit flow is not used for native apps.
  6. But even if the authorization code flow is used instead, the malicious 3rd party app can still intercept the authorization code, and use it to obtain an access token since there is no client secret. So it seems useless to use the authorization code flow instead of implicit flow for public native apps.
  7. This is where PKCE comes in. PKCE makes it so that even if a malicious app intercepts an authorization code, it will not be able to exchange it for an access token. This is accomplished by requiring the entity that is requesting the access token prove that it is the same entity that requested the authorization code in the first place.

Hopefully now you understand why:

  • SPAs would not benefit at all from PKCE since with SPAs everything occurs within the browser. PKCE is only useful when there is inter-app communication.
  • Public native apps should not use the implicit flow because inter-app communication can sometimes be insecure.
  • Public native apps should use the authorization code flow + PKCE

Here is a good blog post that can provide more info: https://medium.com/@justinsecurity/mobile-apps-and-oauths-implicit-flow-68e72c6515a1

15
  • 3
    Thanks for the write up! I think I understand most of that but a few points here: There has been talk about ditching the implicit flow for SPAs (see here). Although PKCE solves a different problem, my argument is that it's better than just plain implicit flows, more of a defense-in-depth mechanism. Hash fragments DO end up in the browser history if you alter them, which is arguably a common use-case in a SPA - though your point about weblogs is valid!
    – someone1
    Jun 11, 2018 at 19:58
  • 2
    "First of all, for SPAs the current best practice is still to use the implicit flow, not the authorization code flow." - this article appears to suggest otherwise. You have a good point on the relative un-importance of PKCE for an SPA, but I don't think your assertion regarding implicit vs. auth code flows for SPAs is valid. Jul 27, 2018 at 18:23
  • 2
    Steve: The OP suggests that the Implicit flow is less secure than the Authorization Code flow (as mentioned in the linked article) because with the Implicit flow, access tokens will end up in web logs and the browser history. I thought that this was incorrect since the Implicit flow uses hash fragments. As the OP mentions in the first comment: hash fragments DO end up in the browser history.
    – el_tigro
    Jul 27, 2018 at 20:42
  • 2
    In the latest OAuth 2.0 recommendation for browser based apps (internet draft) it seems they added browser based apps as required to use PKCE. See my answer below for more details.
    – Eran Medan
    Mar 7, 2019 at 4:31
  • 2
    @SurajJain The browser is not a secure environment for storing a client secret, and thus, client secrets can not be used in the implicit/auth-code+pcke flows.
    – jmrah
    Jul 20, 2020 at 1:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .