5

I have an app where I want to sign in with a 3rd party API (such as Google, Facebook, etc.). The client side front end is just a JavaScript SPA that will interact with my server. The server essentially is only needed to store the 3rd party ClientID and ClientSecret.

To login, the client will link the user to MyAPI/login. MyAPI will then redirect the user to the 3rd party login page, along with the ClientID. Upon authentication, the 3rd party will redirect the user back to MyAPI/callback with a code query parameter. MyAPI will send this code back to the 3rd party API along with ClientID and ClientSecret. The 3rd party API will finally return an access_token and refresh_token.

My question is how I should then send the tokens back to the client application? And, once the client has the tokens, how should I store them?

enter image description here

Micah Cowell
  • 392
  • 4
  • 18
  • You could use a `Set-Cookie` http header. [Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) here. – takendarkk Nov 10 '17 at 23:34
  • @csm_dev Thanks, I went ahead and did that. I'm using `Set-Cookie` to create a temporary cookie for the client who then stores the tokens in local storage. Are there any security risks with storing the plain tokens in localStorage? – Micah Cowell Nov 11 '17 at 01:27
  • @getmicah there’s always a risk but it is an acceptable place to put them. This article outlines the different approaches and their pros and cons. They recommend cookies, but I think many people do use local/sessionStorage. https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage – WillyC Nov 11 '17 at 02:43

1 Answers1

3

What you are describing is the Authorization Code Grant flow of OAuth. In Auth Code flow, a code is provided to the client (in this case, MyAPI) so that the user never receives the access_token or refresh_token and thus doesn't have to be trusted with them.

By providing these tokens to the user and allowing them to store it in local storage, you are circumventing the security benefit of Auth Code flow. It is definitely not recommended to do this unless security isn't as big of an issue for you (you trust the user).

As the comments suggest, you can use cookies to persist a session with MyAPI. Still, the access_token and refresh_token should be kept in the session data and not shared directly with the user. This enforces that access to the 3rd party API is only made by MyAPI on behalf of the user.

A better explanation of Auth Code flow is provided in the accepted answer to this question: What is the difference between the 2 workflows? When to use Authorization Code flow?

An incomplete Express.js example of MyAPI:

// imports
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const jwt = require('jsonwebtoken');

// setup passport (an authentication middleware) and use it with the session provided by express-session
passport.serializeUser((user, done) => {
  done(null, user);
});
passport.deserializeUser((obj, done) => {
  done(null, obj);
});
passport.use(new SomeStrategy(...strategyOptions));
const app = express();
app.use(passport.initialize());
app.use(passport.session());

// route handlers
app.get('/login', passport.authenticate('SOME_STRATEGY'), () => {});
app.get('/callback', passport.authenticate('SOME_STRATEGY'), { failureRedirect: '/badlogin' }, (req, res) => res.send(200));
app.get('/resource', (req, res) => {
  const accessToken = req.user.access_token; // req.user has the session information including the access token
  try {
    // verify the access token with the 3rd party auth's public key (NOT THE SAME AS DECODING IT!) 
    const decodedAccessToken = jwt.verify(accessToken, thirdPartAuthPublicKey);
    return res.send(200).send(decodedAccessToken);
  } catch (err) {
    return res.send(401).send('unauthenticated');
  }
});

Here, the server keeps the access_token in the user's session data (req.user). When the user makes a request for the resource, the server tries to verify the access_token and returns its decoded contents.

Daniel Bank
  • 3,581
  • 3
  • 39
  • 50
  • Disregard that last comment. What do you think about this flow? https://tools.ietf.org/html/rfc6749#section-4.1 – Micah Cowell Nov 11 '17 at 07:42
  • Thanks for the example code. One more question: is it acceptable to instead store the token in an HttpOnly Cookie so that only the server can read it that? That way the user doesn't have to re-login every time they close the session? – Micah Cowell Nov 11 '17 at 19:42
  • I'm using this: https://github.com/gorilla/securecookie and enabling the HttpOnly and Secure flags. The cookie stored on the users machine is encrypted which should prevent the user from manipulating the access code before making a request to MyAPI – Micah Cowell Nov 11 '17 at 21:38
  • Isn't it a bad practice to generate the access token on server side with auth code grant type and then pass it on to the client (browser/native app) for storage? This [link](https://security.stackexchange.com/questions/134975/sending-access-tokens-from-server-to-client-best-practice) also states it. Would be curious to hear back. – Andy Dufresne Sep 26 '18 at 14:12
  • Yes, the access token should not be passed to the client. The decoded contents of it can be passed though (e.g. session state that a front end would need to render). – Daniel Bank Mar 21 '19 at 17:17