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.

cloudfeet
  • 2,528
  • 17
  • 22
  • 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 '13 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 '13 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 01 '13 at 06: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 01 '13 at 16:14
  • that depends on the oauth implementation. The first authentication step should be protected with a CSRF token to prevent this attack. – rook Jun 01 '13 at 17:16
  • You mean the service site generates a CSRF token, which is included in the "redirect URL" it passes to the login site, preventing login over-writes? That's an excellent point! – cloudfeet Jun 02 '13 at 00:02
  • 2
    The oauth "state" variable is intended to be used in this way. – rook Jun 02 '13 at 00:22
  • Hmmm... no more answers yet. So is CSRF the only weakness, and can be fixed by including a CSRF token in step #1? – cloudfeet Jun 13 '13 at 13:43
  • So what is CORS? It breaks the SOP is a very specific way that could allow an attacker to read any response. If you require a secret token to obtain a response, then you have a solid protection against this weakness of CORS. OAuth is a great design and you should try and use this standard when possible. – rook Jun 13 '13 at 15:42
  • I feel you might be missing my point slightly. I would *like* to use OAuth (or a commonly-used OAuth-shaped SSO pattern, or whatever). However, if the user is logged in on the OAuth provider, and has already told the OAuth to grant access to my site when asked, is there a way of obtaining an OAuth token without refreshing the whole page? – cloudfeet Jun 14 '13 at 06:44
  • I don't see how that is a problem at all. Sure the oauth token msut be given by a callback url... but so what, just update the session state and then the service will reflect this new authentication. This is a problem with your application not with the oauth protocol. Don't reinvent the wheal due to lack of imagination. – rook Jun 15 '13 at 00:00
  • Sounds similar to JSON web tokens. http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html – nowen Apr 07 '14 at 21:12
  • Sounds a bit like JSON Web tokens? http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html. Have you seen that effort? – nowen May 05 '14 at 15:26

1 Answers1

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).

rook
  • 46,916
  • 10
  • 92
  • 181