Open juanlet opened 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.
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
Hey, I'm getting the access token correctly on the verification function
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 falseEven 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