9

I like the way that OAuth/OpenID can authenticate/identify a user from another domain, but only if the other domain allows it (presumably on the user's instructions).

I would like to do something similar, but using CORS AJAX or an alternative like JSONP. The problem is that when using a whole-page redirect, the "login domain" can be certain about which domain it's supplying the auth_token/info to, because it's issuing an HTTP Redirect itself. However, that's not true for JSONP.

My current thoughts are below, and my questions are:

  • What are the weaknesses in this pattern?
  • Is there a way of doing this without two separate requests (for the non-CORS case)?

General case:

EDIT: This section originally assumed a JSONP request - in fact, I think it could be any type of data

Case A: user logged in on the "login domain" (using cookies)

  1. The client makes a request to the "login domain", supplying a URL on the "service domain"
  2. The login server looks at the supplied URL, and thinks "yes, that's fine", and returns an HTTP Redirect (3XX) response that includes an auth_token/whatever in the URL
  3. The client follows the redirect back to the service domain. The service domain stores the auth_token/whatever in the session, and returns a static resource (e.g. an image)
  4. The client then makes a second request to the service domain (no cross-origin allowed) to retrieve the auth_token/whatever.

Case B: user not logged in on the "login domain", or has not authorised sharing details with the "service domain".

  1. The client makes a request to the "login domain", supplying a URL on the "service domain"
  2. The login server returns an HTTP Redirect (3XX) response that includes a "not authorised" status in the URL (or perhaps the URL of a full-page OAuth-style login)
  3. The client follows the redirect back to the service domain. The service domain stores the "not authorised" status in the session, returning a static resource to the client.
  4. The client then makes a second request to the service domain (no cross-origin allowed) to find out that authorisation/identification failed.

Case C: other site attempting to inspect login status

  1. The user navigates to the dodgy site
  2. The client makes a request to the login server, supplying a URL on the actual service domain
  3. The login server redirects to some page on the actual service domain, which returns a static result
  4. The user may or may not be logged in - but the client can't find out because the "service domain" endpoint that would tell them is prohibited by the cross-origin restriction

CORS AJAX case

If the "login server" endpoint has CORS enabled, then the request could be made as an AJAX request. If the "service domain" endpoint does not have CORS enabled, the final result of this AJAX request could actually be an echo of the auth_token/whatever, instead of requiring a second request.

  1. The client makes a CORS AJAX request to the "login domain", supplying a URL on the "service domain"
  2. The login server returns an HTTP Redirect (3XX) response that includes the auth_token/failure/whatever in the URL
  3. The client follows the redirect back to the service domain.
  4. The service domain returns a document containing all the info supplied in the URL (auth_token/whatever).

In fact - this "echo the auth_token" behaviour could even be used instead of the static resource from the above section, thus supporting both models with the same endpoint.

13
  • This would also let me do JSONP/CORS requests between domains, if the other domain is authorised by the user / login server. If the other server doesn't have a required auth_token/whatever, it stores the original request data somewhere, forwards the client on to the login domain, and then waits for the redirect sequence to come back to it, at which point it can take the auth_token (or lack thereof) and process the stored request.
    – cloudfeet
    May 31, 2013 at 15:21
  • It could even work for cross-domain POST/PUT/etc. - if the "service domain" saves the data and redirects to the "login domain" with a 303 (instead of a 302), then the redirects should be continued with GET.
    – cloudfeet
    May 31, 2013 at 17:11
  • I don't see why you can't use oauth and then use the token to restrict access to the CORS interface. There is no reason to reinvent the wheel.
    – rook
    Jun 1, 2013 at 6:50
  • The question is more how to get the OAuth token in the first place. If the user is signed in, and has already told the OAuth provider about your app, then your app can obtain an OAuth token without any actual input from the user (just forwarding the browser between sites). I was looking for a way to do that (or the similar problem of SSO) with AJAX instead.
    – cloudfeet
    Jun 1, 2013 at 16:14
  • 2
    The oauth "state" variable is intended to be used in this way.
    – rook
    Jun 2, 2013 at 0:22

1 Answer 1

6

OAuth is flexible, and sometimes the OAuth flow is modified for application specific needs. The most common two flows are 2-legged and 3-legged, which if these flows are implemented correctly, then they are generally accepted as secure.

The proposed CORS AJAX implementation of the OAuth flow violates two security requirements of RFC-6749 - OAuth 2.0 Authorization Framework. This deviation from the standard undermines the ability for the authorization server to properly validate the client, and the client's intentions.

  1. There must be a control in place to prevent a malicious JavaScript client from falsifying interactions with the authorization server. If the URL is supplied as an argument within a CORS Ajax request, rather then checking the HTTP origin header, then a malicious client is permitted to lie about it's context. Further more, a redirection is used for mutual authentication of the authorization server and the client. Not having these controls in place is a violation of RFC-6749 - 10.2. Client Impersonation:

A malicious client can impersonate another client and obtain access to protected resources if the impersonated client fails to, or is unable to, keep its client credentials confidential.

The authorization server MUST authenticate the client whenever possible. If the authorization server cannot authenticate the client due to the client's nature, the authorization server MUST require the registration of any redirection URI used for receiving authorization responses and SHOULD utilize other means to protect resource owners from such potentially malicious clients. For example, the authorization server can engage the resource owner to assist in identifying the client and its origin.

  1. The OAuth flow must be initiated by the user. The OAuth state parameter is used to prevent CSRF when establishing authentication. Forcing a user to login to a web application is a useful link in a session-riding attack chain. After a session has been established an attacker can control the newly created session using CSRF, or XSS. The failure to prove the user's intention in this context is a violation of RFC-6749 - 10.12. Cross-Site Request Forgery:

A CSRF attack against the client's redirection URI allows an attacker to inject its own authorization code or access token, which can result in the client using an access token associated with the attacker's protected resources rather than the victim's (e.g., save the victim's bank account information to a protected resource controlled by the attacker).

You must log in to answer this question.

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