37

The Problem:

Serving a secure API to a client side app using only a local authentication strategy.
The red arrows are part of the knowledge gap.

enter image description here

Context:

That is --- client.example.com is making a POST to api.example.com/login where on success client.example.com can gain access to a GET service like api.example.com/secret.

An idea!

Implimentation of OAuth 2.0 with hybrid grant type sitting in front of API.

Why hybrid?

  • It wouldn't be an Implicit Grant Flow aka Client-Side Web Applications Flow because there is no redirection to API server too grant access token. (i.e.) "Is it ok for so-and-so to access your data?"

  • It wouldn't be a Resource Owner Password Flow because a Client ID and Client Secret are passed along with the request so it's assumed the client app is server-side.

OK... so what about a little bit of both?

What if we used a CRSF token on page load of client-side app, and POST it with user credentials too OAuth 2.0 authentication endpoint to exchange for access token? You would authenticate each subsequent request with the access token and CRSF token after a successful login.

A good Node.js OAuth 2.0 library I found:

https://github.com/ammmir/node-oauth2-provider

Help Me!

I can not find a working example of an authentication measure that solves this problem! Point me in the right direction?

Ultimately, the goal here is too authenticate a client side app to a REST api using CORS with a local strategy --- i.e. username & password --- even if the convention above isn't possible.

To Accommodate Bounty:

This is a client side app, so let's stay trendy.

I'm looking for a working example using the Node.js OAuth 2.0 seed above for the API/Auth server and a front end framework like Angular.js or Backbone.js to make requests.

The example should match the context described above.

Dan Kanze
  • 18,485
  • 28
  • 81
  • 134
  • mind if I ask whats on the box? if its windows server 2008r2+ I was wondering if a locked down service account might be able handle the local auth issue? – Bryan Devaney Apr 23 '13 at 14:16
  • @BryanDevaney Think web dev stack. CentOS 6.4 VM with NGINX server proxy passing node.js server to api.example.com in one doc root, static client.example.com to another. Using `forever` to daemonize/nohup. You could also just use two different VM's to make it easier :D ... whatever works! – Dan Kanze Apr 23 '13 at 14:27
  • So write an entire Node server that implements OAuth 2.0 with an Angular/ Backbone front-end for 100 reputation? Good luck. – AlbertEngelB Apr 23 '13 at 14:35
  • 1
    @Dropped.on.Caprica The OAuth 2.0 server seed above is already implimented including the login/secret routes out of the box with the possibility of a little tweaking. It's just a matter of getting it to play nice with a whopping 2 requests on a front end framework. I didn't think it would be that difficult for someone that's done this before but if no one catches naturally I'll raise the bounty. Thanks for your concern though. – Dan Kanze Apr 23 '13 at 14:39

5 Answers5

3

I'm working on an app with a pretty similar architecture though the services are .NET Web API rather than Node and we're using DotNetOpenAuth for the OAuth provider. Rather than the hybrid approach you're suggesting we're doing the following:

  1. x.com serves up a login page
  2. login page POSTs back credentials to x.com
  3. server side logic at x.com combines client_id and client_secret with the credentials to submit a token request (resource owner password credentials grant that you've mentioned above) receiving back both a temporary access token and a refresh token
  4. the refresh token is encrypted into a cookie issued by x.com
  5. both the cookie (with encrypted refresh token) and the temporary access token are then sent to the browser
  6. the client app (angular in my case) can now use the access token to hit api.x.com for services (It appears you're well aware of the limitations of CORS... we hacked a version of angular's $resource to facilitate this but it wasn't pretty since we wanted to use all HTTP verbs and support IE9)
  7. when the access token expires, the client side app can request a new access token from x.com
  8. server-side, x.com decrypts the cookie to get at the refresh token and issues another oauth call for a new access token

This is fairly high-level but hopefully gives you a sense for how to tackle your situation. In my case, and it appears in yours, we didn't want to use session state or a database to store the refresh token but obviously exposing that to the browser introduces security concerns so the encryption of the refresh token is important (among other security considerations) and the use of the cookie eliminates the need for session state or other persistent storage on x.com.

Community
  • 1
  • 1
jandersen
  • 3,551
  • 22
  • 23
  • This is really interesting, do you have source for client side implimentation? – Dan Kanze May 01 '13 at 14:10
  • Unfortunately I can't share any source code... But if you have some specific follow-up questions, let me know. – jandersen May 02 '13 at 01:02
  • Is OAuth2 endpoint sitting in front of api.x.com or x.com? If x.com is sending access_token and refresh_token, how does api.x.com know about it them? --- what storage method? Also, couldnt Mallory CSRF refresh token? – Dan Kanze May 02 '13 at 22:02
  • In my setup the oauth provider is a separate 3rd component, I'll call it auth.x.com. api.x.com knows how to validate tokens issued by auth.x.com (they have eachother's public keys; no back-channel request in this implementation but important that access tokens have a relatively short expiry). x.com has the endpoint called by the browser for decrypting the cookie and using the refresh token to get another access token from auth.x.com. That particular endpoint can be protected from Mallory by checking the referrer header, including/requiring a CSRF token, etc. – jandersen May 03 '13 at 02:42
1

Not an answer running for the prize. Just my 2 cents :)

