Open codeeverystory opened 7 years ago
It looks like there's no code for getting the profile at all, except we're free to override a method that's documented in the code but not in the README.
Really, OAuth2 doesn't seem to standardize how to get a profile, so it's not quite a fit for passport. The workaround I use is paraphrased below, but I have no idea if that's the right url for getting a profile.
let client = new OutlookStrategy({
authorizationURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
clientID: configAuth.outlookAuth.clientID,
clientSecret: configAuth.outlookAuth.clientSecret,
callbackURL: configAuth.outlookAuth.redirectURL
},
function(accessToken, refreshToken, profile, cb) {
// do stuff
});
client.userProfile = function (accesstoken, done) {
// choose your own adventure, or use the Strategy's oauth client
this._oauth2._request("GET", "https://login.microsoftonline.com/common/oauth2/v2.0/me/", null, null, accesstoken, (err, data) => {
if (err) { return done(err); }
try {
data = JSON.parse( data );
}
catch(e) {
return done(e);
}
done(null, data);
});
};
passport.use(client);
@knightcode your bit of code is a miracle worker I wish I had come across it much earlier.
Found another workaround by passing an extra argument to the verify callback. There is some function arity check inside the library to pass extra params to the verification callback
function(accessToken, refreshToken, params, profile, cb) {
console.log(params); // --> params contains all the data received during the accessTokenRequest
}
@knightcode I followed your suggestions, but I got:
TypeError: Cannot read property 'message' of undefined
at app.use (/app/server/src/main/setErrorHandler.ts:13:37)
at Layer.handle_error (/app/node_modules/express/lib/router/layer.js:71:5)
at trim_prefix (/app/node_modules/express/lib/router/index.js:315:13)
at /app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
at next (/app/node_modules/express/lib/router/index.js:275:10)
at next (/app/node_modules/express/lib/router/route.js:127:14)
at Layer.handle_error (/app/node_modules/express/lib/router/layer.js:67:12)
at next (/app/node_modules/express/lib/router/route.js:135:13)
at OAuth2Strategy.strategy.error (/app/node_modules/passport/lib/middleware/authenticate.js:356:9)
at /app/node_modules/passport-oauth2/lib/strategy.js:169:36
at _oauth2._request (/app/server/src/security/passport/passport.ts:102:20)
at passBackControl (/app/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:132:9)
at IncomingMessage.<anonymous> (/app/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:157:7)
at IncomingMessage.emit (events.js:165:20)
at endReadableNT (_stream_readable.js:1101:12)
at process._tickCallback (internal/process/next_tick.js:152:19)
Have you seen this error before?
In this function, the access token is empty.
// https://github.com/jaredhanson/passport-oauth2/issues/73
oAuth2Strategy.userProfile = function (accesstoken, done) {
console.log(accesstoken);
// choose your own adventure, or use the Strategy's oauth client
this._oauth2._request("GET", "https://login.microsoftonline.com/common/oauth2/v2.0/me/", null, null, accesstoken, (err, data) => {
console.log(err);
console.log(data);
if (err) { return done(err); }
try {
data = JSON.parse( data );
}
catch(e) {
return done(e);
}
done(null, data);
});
};
Thanks!
@muralikg I still get an empty object, would you mind to share your code?
if(process.env.OAUTH2_CLIENT_ID){
passport.use(new OAuth2Strategy({
authorizationURL: process.env.OAUTH2_AUTHORIZE_URL,
tokenURL: process.env.OAUTH2_TOKEN_URL,
clientID: process.env.OAUTH2_CLIENT_ID,
clientSecret: process.env.OAUTH2_CLIENT_SECRET,
callbackURL: process.env.SITE_URL+"/auth/oauth2/callback",
},
function(accessToken, refreshToken, params, profile, cb) {
User.findOrCreateOauth(params.info.email).then(function (user) {
return cb(null, user);
}).catch(function (reason) {
return cb(reason, null);
});
}));
}
@JunyuanZheng , I have tried this with digital ocean oauth2 and works fine
@JunyuanZheng I got that very same error when mongoDB was not connecting correctly. I was at work and they had it blocked on the firewall which stumped me for a bit because I hadn't changed any code. You might double check that whatever DB you are using is not halting the oauth process
const passport = require('passport')
// const { Strategy: GoogleStrategy } = require('passport-google-oauth20')
const { Strategy: GithubStrategy } = require('passport-github')
const { Strategy: OAuth2Strategy } = require('passport-oauth2')
const { GITHUB_CONFIG, OAUTH2_CONFIG} = require('../config')
const Profile = require('./profile')
module.exports = () => {
// Allow passport to serialize and deserialize users into sessions
passport.serializeUser((user, cb) => cb(null, user))
passport.deserializeUser((obj, cb) => cb(null, obj))
// The callback that is invoked when an OAuth provider sends back user
// information. Normally, you would save the user to the database
// in this callback and it would be customized for each provider
const callback = (accessToken, refreshToken, params, profile, cb) => {
console.log('access-token',accessToken)
console.log('refresh-token',refreshToken)
console.log('profile',profile)
console.log('params',params)
return cb(null, profile)
}
// Adding each OAuth provider's startegy to passport
// passport.use(new GoogleStrategy(GOOGLE_CONFIG, callback))
passport.use(new GithubStrategy(GITHUB_CONFIG, callback))
const DjangoStrategy = new OAuth2Strategy(OAUTH2_CONFIG, callback)
DjangoStrategy.userProfile = function(accessToken, done) {
var self = this;
this._userProfileURL = 'http://localhost:8001/accounts/profile/';
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
var json;
if (err) {
if (err.data) {
try {
json = JSON.parse(err.data);
} catch (_) {}
}
if (json && json.message) {
return done(new APIError(json.message));
}
return done(new InternalOAuthError('Failed to fetch user profile', err));
}
try {
json = JSON.parse(body);
} catch (ex) {
return done(new Error('Failed to parse user profile'));
}
console.log('json', json)
var profile = Profile.parse(json);
profile.provider = 'oauth2';
profile._raw = body;
profile._json = json;
done(null, profile);
});
}
passport.use(DjangoStrategy)
}
exports.parse = function(json) {
if ('string' == typeof json) {
json = JSON.parse(json);
}
var profile = {};
profile.id = String(json.id);
profile.displayName = json.name;
profile.username = json.username;
profile.email = json.email;
return profile;
};
or visit this link https://stackoverflow.com/a/58170646/8770790
I was able to make it work by adding the scope array with other options.
authorizationURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
clientID: configAuth.outlookAuth.clientID,
clientSecret: configAuth.outlookAuth.clientSecret,
callbackURL: configAuth.outlookAuth.redirectURL,
scope: ['openid', 'profile', 'https://outlook.office.com/user.read']
},
function(accessToken, refreshToken, profile, cb) {
// do stuff
});```
So whether I write this with _oauth.get
or _oauth._request
it does not set the authorization header (or at least that's the error Aws Cognito returns to me:
client.userProfile = function (accessToken, done) {
// axios worked
// return axios
// .get("https://yttask.auth.us-east-1.amazoncognito.com/oauth2/userInfo", {
// headers: { Authorization: `Bearer ${accessToken}` }
// })
// .then((resp) => resp.data)
// .then((data) => done(null, data))
// .catch((err) => done(err));
this._oauth2._request(
"GET",
"https://yttask.auth.us-east-1.amazoncognito.com/oauth2/userInfo",
null,
null,
accessToken,
(err, data) => {
console.log(err);
console.log(data);
if (err) {
return done(err);
}
try {
data = JSON.parse(data);
} catch (e) {
return done(e);
}
done(null, data);
}
);
};
Am I doing something wrong? Using v1.6.1.
If your params returns an id_token, you could just use jwt to decode it. Try this:
const oktaStrategy = new Strategy({
authorizationURL: '{url}/oauth2/default/v1/authorize',
tokenURL: '{url}/oauth2/default/v1/token',
clientID: "xxxxxxxxxxxxx",
clientSecret: "XXXXXXX",
callbackURL: '{myurl}/callback',
}, (accessToken, refershToken, params, profile, done) => {
const decoded = jwtDecode(params['id_token']);
done(null, decoded);
});
I'll make a pull request to add params
to the README. Wasted so much time trying to figure out where the user's email was
I am connecting my node app to Microsoft Outlook using passport-oauth2 where I am experiencing some major error where it asks for permissions from the user and take me to redirectURL but it is not able to get me the profile info of user and return an empty object of profile.Below is some of my code i used:
passport.js
const OutlookStrategy=require('passport-oauth2').Strategy; passport.use(new OutlookStrategy({
authorizationURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', tokenURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', clientID: configAuth.outlookAuth.clientID, clientSecret: configAuth.outlookAuth.clientSecret, callbackURL: configAuth.outlookAuth.redirectURL }, function(accessToken, refreshToken, profile, cb) { console.log(accessToken); console.log(profile); <-------------- This is empty object-------------------------| console.log(refreshToken); console.log(cb);
} ));
routes.js
router.get('/auth/outlook', passport.authenticate('oauth2',{ scope: outlookScope }) );
router.get('/auth/outlook/callback', passport.authenticate('oauth2',{ failureRedirect: '/' }), function(req, res) { // Successful authentication, redirect home. // var authCode = req.code; res.redirect('/account'); });