No authorization token was found - Express-JWT and Auth0 - node.js

I'm working on integrating Auth0 into a MERN Stack app. The flow should look like this:
User clicks the login button which triggers Auth0Lock.show()
User fills in their credentials and clicks the submit button
The callback URL of the API is hit which logs the user in and redirects them back to the front-end app
(everything looks like it's working fine up to this point)
The front-end requests user information from the API
The front-end receives the information and redirects
This seems to be a fairly standard authentication flow. The problem is that when the front-end asks the back-end for user information, there's an error:
UnauthorizedError: No authorization token was found
My setup looks essentially like this:
// client-side config
const lock = new Auth0Lock(clientID, domain, {
auth: {
responseType: 'token',
audience: 'https://${domain}/userinfo',
redirectUrl: API_URL + '/api/users/callback',
params: {
scope: 'openid profile email' // no change
}
}
})
// server.js
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// [DB setup]
var sessConfig = {
secret: "[random string]",
cookie: {
sameSite: false
},
resave: false,
saveUninitialized: true
};
if(app.get('env') === 'production') sessConfig.cookie.secure = true;
app.use(session(sessConfig));
const {domain, clientID, clientSecret, callbackURL} = require('./config/auth0');
const passportStrategy = new Auth0Strategy(
{domain, clientID, clientSecret, callbackURL},
(accessToken, refreshToken, extraParams, profile, done) => done(null, profile)
)
passport.use(passportStrategy);
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
app.use(passport.initialize());
app.use(passport.session());
// [routing]
// routes/users.js
router.get('/callback', (req, res, next) => {
passport.authenticate('auth0', (err, user, info) => {
if(err) return next(err);
if(!user) return next(info);
req.logIn(user, err => {
if(err) return next(err);
const returnTo = req.session.returnTo;
delete req.session.returnTo;
res.redirect(returnTo || clientRootURL + '/callback');
})
})(req, res, next);
})
router.get(
'/current',
require('cors')(),
authenticate,
(req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
email: req.user.email
});
}
);
// authenticate.js
module.exports = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${domain}/.well-known/jwks.json`
}),
audience: clientID,
issuer: `https://${domain}/`,
algorithms: ['RS256']
});
The vast majority of comes straight out of the Auth0 documentation.
I'm trying to get the user info from the /users/current endpoint after logging in and it says it can't find authorization. Does anyone have any idea how to get this to work?

You should be calling the /userinfo endpoint to get the user profile, or getting the info from the id_token. Take a look at this doc
https://auth0.com/docs/api/authentication#get-user-info

Every authenticated frontend call should contain:
headers: {
Authorization: `Bearer ${token}`,
},
where token should be:
const token = await getAccessTokenSilently();
getAccessTokenSilently is a public function of auth0 lib.
See: getAccessTokenSilently doc

Related

Heroku app login only works on dev PC and not on any other device (Node.js, Passport.js)

