23

A lot of services today still recommend the implicit flow for an OpenID Connect/Oauth2 token exchange when developing Single-Page Apps. (See Okta, 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
    For all who didn't know what PKCE means (like me), a link to Proof Key for Code Exchange. – martinstoeckli Apr 3 '18 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 – patrickmcd Oct 9 '18 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 – patrickmcd Oct 9 '18 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 '18 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 '18 at 19:54
13

@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.

  • 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 at 10:18
  • 3
    Though still incorporated with "all kinds." – Brad J Feb 16 at 16:26
  • Great find - it'd be interesting to see how this draft develops! – someone1 Mar 4 at 4:00
  • 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 at 21:13
12

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.

  • 1
    Great find - it'd be interesting to see how this draft develops! – someone1 Mar 4 at 3:57
  • 2
    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." – Stian Fauskanger Apr 23 at 13:52
10

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

  • 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 '18 at 19:58
  • Also forgot to mention that some providers, such as Google, will not allow the use of prompt=none for implicit flows - though I don't think this is to spec. – someone1 Jun 11 '18 at 20:13
  • I’m not sure I follow the argument here. @catanman says “With the implicit flow, the access token is included in the hash fragment (#) of the redirect URI instead of in a query component (?).” except it doesn’t matter with the code flow, because your access_token doesn’t come back via the query in that flow either. – Steve Jul 15 '18 at 23:16
  • 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. – Jake Feasel Jul 27 '18 at 18:23
  • 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 at 4:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.