3
votes

I am a little confused as to how I should use the OAuth 2.0 Authorization grant for my web app.

I understand that the Implicit grant for SPAs is now decommissioned and that I should use the Authorization grant.

However the articles and documentation I have read, and the videos I have watched still leave some ambiguity.

If I have a React JS front end and a Spring Boot back end I see three ways in which I could do a token exchange. So when a user clicks login I could:

1) Have my React front end perform the authorization grant without the client secret and obtain the token as recommended by the OAuth 2.0 docs for SPAs

2) Have my React front end perform half of the authorization grant, i.e. get the authorization code. Then pass this on to my back end to exchange the authorization code with the client secret to get a token back, and then pass this token to my front end.

(This is the approach recommended here: what's the alternative to password grant now that it is deprecated? OAUTH 2.0).

To be honest I don't understand how this would work as surely the client IDs for the front end and back end would be different?

3) Have my React Front End delegate the authorization flow to my back end server which can perform the authorization grant using the client secret.

Have I misunderstood something or are all of these approaches possible? What is the recommended approach here?

1

1 Answers

2
votes

These are all viable, but risks are different. Probably the most important difference is what component has access to what during authentication. Consider each of these components could be compromised, what would an attacker gain.

Let's start with #3 above. As a user of this application, I would have to enter my credentials in the SPA, which would then pass it to your backend. Credentials are revealed to both the SPA and the backend, if any of those is compromised, an attacker gains access to username and password pairs directly. This is a risk that you probably don't want to take if you don't have to. Also if the identity provider is a public one (not specific to your application), this would not work, ie. I would not want to enter my Facebook password in your app.

#1 above is better from this perspective, because your backend never has access to actual user credentials (their password). Depending on implementation, the SPA does not need access either, if you redirect your user to the identity provider to enter their password, but that's a UX tradeoff. In this case both the SPA and your backend have the access token, which is susceptible to potential XSS attacks. If either is compromised, the attacker might gain access to the access token and use it while it's valid.

#2 further improves this by the SPA never having access to the access token, only the auth code. In this case the SPA would likely have a regular session (with the session token probably in a httponly cookie) with your backend, which is secure against XSS, and the backend could query whatever resource it has access to with the exchanged access token. You could argue that the SPA had the auth code which an attacker could also exchange for an access token, but on the one hand an auth code should be a one time token, and also the SPA need not store the auth code, it only needs to pass it to the backend once.

The reason all of these exist and are viable is because sometimes you can't do one or the other for reasons outside security. Choose the one that exposes the least things to the least components in your application, while still fulfilling functional, UX and other requirements, and always understand the risk that you are implicitly accepting.