How to integrate OAuth with a single page application?

25,882

Solution 1

Most of the time, a redirect is okay even for SPA because users don't like to put their X service credentials on any other website than X. An alternative will be to use an small popup window, you can check what Discourse does. IMHO a redirect is better than a popup.

Google Some providers support the resource owner flow which is what you described as sending username and password, but this is not nice. These are the problems I see:

  1. Asking google credentials to users in your site will be a no-go for some users.
  2. The resource owner flows need the client_secret too and this is something that you must NOT put in your client side javascript. If you instantiate the resource owner flow from your server-side application and your application is not in the same geographically region than the user, the user will get a warning "hey someone is trying to access with your credentials from India".

OAuth describes a client-side flow called implicit flow. Using this flow you don't need any interaction in your server-side and you don't need the client_secret. The OAuth provider redirects to your application with a "#access_token=xx". It is called implicit because you don't need to exchange authorization code per access token, you get an access_token directly.

Google implement the implicit flow, check: Using OAuth2 for Client-Side apps.

If you want to use the implicit flow with some provider that doesn't support it like Github, you can use an authentication broker like Auth0.

disclaimer: I work for Auth0.

Solution 2

What José F. Romaniello said is correct. However, your question is broad and thus I feel any offered conclusions are just generalities at this point.

Application state

For example, without knowing how complex your application state is at the time you want to let your users log in, nobody can know for sure if using a redirection is even practical at all. Consider that you might be willing to let the user log in very late in his workflow/application usage, at a point where your application holds state that you really don't want to serialize and save for no good reason. Let alone write code to rebuild it.

Note: You will see plenty of advice to simply ignore this on the web. This is because many people store most of the state of their application in server-side session storage and very little on their (thin) client. Sometimes by mistake, sometimes it really makes sense -- be sure it does for you if you choose to ignore it. If you're developing a thick client, it usually doesn't.

Popup dialogs

I realize that popups have a bad rep on the web because of all their misuses, but one has to consider good uses. In this case, they serve exactly the same purposes as trusted dialogs in other types of systems (think Windows UAC, fd.o polkit, etc). These interfaces all make themselves recognizable and use their underlying platform's features to make sure that they can't be spoofed and that input nor display can't be intercepted by the unprivileged application. The exact parallel is that the browser chrome and particularly the certificate padlock can't be spoofed, and that the single-origin policy prevents the application from accessing the popup's DOM. Interaction between the dialog (popup) and the application can happen using cross-document messaging or other techniques.

This is probably the optimal way, at least until the browsers somehow standardize privilege authorization, if they ever do. Even then, authorization processes for certain resource providers may not fit standardized practices, so flexible custom dialogs as we see today may just be necessary.

Same-window transitions

With this in mind, it's true that the aesthetics behind a popup are subjective. In the future, browsers might provide APIs to allow a document to be loaded on an existing window without unloading the existing document, then allow the new document to unload and restore the previous document. Whether the "hidden" application keeps running or is frozen (akin to how virtualization technologies can freeze processes) is another debate. This would allow the same procedure than what you get with popups. There is no proposal to do this that I know of.

Note: You can simulate this by somehow making all your application state easily serializable, and having a procedure that stores and restores it in/from local storage (or a remote server). You can then use old-school redirections. As implied in the beginning though, this is potentially very intrusive to the application code.

Tabs

Yet another alternative of course is to open a new tab instead, communicate with it exactly like you would a popup, then close it the same way.

On taking user credentials from the unprivileged application

Of course it can only work if your users trust you enough not to send the credentials to your server (or anywhere they don't want them to end up). If you open-source your code and do deterministic builds/minimization, it's theoretically possible for users to audit or have someone audit the code, then automatically verify that you didn't tamper with the runtime version -- thus gaining their trust. Tooling to do this on the web is nonexistent AFAIK.

That being said, sometimes you want to use OAuth with an identity provider under you control/authority/brand. In this case, this whole discussion is moot -- the user trusts you already.

Conclusion

In the end, it comes down to (1) how thick your client is, and (2) what you want the UX to be like.

Solution 3

OAuth2 has 4 flows a.k.a. grant types, each serving a specific purpose:

  • Authorization Code (the one you alluded to, which requires redirection)
  • Implicit
  • Client Credential
  • Resource Owner Password Credential

The short answer is: use Implicit flow.

Why? Choosing a flow or grant type relies on whether any part of your code can remain private, thus is capable of storing a secret key. If so, you can choose the most secure OAuth2 flow - Authorization Code, otherwise you will need to compromise on a less secure OAuth2 flow. e.g., for single-page application (SPA) that will be Implicit flow.

Client Credential flow only works if the web service and the user are the same entity, i.e., the web service serves only that specific user, while Resource Owner Password Credential flow is least secure and used as last resort since the user is required to give her social login credentials to the service.

To fully understand the difference between recommended Implicit flow and Authorization Code flow (the one that you alluded to and requires redirection), take a look at the flow side-by-side:

Comparing 'Implicit' and 'Authorization Code' flow side-by-side

This diagram was taken from: https://blog.oauth.io/introduction-oauth2-flow-diagrams/

Share:
25,882

Related videos on Youtube

Golo Roden
Author by

Golo Roden

founder and cto @thenativeweb. loves javascript, node.js, lisp, apples and raspberries. favors unix and the shell. spreads knowledge. mvp. he/him.

Updated on July 09, 2022

Comments

  • Golo Roden
    Golo Roden almost 2 years

    When using OAuth (2) I need a redirection endpoint in my application that the OAuth-offering service can redirect to, once I have been authenticated.

    How do I handle this in a single page application? Of course, a redirect to the OAuth-offering service is not nice here, and it may not even be possible to redirect back.

    I know that OAuth also supports a username / password based token generation. This works perfectly with an AJAX call, but requires my single page application to ask for a username and password.

    How do you usually handle this?

  • José F. Romaniello
    José F. Romaniello almost 10 years
    This add a lot of information missing in my former answer, good job!. Something else I'd like to add from my experience, popups are a bad idea if you plan your app to work in mobile devices and tablets, technically it introduce a complexity since the parent tab "freezes" so you can't watch the child or listen events, then it feels wrong from UX. So, if you plan to support mobile, popups is a no-go.
  • tne
    tne almost 10 years
    @JoséF.Romaniello Great point! Do you know how widespread the problem is? I can't seem to find compatibility listings for quirks regarding window.open anywhere, though I believe you that behavior is certainly not consistent. Not sure if full x-doc messaging support means good behavior or not; if so, maybe it's getting better. Modal dialogs can also make it work (most authorization systems outside the web actually use modals, but for different reasons), but I can't find any compatibility listings either. They may also require special UA permissions.