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

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

Related

Google OAuth2 with Passport and Express

I am struggling with getting Google OAuth to work with my Express/React application whilst using Passport.js. I am using JWTs, not sessions.
In my React webapp client, I have a "login with Google" button that calls my backend API /auth/google/ with the following route setup in Express:
router.get('auth/google', passport.authenticate('google', {session: false, scope: ['email','profile']}) );
My Passport.js google strategy is:
const googleStrategy = new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/api/v1/auth/google/callback",
passReqToCallback : true
},
async (request, accessToken, refreshToken, profile, done) => {
try {
console.log('profile', profile);// ** CORRECT USER PRINTED **
let existingUser = await User.findOne({ 'google.id': profile.id });
// if user exists return the user
if (existingUser) {
console.log('Found existing user...');
return done(null, existingUser);
}
// if user does not exist create a new user
const newUser = new User({
method: 'google',
googleId: profile.id,
profileImage: profile.photos[0].value,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
shortName: profile.displayName,
});
await newUser.save();
return done(null, newUser);
} catch (error) {
return done(error, false)
}
}
);
My Google developer dashboard is setup to call the following URL in my Express API backend upon successful authentication: /auth/google/callback
My Express route for this is defined as: router.get('auth/google/callback', passport.authenticate('google', {session: false}), authController.googleAuthCallback);
My Express googleAuthCallback function is defined as:
exports.googleAuthCallback = async (req, res) => {
console.log(req.user) // ** WRONG USER PRINTED HERE ** different from above user printed in google strategy
}
The strange this is when I console.log the profile variable in my googleStrategy, I get the right user profile information for the account from Google. This means the authentication vis a vis Google is fine. However, this same account is NOT being provided to my /auth/google/callback endpoint in the req.user object at that location. It is an entirely different account (it is the first value from my database of Users, which is authenticated using local authentication).
How do I get the user object back to my Express callback endpoint that I supplied to Google in the developer console as the authorized redirect URI?
As a general question, what happens after the strategy calls return done(null, existingUser);? I have no callback in the /auth/google route after the passport.authenticate() middleware is called so what happens next?
I am using "passport-google-oauth20": "^2.0.0"
My let existingUser = await User.findOne({ 'google.id': profile.id });
line was incorrect and was essentially returning no user. Mongoose does not complain and hence the strategy was just returning the first user from my database rather than the authenticated google user.

Trusting Cognito accessToken in lambda (authorizer in use) - Authorization

Is it safe for my lambda function to trust the accessToken passed by the user and checked by the lambda authorizer to perform CRUD db operations?
For example:
const authToken = event.headers['Authorization'];
if (!authToken) throw new Error('No auth token found so no username');
var decodedToken = jwt_decode(authToken);
const userName = decodedToken.username; //---- BUT CAN WE TRUST THIS? ----
let params = {
TableName: "myTable",
IndexName: 'userName-gsi',
KeyConditionExpression: 'userName = :userName',
ExpressionAttributeValues: {
':userName': userName,
},
Limit: 1,
};
let data = await dynamodb.query(params).promise();
return {
statusCode: 200,
headers: utils.getResponseHeaderApplicantifyCors(),
body: JSON.stringify(data.Items[0]),
};
In your example you're only decoding the JWT, so the only verification made is that the token is in JWT form. That is not enough to guarantee, that the JWT is originating from trusted party.
The minimum you should do, is to also verify the contents of the JWT token. Steps for that are listed here: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html

checkSession not returning profile, email or email_verified

In my application using auth0 I need to know if the email is verified. I have followed the documentation when using checkSession but it is still not returning the data I am querying for.
Here I am setting up the connection to my auth0 app
const webAuth = auth0 = new auth0.WebAuth({
domain: 'my_domain',
clientID: 'my_client_id',
responseType: 'token', // code or token,
redirectUri: 'my_redirect_uri',
})
Here I am checking the session
webAuth.checkSession({
scope: 'openid profile email',}, function (err, authResult) {
if(err) {
console.log('err', err)
}
if(typeof authResult !== 'undefined') {
console.log(authResult, 'authResult')
but it does not return a profile, email or email verified as it should according to the documentation

Is there a Passport Strategy to handle an offsite API which isn't OAuth?

I've got a relatively unique use-case here and I can't find the perfect solution.
I have a website that has user log-in via facebook and linkedin, which works flawlessly via passportjs.
However I also need to be able to login via an offsite API which is not OAuth but rather offers a standard JSON response.
I currently use angular to hit the API and return the user object but then I have to store that in sessionStorage to persist the log in. This isn't ideal as it means there is a server session and a local session. To log out I have to manually clear the sessionStorage and this hit a log out path for the server.
What I want to do is create a local log in path on the ExpressJS website and the route then hits the API to log in and then the user is stored in the server session.
In the end I hacked the local strategy.
The key was adding passReqToCallback so I could manipulate the request.
This adds the response to req.user and starts a session.
Strategy:
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
request = require('request')
module.exports = function() {
passport.use(new LocalStrategy({
passReqToCallback: true
},
function(req, username, password, done) {
request.post({
url: 'http://api/',
form: {
username: username,
password: password
},
json: true
}, function(err, httpResponse, body) {
if (err || !body.success) done();
else done(null, body);
});
}
));
};
Route:
app.post('/auth/local',
passport.authenticate('local', {
failureRedirect: '/fail'
}),
function(req, res) {
res.redirect('/success');
});

PassportJS Profile Undefined Name

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', ...]
}