On my web server,

I do my authentication through a rest call with login/password with basic authentication over https. This call delivers a key to the client (a one page web app).

Then every subsequent REST call is signed with the key. The server checks that the signature is correct and everything still happen in https.

This mechanism is quite used I believe.

I don't see the issue with cross domain. I have a single source anf if I need something from another source, I'd use JSONP.

I use nginx as an https->http forwarder.

Not sure how it competes with an OAuth2 solution.

unludo
  • 4,912
  • 7
  • 47
  • 71
  • Check out this link: http://avalanche123.com/blog/2011/10/10/cross-domain-javascript-lessons-learned/ towards the bottom of the article it goes into limitations of basic auth cross domain... Basically it's impossible because of browser compatibility issues... :( – Dan Kanze Apr 24 '13 at 17:27
  • @DanKanze With REST you can avoid the HTTP/1.1 401 Authorization Required phase by directly providing the username/password. You avoid one loop. It still returns a 401 if no credential is provided at 1st, but you can avoid this. It works well with my web app. – unludo Apr 24 '13 at 20:28
  • @DanKanze By the way for me oauth2 is needed when a server needs to keep credentials for accessing another server on behalf of an end user of this server. Not really the use case here? – unludo Apr 24 '13 at 20:32
  • Very cool. I'm thinking though that in the future calling API too accept different authentication contexts like `Implicit` grants or `Authorization Code` grants would become entirely different implimentations so I'm trying to build solution that accomodates every context from the beginning. – Dan Kanze Apr 24 '13 at 20:37
  • The client side app authenticating with username/password is one edge case, so I am trying to "sneak" it into an existing OAuth2.0 solution from the beginning. This edge case, as you correctly describe, is not part of the official specification. However, it is very close too the `Resource Owner Password Flow` which, directly returns an access_token on a successful username/password/client_id/client_secret POST as a server-side client app --- and makes me think it could be tweaked to accomodate a client-side client app. – Dan Kanze Apr 24 '13 at 20:38
1

I've built this example using Node and PassportJS to show how to authenticate the users with Facebook or Local Strategy. Both sides are on different domains as you described and it requires CORS enabled.

GitHub: https://github.com/pablodenadai/Corsnection
Live demo: http://corsnection-client.herokuapp.com/

Pablo
  • 2,540
  • 1
  • 18
  • 26
0

I can't promise that I have time to write working example but I can show you 2 paths :)

The biggest deal is CORS. After you solve that problem it is easy to use $http service. So, first and probably easiest may be to configure reverse proxy in x.com webserver which points to api.x.com. I wrote article here

Second approach is better, and created for exactly this purpose, to authorise specific domain to use your resource. It involves a bit of coding in api.x.com so you don't have to change anything in new web applications served in other domains. You simply need to authorise CORS requests in api.x.com service.

  • Create table in database where you can manage list of authorised domains
  • Add in that table record "x.com"
  • in api.x.com add request filter/interceptor what ever tech term you use for method which should be invoked after request is handled and add in response Access-Control-Allow-Origin: x.com if request comes from x.com (in other words check in request header refer value match to any value in table above and put that value in Access-Control-Allow-Origin response header).

That is all :) After this if you know how to use $http or jQuey.ajax you will be able to POST/PUT/DELETE/... any request to api.x.com from any authorised domain in just few minutes.

Community
  • 1
  • 1
Milan Jaric
  • 5,556
  • 2
  • 26
  • 34
  • I'm sorry Milan but proxy passing is not an issue, neither is implimenting a CORS request. I am tryng to hold authentication of users during requests --- that is --- Once I "log in" I have access too API service gaurded by the authentication endpoint. There are also browser hurdles you need to solve using purely CORS implimentation that leverages sessions for user logins... Nearly impossible. That's why I believe an OAuth 2.0 hybrid is the only way to solve this. – Dan Kanze Apr 23 '13 at 22:46
  • give me more details what you are using aside from node-oauth2-provider in api.x.com (like express or something). I will take some time tomorrow morning to make example... will post github link for you. – Milan Jaric Apr 23 '13 at 23:17
  • Ok cool! The node-oauth2-provider seed is sitting on top of express 3.0 in the example they provide so that's fine. I would really like too see it paired with Angular.js. The POST method for login in the example is really what you want to look at. In the provider class itself (pretty small) you will see the Resource Owner Password Flow, which will accept --- username, password, client_id, client_secret. That is where some hackery might take place as I described above there really is no "client_id or client_secret". – Dan Kanze Apr 23 '13 at 23:35
0

I very similar idea using vinilla js web app and cross domain authentication to GAE backend or OpenID connect.

The web app is run on CDN. When click login link, it goes to respective login server and redirect back to the web app (with XSRF security token and HTTPS only cookie). Login server accept cross domain request with credentials. XSRF token has to be set (in header) with every request. cookie is set by the browser. Since it is HTTP only cookie, JS cannot read it. The technique is very secure.

Once login, you can get secure assess from login server.

For detail description, you can find here and open source repo here.

Kyaw Tun
  • 12,447
  • 10
  • 56
  • 83