Passport js authentification without sessions - express

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
}));

Related

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.

passport.js return error/failureFlasg message to view

I just started with express and passport.js and am having a difficulty with showing error messages during authentication with passport
When user successfully authenticate I am able to redirect the user and all is well, however, if the same email is authenticated with a different strategy I would like to show a message saying that the given email is already associated with a different strategy.
Here's my auth-routes.js
router.get(
'/google',
passport.authenticate('google', {scope: ['email', 'profile']})
);
router.get('/google/redirect', passport.authenticate('google', { successRedirect: '/protected/'}));
Here's my authController.js
passport.use(
new GoogleStrategy(
{
callbackURL: cbUrl,
clientID: process.env.GOOGLE_KEY,
clientSecret: process.env.GOOGLE_SECRET
},
(accessToken, refreshToken, profile, done) => verify(profile, done, 'Google')
)
);
function verify(profile, done, provider) {
User.findOne({
email: profile.email || profile.emails[0].value
}).then((currentUser) => {
if (currentUser) {
if (currentUser.provider !== provider)
return done(null, false, {message: `Login with ${provider} failed, this account is already associated with ${currentUser.provider}` });
else
return done(null, currentUser);
} else {
new User({
name: profile.displayName,
email: profile.email || profile.emails[0].value,
userId: profile.id,
provider: provider
}).save().then((newUser) => {
return done(null, newUser);
});
}
return done('Something went wrong!');
});
}
to sum it up, if I registered an account with email jhon.doe#gmail.com using Google oauth and later try to sign in or sign up with jhon.doe#gmail.com but this time using Facebook oauth, I would like to return a message and display that message on my login page.
So, after some reading I found my solution, in case anyone else also have the same issue, here's the solution.
Install these dependencies
npm install express-session express-flash
Where you initialized the passport
app.use(flash());
app.use(
session({
secret: process.env.SESSION_KEY,
resave: false,
saveUninitialized: false
})
);
app.use(passport.initialize());
app.use(passport.session());
On my login.ejs
<% if (messages.error) { %>
<%= messages.error %>
<% } %>
My auth-routes.js
router.get(
'/facebook/redirect',
passport.authenticate('facebook', {
successRedirect: '/protected/',
failureRedirect: '/auth/login' ,
failureFlash: true
})
);
And finally, my authController.js
done(null, false, { message: `Login with ${provider} failed, this email is already associated with ${currentUser.provider}` });

req.isAuthenticated() is always false