I have recently deployed an app on Heroku. It works fine in the dev environment and it works fine when I access it through my PC. I cannot log into the app on any device that is not my development PC. I can post to MongoDB Atlas on any device no problem, so the database looks like it is connected and able to receive information. I looked at another answer for a similar problem from a different user and the reply was
"After looking at your repo, I would double check your database and API calls. In your client, you are fetching data from a localhost URL. If you are running React client side, the API call will try to fetch information locally on your device instead of from the Heroku server."
I don't know what the fix would be if there is something wrong with my calls. The MongoDB Atlas Database is recording the registration information no problem.
this is the github link:
https://github.com/chrishjung/Armada-Development-master
this is it on Heroku:
https://armada-phr.herokuapp.com/
UPDATE:
So somehow for a short period of time, I was able to register users and login after I commented out "secure: true", but now it does not work again.
snippets from app
// Init Session
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: true,
saveUninitialized: true,
proxy: true, // add this line
cookie: {
//secure: true, It worked for a short time when I commented this out
httpOnly: true,
},
store: new MongoStore({ mongooseConnection: mongoose.connection }),
})
);
// For Passport JS Authentication
app.use(passport.initialize());
app.use(passport.session());
require("./utils/passport.auth");
app.use((req, res, next) => {
res.locals.user = req.user;
next();
});
//passport.auth:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user.model');
passport.use(
new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
async (email, password, done) => {
try {
const user = await User.findOne({ email });
// Username/email does NOT exist
if (!user) {
return done(null, false, {
message: 'Username/email not registered',
});
}
// Email exist and now we need to verify the password
const isMatch = await user.isValidPassword(password);
return isMatch
? done(null, user)
: done(null, false, { message: 'Incorrect password' });
} catch (error) {
done(error);
}
}
)
);
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
//auth.route.js
router.get(
"/login",
ensureLoggedOut({ redirectTo: "/" }),
async (req, res, next) => {
try {
return res.render("login");
} catch (e) {
console.log("e", e);
}
}
);
router.post(
"/login",
passport.authenticate("local", {
failureRedirect: "/auth/login",
failureFlash: true,
}),
async (req, res) => {
try {
res.redirect(`/`);
} catch (e) {
console.log("e", e);
}
}
);```
After successfully deploying your app to heroku you get a url link to your app ..
You find this url in your app settings in heroku.
After finding it , Copy that url and use it by replacing the "http://localhost:××××" by the new url

Authenticating with Passport.js ActiveDirectory doesn't maintain session

I'm having an issue with Passport.js using the ActiveDirectory strategy where I'm successfully authenticating but fail to maintain a session on subsequent requests. After authentication, any call to req.isAuthenticated() returns false. Additionally, no cookie is being set on the client's browser. I've spent the day pouring over similar posts but so far none of the suggested solutions have worked.
Some observations to note:
-Authentication is always successful and returns the req.user to client.
-The console.log in serializeUser() always fires and correctly logs the user record.
-The console.log in deserializeUser() never fires, I'm not sure when this function is supposed to be called but it doesn't appear to be?
-After logging in, each time I make a call to my /test endpoint the non-authenticated condition fires.
// Passport Requires
const passport = require('passport');
const session = require('express-session');
var ActiveDirectoryStrategy = require('passport-activedirectory');
// Setup
app.use(session({
secret: 'secret',
resave: false,
saveUninitialized: false,
cookie: { secure: false, maxAge: 600000 }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log('userStrategy -- serialized:', user)
done(null, user);
});
passport.deserializeUser(function(user, done) {
console.log('userStrategy -- deserializeUser', user)
done(null, user);
});
passport.use(new ActiveDirectoryStrategy({
integrated: false,
passReqToCallback: true,
ldap: {
url: "url",
baseDN: "baseDN",
username: `username`,
password: `password`
}
}, function (req, profile, ad, done) {
ad.isUserMemberOf(profile._json.dn, 'Access Group', function (err, isMember) {
console.log('isMember:', isMember)
if (err) {
return done(err)
} else {
return done(null, profile)
}
})
}))
// Login Route
app.post('/login',
passport.authenticate('ActiveDirectory', { failWithError: true }),
function (req, res) {
console.log('Authenticated');
return res.status(200).send(req.user);
}, function (err) {
console.log('Not Authenticated');
return res.sendStatus(401).send(err);
}
)
// Test endpoint to check whether user is authenticated
app.get('/test', function(req, res) {
if (req.isAuthenticated()) {
res.send('Youre authenticated!')
} else {
res.send('Youre not authenticated!')
}
})
Appreciate any thoughts on the problem -- thanks!
I've figured out the issue which has nothing to do with Passport or server setup.
In my client-side API call to login I've been using fetch() but I did not have credentials: 'include' option on the initial POST to login. Once I added this option, a cookie was sent back to the client browser and future API calls to isAuthenticated() returned true.
Make sure to have credentials: 'include' on ALL of your API calls, including the initial POST to login.

Angular request to google oAuth fails due to CORS policy

I've set up an angular/nodejs(express) application with google authentication but whenever I request to google oauth through angular app it throws a cors error but if I request directly from browser it works as it should.
Backend is running on port 3000 and angular is running on 4200.
I'm using cors package to allow all cors requests in server.js:
app.use(passport.initialize());
// Passport Config
require('./config/passport')(passport);
// Allowing all cors requests
app.use(cors());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/users', usersRouter);
app.use('/auth', authRouter);
Passport config:
passport.use(
new GoogleStrategy(
keys,
(token, refreshToken, profile, done) => {
process.nextTick(() => {
User.findOne({ email: email })
.then(user => {
if (user) {
return done(null, user);
}
})
.catch(err => console.log(err));
});
}
)
);
This is the google authentication route:
router.get('/google', passport.authenticate('google', {
scope: ['profile', 'email'],
session: false
}),
(req, res) => {
// Create JWT payload
const payload = {
id: req.user.id,
email: req.user.email
};
jwt.sign(payload, secret, expires, (err, token) => {
res.json(token);
});
}
);
And here is angular request:
googleAuth() {
return this.http.get<any>('http://localhost:3000/auth/google').pipe(
map(user => {
if (user) {
localStorage.setItem(
'currentUser',
JSON.stringify(user)
);
}
return user;
})
);
}
Error in chrome console:
Failed to load "https://accounts.google.com/o/oauth2/v2/auth?response_type=code...": No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access
I've also authorized JavaScript origins in google developers console:
origins
I'm pretty new to angular and nodejs and I have read all the similar questions but couldn't find a workaround to this problem.
Try out this method of directly calling the URL: ( Pardon if I missed something syntactically, but basically this is the idea )
googleAuth(){
window.open('/google',"mywindow","location=1,status=1,scrollbars=1, width=800,height=800");
let listener = window.addEventListener('message', (message) => {
//message will contain google user and details
});
}
and in server you have passport strategy set I assume, Now
router.get('/google', passport.authenticate('google', {
scope: ['profile', 'email'],
session: false } ))
router.get('/google/callback',
passport.authenticate('google', { failureRedirect: '/auth/fail' }),
function(req, res) {
var responseHTML = '<html><head><title>Main</title></head><body></body><script>res = %value%; window.opener.postMessage(res, "*");window.close();</script></html>'
responseHTML = responseHTML.replace('%value%', JSON.stringify({
user: req.user
}));
res.status(200).send(responseHTML);
});

Passport/ExpressJS - How to Properly Maintain Session State with Cookies?

I am new to using Passport authentication with express between my
server and client (on two different ports), and am having great
trouble understanding both the concept of maintaining session state
with Passport and also trying to retrieve req.session.passport.user
in order to determine if a request is sent from a user that has
successfully logged on.
Checking Persistent Session State with Passport
In my code, I have a route to my profile page that uses my own
authenticated function. My login route uses
passport.authenticate('local'), however. Should I be applying this
to ALL subsequent routes (like for my profile), or is my separate
authentication expected?
req.session.passport undefined
For some reason, the cookie that is sent with the /profile GET request is missing the passport object after a successful login (by
passport.use('local-login')) It looks like the following if I
console.log(req.session) in authenticated. Why is this?
Session {
cookie:
{ path: '/',
_expires: 2017-07-11T01:38:30.087Z,
originalMaxAge: 14400000,
httpOnly: true,
secure: false } }
Server Code
// server.js
let app = express();
app.use(cors());
import passportConfig from '../config/passport';
passportConfig(passport); //apply passport configuration
var jsonParser = bodyParser.json();
app.use(jsonParser);
app.use(cookieParser('mySecret'));
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
cookie: {secure: false, maxAge: 4*60*60*1000 }
}));
app.use(passport.initialize());
app.use(passport.session());
function authenticated (req, res, next) {
// req.session.passport is undefined
if (req.session.passport.user) {
next();
}
else {
res.redirect('/signup');
}
}
app.get('/profile', authenticated, (req, res) => {
// server code handling route
}
app.post('/login', passport.authenticate('local-login', {
session: true
}), (req, res) => {
res.json({
email: req.user.local.email
});
});
// passport.js
import User from '../models/User';
export default function passportConfig(passport) {
passport.serializeUser((user, done) => {
console.log('serializing user');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
console.log('deserializing user');
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
}, (req, email, password, done) => {
// if verification is successful
var newUser = new User();
// set credentials of newUser...
return done(null, newUser);
}));
}
Client Code
// profile.js (client action code with react-redux)
export function profile(terms, location) {
return (dispatch) => {
return fetch('profile',
{credentials: 'include'})
.then(response => response.json())
.then(json => { dispatch(profileSuccess(json)); })
.catch(error => dispatch(profileError(error)));
}
}
I am aware there are actually quite a lot of articles/posts available
of similar issues, but applying many of the different solutions have
all failed for me. Any help to my problem specifically is greatly
appreciated.

Saving multiple passportjs providers into same user document in mongodb

I'm having a problem I'm not able to resolve. I'm developing an app with nodejs, using mongodb, expressjs and passportjs as my authentication middleware.
I currently have 3 strategies: facebook, twitter and instagram. What I want to achieve is that when a user login for the first time, if the user is logged with one strategy and logs in with another one save the profiles into the same mongodb user document.
This is my auth/index.js:
require('./local/passport').setup(User, config);
require('./facebook/passport').setup(User, config);
require('./twitter/passport').setup(User, config);
require('./instagram/passport').setup(User, config);
var router = express.Router();
router.use('/local', require('./local'));
router.use('/facebook', require('./facebook'));
router.use('/twitter', require('./twitter'));
router.use('/instagram', require('./instagram'));
And this is, for example, my auth/twitter/index.js
var router = express.Router();
router
.get('/', passport.authenticate('twitter', {
failureRedirect: '/',
session: false
}))
.get('/callback', passport.authenticate('twitter', {
failureRedirect: '/',
session: false
}), auth.setTokenCookie);
module.exports = router;
But how could I pass for example a mongodb _id to this auth/twitter/passport.js in order to pass it to the mongoose query and update an user? Something like making a POST to auth/twitter and accessing to req.user._id ? I can't figure out how to do it.
exports.setup = function (User, config) {
var passport = require('passport');
var TwitterStrategy = require('passport-twitter').Strategy;
var TwitterApi = require('twitter');
passport.use(new TwitterStrategy({
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
callbackURL: config.twitter.callbackURL
},
function(token, tokenSecret, profile, done) {
User.findOne({
'twitter.id_str': profile.id
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
user = new User({
role: 'user',
[...]
Thank you very much.
EDIT:
This is how I set my cookie:
function setTokenCookie(req, res) {
if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'});
var token = signToken(req.user._id, req.user.role);
res.cookie('token', JSON.stringify(token));
res.redirect('/');
}
and the signToken function:
function signToken(id) {
return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*24*30 });
}
Why req.user and req.session are always empty in my Strategy?
EDIT2:
I think I could use the auth.isAuthenticated() function to attach user to the request before invoking the Strategy. What I have done is this:
router
.get('/', auth.isAuthenticated(), passport.authenticate('twitter', auth.isAuthenticated, {
failureRedirect: '/',
session: false
}))
.get('/callback', auth.isAuthenticated(), passport.authenticate('twitter', {
failureRedirect: '/',
session: false
}), auth.setTokenCookie);
But now I'm having this problem:
UnauthorizedError: No Authorization header was found
My request to auth/twitter comes from a $window.location. It seems that this does not attach the user object to the request, because when I make a GET or POST using isAuthenticated() the user object is passed correctly. This is my isAuthenticated() function:
function isAuthenticated() {
return compose()
// Validate jwt
.use(function(req, res, next) {
// allow access_token to be passed through query parameter as well
if(req.query && req.query.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.query.access_token;
}
validateJwt(req, res, next);
})
// Attach user to request
.use(function(req, res, next) {
User.findById(req.user._id, function (err, user) {
if (err) return next(err);
if (!user) return res.send(401);
req.user = user;
next();
});
});
}
FYI I just dealed with this decoding the JWT token in my Strategies. I don't know if this is a good practice but the problem was I was not having my user attached to the request if the request was made with a $window.location.href
So in my strategies I read the cookie and decode it on the fly for searching a user in database.
Thank you.
You can set passReqToCallback to true when you define your passport strategy. It will make the current request, thus the current logged in user available to your callback function.
exports.setup = function (User, config) {
var passport = require('passport');
var TwitterStrategy = require('passport-twitter').Strategy;
var TwitterApi = require('twitter');
passport.use(new TwitterStrategy({
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
callbackURL: config.twitter.callbackURL,
passReqToCallback: true
},
function(req, token, tokenSecret, profile, done) {
User.findOne({
'twitter.id_str': profile.id
}, function(err, user) {
if (err) return done(err);
if (!user) {
if (req.user) {
[...]

Resources