2

It's seeming like the extra loop via the client backend in code flow only adds obscurity - not security. If, in the end, the client backend dishes out the same access token to the front end as the auth server would via implicit flow, what specific attack is mitigated by the extra back channel communication?

Having read around on the topic, it sounds like code flow's superiority is only achieved if the client front end never sees the access token. Is that true?

4 Answers 4

3

the client backend finally gives the code flow's access token in the URL fragment to the client front end

This should never happen; I think there still is (or at least was) some confusion here. I think you're referring to a backend-for-frontend architecture here, and in that case, the "client backend" - which is itself a server - should not ever send a redirect back to the "client frontend". For sure, doing so is not part of either the implicit or code grant flow specifications.

What a client backend would typically use to authenticate a client frontend would be a session cookie, as demonstrated here. The exchange of the token for resources would then typically be done with a resource server, directly from the "client backend" - so the frontend would never see the token. Some would argue that this setup is more secure than the client having a token e.g. because the token is stateless and can't be invalidated easily server side if compromised. But that's not really relevant to this question.

To keep things simple, let's forget about a "backend-for-frontend" architecture. We can show that even in the case of a static frontend (i.e. one without any "backend client"), the code grant flow - when used correctly with PKCE - does not result in tokens going to the browser history via a redirect. Sure, the tokens go to the client code running in the browser, but that's different. The key point is outlined in this OKTA article:

The response is returned on the same channel (as opposed to a redirect response with the Implicit flow) and, as such, that response will not be in your browser history. [Bold formatting mine]

If it's still not clear, let me attempt to explain, with steps, what happens with the implicit vs. code grant with PKCE flows, for a static website.

Implicit Flow

  1. User requests page from CDN
  2. Page loaded to user's browser
  3. Javascript (JS) on the page determines the user is not logged in & redirects the browser to the authorization server. As part of that redirect URL, it includes a redirect URL parameter (yes, a redirect parameter within a redirect URL - sometimes referred to as the callback URL maybe to avoid confusion) specifying where the auth server should redirect to after it's done. In this case it will be the same URL as the frontend page.
  4. Authorization server notes the redirect URL for later
  5. Authorization server sends login page as response
  6. User sends over credentials
  7. Authorization server validates the credentials and responds with the redirect URL it was given earlier and an access token in the URL fragment
  8. Browser gets the redirect response and it loads into the browser's address bar and history
  9. Browser redirects back to the client app
  10. Client app extracts the token from the URL fragment and stores it e.g. in local storage or else in memory.
  11. Client can now use that token in requests to the resource server.

The key vulnerability here, in boldface above, is that the access token went through the browser's history.

Code Grant Flow with PKCE

It starts off quite similar to the implicit flow, but diverges after a few steps.

  1. User requests page from CDN
  2. Page loaded to user's browser
  3. Before redirecting, a random value is created in memory. Let's call this k. This is saved somewhere e.g. session storage. Here the flows have diverged.
  4. Client JS now redirects the user to the authorization server, with a redirect (a.k.a. callback) URL to itself, just like it did in the implicit flow. But it also sends the hash of key, let's say h(k) as the code_challenge parameter.
  5. Authorization server notes the redirect URL for later
  6. Authorization server notes h(k)
  7. Authorization server sends login page as response
  8. User sends over credentials
  9. Authorization server validates the credentials and responds with the redirect URL it was given earlier plus a code that it just generated. Just like the token, the code indeed still goes to the browser history, as it's given via a redirect. So it's just as likely someone will get their hands on the code... BUT - see the following steps for why the code alone is not enough.
  10. Browser gets the redirect response and it loads into the browser's address bar and history
  11. Browser redirects back to the client app
  12. Client app extracts the code from the URL fragment
  13. Client extracts k from storage. A key point (no pun): k was never sent anywhere, so it's a secret that only the client can have.
  14. Client now directly sends code+k to the authorization server's token granting endpoint. This is done over a secure channel (HTTPS).
  15. Authorization server compares code to the code it generated and k to h(k) and if both match, it then returns a token as a response. Not as a browser redirect. This is probably the most crucial point in relation to the above misunderstanding.
  16. Client JS can now process the response and store it e.g. in local storage, session storage or in memory. There are still vulnerabilities here e.g. if it's being stored in local storage, an XSS attack on the frontend page would be able to access that storage. But discussing that is beyond the scope of this answer, whose sole purpose is to demonstrate a vulnerability that differentiates code grant from implicit.
  17. Client can now use that token in requests to the resource server.

Note, to avoid over-complicating matters, I did not talk here about another optional request parameter called state which can and probably should be used also in the above case, to prevent CSRF attacks. See here for an approach that also uses a state.


I will augment this answer with some diagrams. Like many diagrams you will find when Googling this subject, they are abstracted somewhat. However, I believe many of the other diagrams you'll find will abstract a little too much detail from what's happening client side. The main difference with these diagrams, to demonstrate the issue with the implicit flow, is the differentiation between the "browser" and the "webpage". The "browser" is the computer program Chrome/Firefox etc. running on your OS. Its code is predefined and unchangeable (presumably). The "webpage" is the Javascript and HTML that gets loaded in the browser. While yes, it runs in the browser, we must think of it as a separate process for the purpose of this demonstration. Also, we can think of the "browser" abstractly as a machine that can be asked to go to some URL, either by the webpage or the user, and after doing so, it will make a request to that URL and remember that request in its history. With that in mind, here are the flows.

