PassportJS Profile Undefined Name - express

I'm using PassportJS and passport-google-oauth in an ExpressJS web app.
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: CALLBACK
},
function(accessToken, refreshToken, profile, done) {
console.log(profile.displayName);
console.log(profile.name.familyName);
console.log(profile.name.givenName);
...
}));
The problem is that profile.displayName, profile.name.familyName and profile.name.givenName are undefined. When I use the callback with Passport Facebook, no problem at all.
How to get the name of the user when using a Google account to login?

When I checked it seems it has more parameters than what is in the official sample leading people to confusion including me..
rather than
function(accessToken, refreshToken, profile, done)
use
function(req, accessToken, refreshToken, profile, done)

you need to request for it, include 'https://www.googleapis.com/auth/userinfo.profile' in your scope.
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: CALLBACK,
scope: ['https://www.googleapis.com/auth/userinfo.profile','email', ...]
}

Related

Refresh token with expo auth sessions (Google)

I use for my Android app expo-auth-sessions to authorize the user via Google:
const [request, response, promptAssync] = Google.useAuthRequest({
androidClientId: ANDROID_CLIENT_ID,
iosClientId: IOS_CLIENT_ID,
expoClientId: EXPO_CLIENT_ID,
scopes: [
"https://www.googleapis.com/auth/drive",
],
})
The request works just fine and it returns an access token, but it does not contain a refresh token.
Is there any possibility to configure the request that it also returns a refresh token or something similar to not force the user to sign in again after the access token expires?
Current response:
{
authentication: {
accessToken: TOKEN,
expiresIn: "3599",
idToken: undefined,
issuedAt: 1644693943,
refreshToken: undefined,
scope:
"https://www.googleapis.com/auth/drive",
tokenType: "Bearer",
},
params: {
access_token: TOKEN,
expires_in: "3599",
scope:
"https://www.googleapis.com/auth/drive",
token_type: "Bearer",
},
type: "success"
}
Thanks in advance!
You'll have to add access_type: "offline" (Google APIs auth) in your auth URL because expo doesn't do that by default. For this, you can make use of the extraParams attribute in useAuthSession. However, access_type: "offline" is not supported for responseType: "token" (the default value) since the concept of refresh_token doesn't exist for implicit_grant_flow. You'll have to change that to responseType: "code" and later exchange the obtained code for access and refresh token on the server side. Overall you'll have to make the following changes -
const [request, response, promptAssync] = Google.useAuthRequest({
androidClientId: ANDROID_CLIENT_ID,
iosClientId: IOS_CLIENT_ID,
expoClientId: EXPO_CLIENT_ID,
scopes: [
"https://www.googleapis.com/auth/drive",
],
responseType: "code",
shouldAutoExchangeCode: false,
extraParams: {
access_type: "offline"
},
})
However, if you've already authorized the app once before, you might have to add prompt: 'consent' too. You can take a look at this answer for more clarity - https://github.com/googleapis/google-api-python-client/issues/213#issuecomment-205886341

Passport Slack OAuth Scope Throwing Error with Identity

I'm trying to request a slack oauth token using the passport-slack module with the following code.
passport.use(new SlackStrategy({
clientID: xxxxxxx,
clientSecret: xxxxxxx,
scope: [
'chat:write:user',
'channels:read'
],
callbackURL: xxxxxxx+"/slack/callback"
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
}
));
router.use(passport.initialize());
router.use(require('body-parser').urlencoded({ extended: true }));
router.get('/authorize', function(req, res, next) {
passport.authenticate('Slack', function(err, user, info) {
next();
})(req, res, next);
});
/* OAuth Callback flow */
router.get('/callback', passport.authorize('Slack', { failureRedirect: '/authorize' }), function(req, res) {
});
However, I am getting the following response "missing_scope","needed":"identity.basic","provided":"identify,channels:read,chat:write:user"}.
But when I add identify.basic as a scope it errors saying I can't request both the identity and other scopes at the same time. Anyone know how to resolve this? I just want to generate an oauth token so my api can post to slack as a user.

Is there a way to pass the redirect_uri through the state parameter and access the said parameter in the callback endpoint?

I've been trying to implement a redirect after the user is successfully authenticated to the service that utilized our authentication service.
To do so, I've tried passing the redirect_uri through the state parameter shown below:
let state1 = {
clientId: clientId,
clientSecret: clientSecret,
redirectUri: redirectUri
}
passport.authenticate(
'auth0',
{
responseType: 'code',
scope: 'openid profile email',
state: JSON.stringify(state1)
},
function(err, user, info) {
res.send(user)
})(req, res, next)
I've expected the state containing the redirect_uri, client_id and client_secret to be accessible in the callback endpoint, but it's just the successful authentication code that is in the URL of the callback.
Yes there is a way. It uses a nonce to secure the info against CSRF attacks. Please take a look at this doc:
https://auth0.com/docs/protocols/oauth2/redirect-users

Passport-auth0 access accessToken

Since sometime I am using auth0 with express. But now I have one question.
This is how my code looks like:
var passport = require('passport');
var Auth0Strategy = require('passport-auth0');
var strategy = new Auth0Strategy({
domain: '',
clientID: '',
clientSecret: '',
callbackURL: '/loginapi/callback'
}, function (accessToken, refreshToken, extraParams, profile, done) {
// accessToken is the token to call Auth0 API (not needed in the most cases)
// extraParams.id_token has the JSON Web Token
// profile has all the information from the user
return done(null, profile);
});
passport.use(strategy);
// This is not a best practice, but we want to keep things simple for now
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
module.exports = strategy;
But how can I access the accessToken in a express request like the user element. I really don't know how, but I already tried some things.
Nils
I got it guys!
var strategy = new Auth0Strategy({
domain: '',
clientID: '',
clientSecret: '',
callbackURL: '/loginapi/callback'
}, function (accessToken, refreshToken, extraParams, profile, done) {
// accessToken is the token to call Auth0 API (not needed in the most cases)
// extraParams.id_token has the JSON Web Token
// profile has all the information from the user
var info = {
"profile": profile,
"accessToken": accessToken,
"refreshToken": refreshToken,
"extraParams": extraParams
};
return done(null, info);
});
Now I can simply access the accessToken with the req.user object.

Passport js authentification without sessions

I'am a beginner in expressjs and passportjs.
I played with authentication via google using passport with GoogleStrategy. Using the code below i have req.user = { id: '123456' } in /users/hello route handler, but i want to get some like this without session support to send it as the answer to authenticated client. In other words i want to send some token to client if authentication is successful without cookie session start. I can't find the way how to forward user object to target route handler when i turn off sessions.
passport.use(new GoogleStrategy({
returnURL: 'http://localhost/auth/google/return',
realm: 'http://localhost/'
},
function(identifier, profile, done) {
done(null, {id: '123456'});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
done(null, {id: id});
});
app.use(session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
app.get('/auth/google', passport.authenticate('google');
app.get('/auth/google/return',
passport.authenticate('google', {
successRedirect: '/users/hello',
failureRedirect: '/users/goodbye'
}));
To turn off sessions try changing this:
app.get('/auth/google/return',
passport.authenticate('google', {
successRedirect: '/users/hello',
failureRedirect: '/users/goodbye'
}));
to:
app.get('/auth/google/return',
passport.authenticate('google', {
session:false
}));