jaredhanson / passport-oauth2

OAuth 2.0 authentication strategy for Passport and Node.js.
https://www.passportjs.org/packages/passport-oauth2/?utm_source=github&utm_medium=referral&utm_campaign=passport-oauth2&utm_content=about
MIT License
607 stars 343 forks source link

How do I disable the sessions? #145

Open juanlet opened 3 years ago

juanlet commented 3 years ago

Hey, I'm getting the access token correctly on the verification function

function(req,accessToken, refreshToken, profile, cb) {
     console.log("COGNITO LOGIN RESULT",accessToken, refreshToken, profile);
    //I just need to pass the access token to the final destination    
     return cb(null, req);
  }

I have this route, trying to set session to false, with no success

router.get('/', passport.authenticate('oauth2',{ session: false})); also I have this other route, same, I try to set session to false

router.get('/callback', passport.authenticate('oauth2', { failureRedirect: '/failure', session: false }), 
 (req,res)=> {
  res.redirect('/api/v1/auth/cognito/success');
});
router.get('/success', (req,res,next)=> {
   // I need to do something with the accessToken here
  res.render('cognito-auth-result', { result: 'success' });
}); 

Even if I got the token successfully I get an error

req.logIn is not a function TypeError: req.logIn is not a function

I suspect is expecting to have something set like req.user in the session, how can I achieve what I want?. I'm just going to use JWT's. thanks

xAt0mZ commented 3 years ago

@juanlet I think I'm trying to achieve something maybe similar: login using OAuth (Authorization Code Flow) and manage my user authorization to my API through JWT (won't expose the token to clients).

This code sample may help you understand what is going behind the library (explored it along with passport to understand the mecanisms).

var express = require('express');
var passport = require('passport');
var OAuth2Strategy = require('passport-oauth2');

const CLIENT_ID = 'YOUR_APP_ID_GOES_HERE';
const SECRET = 'YOUR_APP_SECRET_GOES_HERE';
const CALLBACK_URL = 'http://localhost:3000/oauth2/callback';

var app = express();

OAuth2Strategy.prototype.userProfile = function (accessToken, done) {
  // do your stuff here to fetch user profile from your OAuth API if you need/want to
  // otherwise delete the function

  // var options = {
  //   url: 'https://some.api.example.com/users',
  //   method: 'GET',
  //   headers: {
  //     'Client-ID': CLIENT_ID,
  //     'Accept': 'application/json',
  //     'Authorization': 'Bearer ' + accessToken
  //   }
  // };

  // using here request (https://www.npmjs.com/package/request) for demo/syntax purpose

  // request(options, function (error, response, body) {
  //   if (response && response.statusCode == 200) {
  //     done(null, JSON.parse(body));
  //   } else {
  //     done(JSON.parse(body));
  //   }
  // });
}

passport.use('oauth2', new OAuth2Strategy({
  authorizationURL: 'https://some.api.example.com/oauth2/authorize',
  tokenURL: 'https://some.api.example.com/oauth2/token',
  clientID: CLIENT_ID,
  clientSecret: SECRET,
  callbackURL: CALLBACK_URL,
}, function verify(accessToken, refreshToken, profile, done) { // just naming it 'verify' for further comments
  // 'profile' is what your returned from 'userProfile()' function above

  // Securely store user profile in your DB

  // User.findOrCreate(..., function(err, user) {
  //   done(err, user);
  // });

  // you can save 'accessToken' and 'refreshToken' in 'user'
  // and not use the 3rd 'info' param if you prefer to
  const user = profile; // normally generated from above User.findOrCreate(), shortening for sake of example
  const info = { accessToken, refreshToken };
  done(null, user, info);
}));

// Set route to start OAuth link, this is where you define scopes to request
app.get('/oauth2', passport.authenticate('oauth2', { scope: 'user_read', session: false }));

// Set route for OAuth redirect
app.get('/oauth2/callback', function (req, res, next) {

  passport.authenticate('oauth2',
    { session: false },
    // see comment at the bottom regarding this callback (3rd) parameter of passport.authenticate()
    function (err, user, info, status) {
      // user = user returned from 'verify' function
      // info = info returned from 'verify' function
      if (err) { return next(err) }
      res.redirect('/success');
    })(req, res, next);

});

// generate link to start oauth process
app.get('/', (_, res) => res.send('<html><a href="/oauth2">Login</a></html>'))

// redirected on oauth success
app.get('/success', (_, res) => res.send('success'))

app.listen(3000, function () {
  console.log('App listening on port 3000')
});

/**
 * On passport.authenticate() :
 *
 * An optional `callback` can be supplied to allow the application to override
 * the default manner in which authentication attempts are handled.  The
 * callback has the following signature, where `user` will be set to the
 * authenticated user on a successful authentication attempt, or `false`
 * otherwise.  An optional `info` argument will be passed, containing additional
 * details provided by the strategy's verify callback - this could be information about
 * a successful authentication or a challenge message for a failed authentication.
 * An optional `status` argument will be passed when authentication fails - this could
 * be a HTTP response code for a remote authentication failure or similar.
 *
 *     app.get('/protected', function(req, res, next) {
 *       passport.authenticate('local', function(err, user, info, status) {
 *         if (err) { return next(err) }
 *         if (!user) { return res.redirect('/signin') }
 *         res.redirect('/account');
 *       })(req, res, next);
 *     });
 *
 * Note that if a callback is supplied, it becomes the application's
 * responsibility to log-in the user, establish a session, and otherwise perform
 * the desired operations.
 *
 */

Depending on what you want to do with the OAuth token (login only or full app authentication with it) the solution may defer.

If you want to login only using OAuth: I would save the access token to the DB in the verify function (or last in the 3rd callback function (err, user, info, status)), generate a JWT for the user, set it as authorization header and redirect to /success. Then on /success get user from DB based on JWT content and retrieve it's OAuth token.

If you want a full app management, I guess saving it to the cookies from 3rd callback func can work. It will be saved on the client machine tho.

juanlet commented 3 years ago

Thanks, I will take a look at your solution. I could make it work by changing one line on the library and commenting the call to verify

              if (self._passReqToCallback) {
                var arity = self._verify.length;
                /* if (arity == 6) {
                  console.log("GOING 6")
                  self._verify(req, accessToken, refreshToken, params, profile, verified);
                } else { // arity == 5
                  console.log("GOING 5")
                  self._verify(req, accessToken, refreshToken, profile, verified);
                }  */
                options.res.redirect(`${options.successRedirectURL}?access_token=${accessToken}&refresh_token=${refreshToken}`);

and calling it with some extra options, res in this case in which instead calling the verify function I just go ahead and redirect the user

router.get('/', passport.authenticate('oauth2'));

router.get('/callback', (req, res,next) => { 
  passport.authenticate('oauth2', { failureRedirect: '/api/v1/auth/cognito/failure', successRedirectURL: '/api/v1/auth/cognito/success', req, res, next })(req,res,next);
 });

I will see how it goes from here and check your solution as well. Thanks again.

It would be nice if we had the option to disable the sessions though, since I had to bypass that verify call cause that option was not available like in the LocalStrategy in which you have the session: false option. Even if I got the token right, when that verify function was called I kept getting the TypeError: req.logIn is not a function error