Implicit

Notice the vulnerability, highlighted by the red square:

Implicit Flow

Code Grant

Code Grant Flow

Remarks

Arguably, my diagrams are themselves too abstract. You could say, sure, the token ends up in browser history. So what? How does that make it more vulnerable? To answer such questions, further explanation would be needed to detail how exactly such a vulnerability could be exploited. For me, I'm reasonably happy enough to accept, at least for now, various high-level explanations of why having a token in browser history is a bad thing. But a more thorough answer would also give a concrete example of this vulnerability being exploited in the real world. Perhaps an interested reader will find such an example and contribute it to this thread some day.

Also, the examples I have given are just one implementation of these protocols. The protocols themselves are abstract enough to allow multiple implementations. For example, there is nothing in the code grant flow dictating that a key needs to be stored in a browser's local storage - that is just an implementation detail I've added to help make this concrete.

Let me briefly return to the notion of a backend-for-frontend (BFF) since it was mentioned by the OP. With such an architecture, using code grant, it is possible to use a "client secret" instead of the "proof key" that was used in PKCE. This is possible because the server, being on the backend, can keep secrets, while a static webpage, being served publicly, cannot. However, the BFF could also use PKCE server side. Another choice with this architecture is whether you want the tokens sent to the client browser or not. For example, in this SO blog, the tokens are sent by the BFF to the frontend client (on the browser) as cookies & are used directly by the frontend client thereafter. But as I mentioned earlier you could also have the BFF securely store those tokens and then keep a session alive with the client frontend; all requests would then need to go through the BFF.

I will also note that even without PKCE, or a client secret in the case of the BFF architecture, the code grant is arguably more secure. In fact, as pointed out in the OKTA article:

It’s very short lived (60 seconds with Okta)

So think about an attacker program that tries to use it more than a minute later e.g. another malicious app that is only opened by the user hours later. The code, even if left unused for some reason, is no longer a vulnerability in this case.

and:

It can only be used once to be exchanged for tokens

This means that any attacker is forced to steal and use your code before you do so. This makes their attack extra tricky because they need to race with you or else try to cut you off.

That article is well worth the read.

2

1st question: it mitigates attacks described in OAuth 2.0 Security Best Current practice draft-ietf-oauth-security-topics-133.1.2. Implicit Grant, mainly:

  • browser history token leakage
  • compromised devices where an attacker can somehow manipulate the client (such as a malicious browser extension)

2nd question: the client will necessarily have to use the acces token. In the authorization code flow, however, the access token doesn't go through the web flow which helps with mitigating attacks on the web stack. On the contrary, it exchanges the authorization code against tokens on the /token API direct. Moreover, use of PKCE prevents an attacker from using an authorization code intercepted in the browser or on the host application (eg. on mobiles, by claiming the redirect URI which cannot be owned by a single application).

1
  • Thanks for the link! But in a case where the client backend finally gives the code flow's access token in the URL fragment to the client front end, it's as insecure as implicit flow, right? I mean, why does it matter in the end whether the one returning the access token is the auth server or the client backend? So to achieve that infamous code-flow security, we'd have to make it so that the front end doesn't see the token, eg. by proxying all resource request via the client backend, and having the access token in a secure HttpOnly cookie. Commented Jul 29, 2019 at 3:33
0

I'm not going to attempt to answer this question because I found a website and youtube video that can answer it infinitely better than me. Check out https://oauth.net/2/grant-types/implicit/

1
  • 1
    This is a link-only answer. Please include the relevant parts of the link in your answer here.
    – schroeder
    Commented Jun 14, 2022 at 19:24
-1

I will try in simple words. Authorization Code flow should be used by applications where you have a backend (like ASP.NET MVC, Spring,). This flow uses intermediate code value which in the backed will be exchanged to access token. After that access token can be stored securely in application backed and used whenever is needed. In this use case, access token is never exposed to the browser (if application will not reveal it).

Implicit flow send back the access token directly in the authentication response. When used in browser app (applications without a backend) it is necessary to store it securely which is not that easy. In this case it is adviced to use PKCS instead and short-lived access tokens.

4
  • No, this is wrong. The latest advice from the OAuth2 mailing list is to not use implicit anymore, but instead authorization code even for javascript and native mobile applications. Also, you should define stored securely. A backend can be hacked, its logs can contain the access tokens if not configured properly, TLS might not be used between backend servers, etc. Finally, an SPA will have to store the access token on the browser side somehow, you can't avoid that.
    – Tangui
    Commented Jul 27, 2019 at 15:05
  • Also, this point speaks more to why exposing/storing a token in the front end is less secure than in the backend, and less to the levels of security in the flows themselves, even if there is an obvious connection Commented Jul 29, 2019 at 3:37
  • Why do you think this answer is wrong? This is adviced to not use implicit flow. As a secure storage I mean for example in-memory. From my practice, im my projects i am not using regresh tokens and instead Access tokens on form of JWT, I use reference tokens. Commented Jul 30, 2019 at 10:14
  • @Tangui "an SPA will have to store the access token on the browser side somehow, you can't avoid that." I'm probably nitpicking here - because I assume you mean an SPA without any further backend support. But note that it is possible set up a backend-for-frontend architecture (BFF) so that the SPA doesn't need to store any tokens. See here. Of course, the SPA will still need to store something e.g. a session cookie. But arguably that's more secure as it can be more easily invalidated. Commented Jun 15, 2022 at 9:06

You must log in to answer this question.

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