Bampfer
Bampfer

Reputation: 2220

OAuth2 redirection fails with CORS error

I have a RESTful service which I have secured using Spring Security and pac4j-oauth. An important detail is that Google is acting as the OAuth2 server-- we need the user's Gmail address to know if they are a legal user of our system, and eventually the service will also need access to their calendar.

When the user first accesses a protected resource (typing in the URL) they get redirected to Google. Once they have authenticated they get redirected back to our service. This works fine.

The problem is that I want to call the service using Ajax. When I issue the first Ajax call it gets back a redirect to Google. This could mess up the Ajax call but in fact it doesn't even get that far. The browser sees the redirect and throws a CORS error: "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:8443' is therefore not allowed access."

I'm at a loss how to handle this. Suggestions would be welcome.

Some thoughts:

  1. If I replace the regular Ajax call with a JSONP call, the redirection still causes an error (because it returns a URL instead of a JSONP script) but maybe the client could detect that and navigate to Google on behalf of the service? Once the user authenticates, the client could then take them back to the protected resource again?

    This feels wrong though. For one thing it means that future Ajax clients of the RESTful service would have to jump through the same hoops. I'd prefer the clients not to have any dependencies on the server's security implementation.

  2. While CORS errors are common enough, I've only found a couple other people complaining about them specifically with OAuth2 authentication. So I'm also wondering if we took a wrong turn with our architecture somewhere. What exactly are we doing that's so unusual?

Any insight would be appreciated.

Upvotes: 1

Views: 4087

Answers (1)

Bampfer
Bampfer

Reputation: 2220

After more investigation here are my conclusions.

I'll start with #2 first. Yes, we probably took a wrong turn. Specifically, OAuth2 was designed for authorization, not authentication. (Though if we had been running our own OAuth2 server we might have gotten away with it.)

Google supports authentication of course: see their docs for Google Sign-In. But note the two options they list.

  1. Code it into the client. This doesn't work well for a RESTful service.
  2. One-time authorization code flow. This option makes perfect sense for a RESTful service. But again it's not intended to authenticate the user; it's about letting the user authorize our service to access their calendar. And they only need to do this once.

So the plan is that we'll have one webpage that offers the option to authorize our services to access their Google calendar. The user will click on the link explicitly to get the authorization from Google-- the RESTful service won't need to redirect their browser. This avoids the CORS problem. The client will then post Google's access token to our RESTful service.

Note that other consumers of our service don't need to implement this, they just need to let the user authenticate in a way that doesn't require a redirection to a third-party. Initially we'll use our own user/pwd database, with an eye towards hooking into a company directory or SSO in the future. Spring Security supports numerous options of course.

So: we had to compromise our original plan, but now we are dealing with flows that are well documented and have sample code. So I expect things will go much smoother from now on.

I hope this helps someone avoid the same OAuth2 design mistake we made, namely using it with a third-party to authenticate users of our RESTful service. There are more appropriate ways to authenticate users.

Upvotes: 1

Related Questions