My authentication function using passportjs will always return false even though the user exists already and it will always redirect to the login page and this is overwriting all my authentication routes, so when I log in with a valid user credential or create a new user, the default behavior is to redirect to the 'secret page' but that is only redirecting to the login page every time.
I don't know what I am doing wrong here guys, I need ur help, please...
I have seen other related questions, but most of the threads aren't really answering the questions, or the answers that looks like a solution are not working even though I applied it, as they should I am still confused about what to do to make this work.
I have written a simple app to authenticate user login signup and logout using routes and passportjs.
My last piece of code is setup to only allow user access to the contents of the main site which is called a secret template in this case only if the user is a valid user (that is they are logged in or have successfully signed up).
The function I have created to do that looks like this:
// Authenticate user Login
function isLoggedIn(req, res, next) {
if(req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
and this basically was supposed to check if a user was already logged in.
and then I called the function as a middleware in one of my routes:
app.get('/secret', isLoggedIn , (req, res)=>{
res.render('secret');
});
This is supposed to make sure that the user is logged in or have signed up before they get access to the secret page, otherwise, it should return the login page and require that the user is logged in or has signed up to gain access to the secret page.
This is my full code just in case, you have a spotty eyes keener than mine.
var express = require('express'),
app = express(),
mongoose = require('mongoose'),
bodyParser = require ('body-parser'),
User = require('./models/user'),
passport = require('passport'),
localStrategy = require('passport-local'),
passportLocalMongoose = require('passport-local-mongoose');
mongoose.connect('mongodb://localhost/auth_demo_app', {
useNewUrlParser: true
});
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(passport.initialize());
app.use(passport.session());
app.use(require("express-session")({
secret: "Rusty is the worst and ugliest dog in the wolrd",
resave: true,
saveUninitialized: true
}));
passport.use(new localStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// ==================================================
// ROUTES
// ==================================================
app.get('/', (req, res)=>{
res.render('home');
});
app.get('/secret',isLoggedIn, (req, res)=>{
res.render('secret');
});
// AUTH ROUTES
// Register - Show Registration form
app.get('/register', (req, res)=>{
res.render('register');
});
// Handle user Signup
app.post('/register', (req, res)=>{
req.body.username
req.body.password
User.register(new User({username: req.body.username}), req.body.password, (err, user)=>{
if(err){
console.log(err);
return res.render('register');
}
passport.authenticate('local')(req, res, ()=>{
res.redirect('/secret');
})
})
});
// Login - Show Login form
app.get('/login', (req, res)=>{
res.render('login');
});
// Handle user Signup
app.post('/login', passport.authenticate('local', {
successRedirect: '/secret',
failureRedirect: '/login',
}),(req, res)=>{
// Other stuff goes here
});
// LOGOUT ROUTE
// Logs user out - ends user session
app.get('/logout', (req, res)=>{
req.logOut();
res.redirect('/');
});
// Authenticate user Login
function isLoggedIn(req, res, next) {
if(req.isAuthenticated()) {
console.log('User logged in successfully');
return next();
}
res.redirect('/login');
}
app.listen(3000, ()=>{
console.log('Server Started...');
});
console.log(req.isAuthenticated()) // Is always returning false.
Try changing the order of
app.use(passport.initialize());
app.use(passport.session());
app.use(require("express-session")({
secret: "Rusty is the worst and ugliest dog in the wolrd",
resave: true,
saveUninitialized: true
}));
to
app.use(require("express-session")({
secret: "Rusty is the worst and ugliest dog in the wolrd",
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
If you are using cookies make sure you add cookie-parser middleware
var express = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
If this is not the case check you calling end, if you are using axios include withCredentials
axios.get('some api url', {withCredentials: true});
if you are uisg fetch make sure to add credentials: 'include'
fetch('/...', {
method: ..,
headers: ...,
credentials: 'include',
body: ...
...})
Starting with version 0.2.1 passport-local-mongoose adds a helper method createStrategy as static method to your schema. The createStrategy is responsible to setup passport-local LocalStrategy with the correct options.
const User = require('./models/user');
// CHANGE: USE "createStrategy" INSTEAD OF "authenticate"
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
If you are not using a session middleware e.g., express-session you could use the following to sign the user and turn isAuthenticated() to its actual value:
req.logIn(user, { session: false });

How can I share data between routes on the request object in Express?

I'm working on an application that authenticates with Spotify's API. I am using passport-spotify to do so. I need to be able to access a session ID a my root route (/).
While I'm able to set the session id during the /callback after authentication with Spotify, I can't then access the session id at /. Can someone please explain to me how to pass data between routes in Express so that I can access req.session.id in / after I've authenticated?
I'll share my endpoints here:
/
app.get('/', cors(), (req, res) => {
if (req.session.id != null) {
res.json({isAuthenticated: true })
} else {
res.json({isAuthenticated: false, message: 'Please log in.' })
}
})
Passport Strategy
passport.use(new SpotifyStrategy({
clientID: clientId,
clientSecret: clientSecret,
callbackURL: CALLBACK_URL
},
(accessToken, refreshToken, profile, done) => {
process.nextTick(function () {
let user = { spotifyId: profile.id, access_token: accessToken,
refresh_token: refreshToken }
return done(null, user)
})
}))
/auth/spotify
app.get('/auth/spotify',
passport.authenticate('spotify', {scope: ['user-read-email', 'user-
read-private'], showDialog: true}),
(req, res) => {
})
/callback
app.get('/callback', passport.authenticate('spotify', {
failureRedirect: '/', successRedirect: FRONTEND_URL }), (req, res,
next) => {
req.session.id = req.user.spotifyId
localStorage.setItem('access_token_' + req.session.id,
req.user.access_token)
localStorage.setItem('refresh_token_' + req.session.id,
req.user.refresh_token)
return next(null, req.session.id)
})
There is no way to share data in the way you asked your question, and the reason thereof is very simple, they are 2 different req Objects.
The what you can do, is define a separate middlware function, something like this :
function middlware(req, res, next) {
req.session.id = req.user.spotifyId;
next();
}
And you call that function in both of your routs as a middlware after the Spotify middlware.

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.