Make a secure oauth API with passport.js and express.js (node.js)
Asked Answered
K

2

18

I've got I think a specific problem, unless I'm not doing things in the right way.

I've got an application with 2 sides, a client (html site), and an API (built with express + mongodb). The API will need to be securely accessed. They both are on different domains, for now let's say my site is on domain.com and my API is on api.com:3000.

I've added passport to get an access token from Github as I'm creating my users with their data, so I can have a "Sign in with Github" basically.

My current process is:

1) the client (the site), opens a popup on the API

window.open("http://api.com:3000/oauth")

2) The express server, start the passport process:

app.get('/oauth', passport.authenticate('github'), function(req, res) {

});

For the strategy, I used this code.

3) I redirect the callback to a url that closes the popup (javascript with a window.close()):

app.get('/oauth/callback', passport.authenticate('github', { failureRedirect: '/' }), function(req, res) {
    res.redirect('/auth_close');
});

At that point, all is fine, I am now logged in the API. My problem is how to get data back to the client, as the client doesn't know anything yet about accessToken, user or user id.

So, from the client (the site that opens the popup), I tried different approaches:

  • getting a value from the popup: doesn't work because of the redirection, I lose track of the popup javascript information

  • calling my API to get the "current user" in session, such as http://api.com:3000/current One more request, not ideal, but this one work. However, I still have a problem.

This /current url is returning the user if I reach it from the browser, because the browser send in the header request the express session cookie:

Cookie:connect.sid=s%3AmDrM5MRA34UuNZqiL%2BV7TMv6.Paw6O%2BtnxQRo0vYNKrher026CIAnNsHn4OJdptb8KqE

The problem is that I need to make this request from jquery or similar, and that's where it fails because the session is not sent. So the user is not returned:

app.get('/current', function() {
    // PROBLEM HERE, req.user is undefined with ajax calls because of the session!
});

I found a way of making it work but I'm not happy with because I'll have cross-browsers CORS problems, it is adding to express:

res.header("Access-Control-Allow-Credentials", "true");

And add the withCredentials field in a jquery ajax call, as explained here.

A map of fieldName-fieldValue pairs to set on the native XHR object. For example, you can use it to set withCredentials to true for cross-domain requests if needed.

$.ajax({
   url: a_cross_domain_url,
   xhrFields: {
      withCredentials: true
   }
});

I am also not happy with this as I'm losing the wildcard on my header: Access-Control-Allow-Origin, I need to specify a domain, as explained here.

So, I'm not sure the approach I should take here, the only thing I need is the client getting either an accessToken, or a userID back from the passport oauth process. The idea is using that token in each call to the API to validate the calls.

Any clue?

Kucik answered 18/12, 2012 at 13:23 Comment(2)
Did you find something? I'm with the same problem :(Rhynd
You can see my this answer regarding the same. It works for me.Finally
P
13

I had the same issue. I was using the Local Strategy on the login page, and then checking to see if the user was on the session on other requests.

As you say, the solution is to use CORS in order for the session ID to be passed in a cookie using XMLHTTPRequest.

Instead of using the CORS which does not yet work on all browswers, I deceided to use access tokens on other requests. The workflow I used is as follows:

POST /login
  • Username and password get passed in the body.
  • Authentication using Local Strategy.
  • Response returns the user object, including the access_token

GET /endpoint/abc123?access_token=abcdefg

  • Token obtained from the login response
  • Authentication using Bearer Strategy (in passport-http-bearer)

Sessions are now not needed in Express.

I hope this alternative helps.

Peursem answered 23/1, 2013 at 16:0 Comment(4)
I went with tokens as well. However, I used the Bearer Authentication strategy with an Authorization Header. Then with Express using Passport HTTP Bearer : github.com/jaredhanson/passport-http-bearerHowse
Regarding the token generation, is it secure to have one token that never expires? DO i have to make tokens expire and generate new ones?Titanism
I think of tokens like I think of passwords - some sites expire them automatically (e.g. like OAuth), and others do not (e.g. Amazon Web Services). If you treat a token like a password - never pass it over non-secure HTTP, encrypt them in your DB, allow user to manually regenerate, etc., then you may not need to expire automatically. Different people have different thresholds for security vs. usability.Peursem
How do you make sure that the token cannot be intercepted and replayed? If you cannot do that, it is ESSENTIAL to expire the token fairly rapidly.Hypothalamus
F
1

Before configuring anything in express app, use the following(exactly the same) to set header of response for cross-domain :

app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
     res.send(200);
 } else {
     next();
 }
});
Finally answered 26/11, 2013 at 13:52 Comment(1)
could you explain what is the meaning of the sintax line by line? and what if I dont use it?Ralf

© 2022 - 2024 — McMap. All rights reserved.