I'm building a Node application in which the users must register or login, then when they drag and drop some elements (the front end is all working) I store on the database their action with their corresponding userId.
My understanding is that once they are registered/logged in, I can use the req.user to access their id and correctly store their actions, however it isn't working.
Here is the section of my server.js file that deals with Passport. Also, I'm using Sequelize as an ORM, but everything dealing with the database works perfect without the req.user part.
app.use(cookieParser());
app.use(bodyParser.json());
app.use(passport.initialize());
app.use(passport.session());
/****** Passport functions ******/
passport.serializeUser(function (user, done) {
console.log('serialized');
done(null, user.idUser);
});
passport.deserializeUser(function (id, done) {
console.log("start of deserialize");
db.user.findOne( { where : { idUser : id } } ).success(function (user) {
console.log("deserialize");
console.log(user);
done(null, user);
}).error(function (err) {
done(err, null);
});
});
//Facebook
passport.use(new FacebookStrategy({
//Information stored on config/auth.js
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: configAuth.facebookAuth.callbackURL,
profileFields: ['id', 'emails', 'displayName', 'name', 'gender']
}, function (accessToken, refreshToken, profile, done) {
//Using next tick to take advantage of async properties
process.nextTick(function () {
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(err) {
return done(err);
}
if(user) {
return done(null, user);
} else {
//Create the user
db.user.create({
idUser : profile.id,
token : accessToken,
nameUser : profile.displayName,
email : profile.emails[0].value,
sex : profile.gender
});
//Find the user (therefore checking if it was indeed created) and return it
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(user) {
return done(null, user);
} else {
return done(err);
}
});
}
});
});
}));
/* FACEBOOK STRATEGY */
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback//
app.get('/auth/facebook', passport.authenticate('facebook', { scope : ['email']}));
/* FACEBOOK STRATEGY */
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/' }),
function (req, res) {
// Successful authentication, redirect home.
res.redirect('../../app.html');
});
app.get('/', function (req, res) {
res.redirect('/');
});
app.get('/app', isLoggedIn, function (req, res) {
res.redirect('app.html');
});
app.post('/meal', function (req, res) {
//Testing Logs
/*console.log(req.body.foodId);
console.log(req.body.quantity);
console.log(req.body.period);
console.log(req.body);
*/
//Check whether or not this is the first food a user drops on the diet
var dietId = -1;
db.diet.findOne( { where : { userIdUser : req.user.idUser } } ).then(function (diet, err) {
if(err) {
return done(err);
}
if(diet) {
dietId = diet.idDiet;
} else {
db.diet.create( { userIdUser : req.user.idUser }).then(function (diet) {
dietId = diet.idDiet;
});
}
});
db.meal.create({
foodId : req.body.foodId,
quantity : req.body.quantity,
period : req.body.period
}).then(function (meal) {
console.log(meal.mealId);
res.json({ mealId : meal.mealId});
});
});
From what I read on the documentation for Passport, the deserializeUser function that I implemented should be called whenever I use req.user, however, with my console.logs(), I found out that serializeUser is called after logging in, therefore it is storing my session, but deserializeUser is never called! Ever.
Any idea on how to get around this? Any help is appreciated, thank you!
You need the express session middleware before calling passport.session(). Read the passportjs configuration section on documentation for more info.
Make sure to set cookieParser and express-session middlewares, before setting passport.session middleware:
const cookieParser = require('cookie-parser')
const session = require('express-session')
app.use(cookieParser());
app.use(session({ secret: 'secret' }));
app.use(passport.initialize());
app.use(passport.session());
To test if passport session is working or not, use:
console.log(req.session.passport.user)
(put in on a middleware for example)
In my case, i was using LocalStrategy and i was thinking i can protect and endpoint with simple username and password as form parameters, and i though passport will only use form parameters when it can't find user in session. but it was wrong assumption. in passport localStrategy, you should have separate endpoints for login and protected endpoint.
So Make sure you're using right middlewares for each endpoints. in my case:
wrong:
Protected endpoint:
app.get('/onlyformembers', passport.authenticate('local'), (req, res) => {
res.send({"res": "private content here!"})
})
correct :
Login:
app.post('/login', passport.authenticate('local'), (req, res) => {
res.send('ok')
})
Protected endpoint:
var auth = function (req, res, next) {
if (req.isAuthenticated())
return next();
res.status(401).json("not authenticated!");
}
app.get('/onlyformembers', auth, (req, res) => {
res.send({"res": "private content here!"})
})
Related
I have a react application that uses passport authentication strategy using express server. In my local machine my set up will start a server (app.js) in one port and react app in other port. And I'm able to authenticate successfully using passport. In the hosting environment the server will take care of starting the server and building the react client to public folder.
My issue happens after I push my code to the hosting environment, the hosting environment starts the express server (node app.js) and build the react app to public folder. Even though I'm able to authenticate using passport and able to receive the user information, if someone else was logged in from a different browser of a different machine then that user's profile is available in my browser.
app.js
const express = require('express');
const passport = require('passport');
...
let user = {}; //this is an object to store the profile data
...
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
var OpenIDConnectStrategy = require('passport-ci-oidc').IDaaSOIDCStrategy;
var Strategy = new OpenIDConnectStrategy({
clientID: settings.client_id,
clientSecret: settings.client_secret,
callbackURL: settings.callback_url,
function (iss, sub, profile, accessToken, refreshToken, params, done) {
process.nextTick(function () {
user = {...profile}
profile.accessToken = accessToken;
profile.refreshToken = refreshToken;
done(null, profile);
})
}
);
passport.use(Strategy);
app.use(express.static(__dirname + '/public'));
app.get('/login', passport.authenticate('openidconnect', {}));
app.get('/oidc_callback', function (req, res, next) {
passport.authenticate('openidconnect', {
successRedirect: redirect_url,
failureRedirect: '/failure',
})(req, res, next);
});
app.get('/user', function (req, res) { //This is the api i use to access user information in ract page
res.send(user);
});
app.get('/logout', function (req, res) {
user = {}; //empty the user data object
req.session.destroy();
req.logout();
res.end();
});
Here we can see I'm using a user object to store the user data and created a API /user (app.get('/user' ...) to access the user data from my client react application.
React component to access the user
constructor() {
super()
this.state = {
loggedUser: []
}
}
componentDidMount() {
axios.get('/user') //If I'm testing with local the API will be https://localhost:5000 - node server
.then(res => {
const loggedUser = res.data;
this.setState({ loggedUser });
}).catch(err => {
console.log("Fetch error", err)
})
}
Edit: completely reviewed answer after question edited with new data.
First of all we need a unique key in profile to uniquely identify a user, in my example I use "oid" but you can use the one better fits your requirements.
You could try to change following pieces of code:
let user = {}; //this is an object to store the profile data
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
// Later ...
function(iss, sub, profile, accessToken, refreshToken, params, done) {
process.nextTick(function () {
user = {...profile}
profile.accessToken = accessToken;
profile.refreshToken = refreshToken;
done(null, profile);
})
}
with:
let uniqueKey = "oid"; // The field name of profile which uniquely identify a user
let users = {};
passport.serializeUser((user, done) => done(null, (users[user[uniqueKey]] = user)[uniqueKey]));
passport.deserializeUser((key, done) => done(null, users[key]));
// Later ...
(iss, sub, profile, accessToken, refreshToken, params, done) => done(null, { ...profile, accessToken, refreshToken })
// or
(iss, sub, profile, accessToken, refreshToken, params, done) => {
process.nextTick(() => done(null, { ...profile, accessToken, refreshToken }));
}
All these are async functions, so when I say to return I mean doing it through the done callback function.
The first login function only need to return the user object (actually passport-ci-oidc seems to require it in process.nextTick, you can try with or without it).
serializeUser needs indeed to serialize the user object, which in our case means just to sore it in the memory store users[user[uniqueKey]] = user and then to return the unique key which passport should later use to deserialize the same user done(null, user[uniqueKey]).
As last deserializeUser need to return the user object by a given identification key, in our case just to return it from the memory sore done(null, users[key]).
Hope this helps.
following this tutorial I have build my azure ad login to use the graph api
https://learn.microsoft.com/en-us/graph/tutorials/node?tutorial-step=3
The response from azure looks got to me and I can't find any error message but it always triggers the failure redirect and therefore it does not proceed with the signInComplete function.
I was not able to figure out what's wrong. Any hint where I could look to at least to get an idea what triggered the failureRedirect?
// Callback function called once the sign-in is complete
// and an access token has been obtained
async function signInComplete(iss, sub, profile, accessToken, refreshToken, params, done) {
logger.debug("signInComplete function called")
if (!profile.oid) {
logger.error("No OID found in user profile.")
return done(new Error("No OID found in user profile."));
}
return done(null);
}
// Configure OIDC strategy
passport.use(new OIDCStrategy(
{
identityMetadata: `${config.OAUTH_AUTHORITY}${config.OAUTH_ID_METADATA}`,
clientID: config.OAUTH_APP_ID,
responseType: 'code id_token',
responseMode: 'form_post',
redirectUrl: config.OAUTH_REDIRECT_URI,
allowHttpForRedirectUrl: true,
clientSecret: config.OAUTH_APP_PASSWORD,
validateIssuer: false,
passReqToCallback: false,
scope: config.OAUTH_SCOPES.split(' '),
loggingLevel: 'warn'
},
signInComplete
));
office365Signin: function (req, res, next) {
passport.authenticate('azuread-openidconnect',
{
response: res,
prompt: 'login',
failureRedirect: '/',
failureFlash: true,
successRedirect: '/'
}
)(req,res,next);
},
office365SigninCallback: function(req, res, next) {
passport.authenticate('azuread-openidconnect',
{
response: res,
failureRedirect: process.env.FRONTEND_URL+'fail',
failureFlash: true,
successRedirect: process.env.FRONTEND_URL
}
)(req,res, next);
//failureRedirect gets always trigger even if the req.body looks correct and no error message is send
//Therefore we just check the body and trigger next if the body looks correct
var util = require('util');
logger.debug("office365SigninCallback res: "+util.inspect(res.req.body));
if (res.req.body.code && res.req.body.id_token) {
logger.debug("req.body seems correct there");
}
},
Logger office365SigninCallback res.req.body output:
office365SigninCallback res: { code: 'OAQABAAIAAAAm-06blBE1TpVMil8KPQ41qTM1deUhK_bLgEaGpsiIg5_3sa0ZNEBusd3m4rpBCrXflEsSvEtyjWWzqDhQ_9MybvYdqiR5B2FB59Msd7g8uL5YFcAExrGDqLzYo8xVIaZexHej_K3gDdJFfXbZZsiL6umdepdEXa1pyPIv4S8xVRHPcTyoB80RxpPp97uBCZagR7WstIF0QkfauUxklwlmOygAWjFvIMTuSijkkVZZ-04MbSX6wT3vBwJmQ2-kj6x_W_9fdCbYtdavgR6ZlYKtdiAxVm-3qULweEfvFo8RVC5xV2wdaPKqqYh41lcAq_1NHCiTdUcmmxbk8177WGzabDbH-rM-jRzORamSbLg_0vF48KWqu9zSgiCTX4RW556akFo6pcSkpriJWZH1aVl1cSMTWM64zb9tRM08O7hJ9YyFGTM-n6RpIiA3h9-Xh1E_TEZ8sG0noVId3yN8-gJXZ-pEB7Bur8s5C3DFOOlPgqgdEDj16tM8Wg0RinaL8P1BJ18k_Y_pr-huHMzhKaVCLYCX1Urq8fDomv0UAVchDQNIjdQ5PfiiYIT-0GYYzR5BB_5wGKJgwZypae89RRXpNJw-XOY5dv10jsUk3jEHRXW5xle2HtpM5DgCs6VbuxwVuaJfrRhfNdy7WkoOT3caV-4qTYfpfqwKvX_YtdD15RMpg-BVZQyI8b12meomlHdRi2aiqwqpTfJas0mrE7jHeScQErWx0qWAhvnZS8JJbauJGbXvjCbl2Tcoh19ngaggAA',
id_token:
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkN0VHVoTUptRDVNN0RMZHpEMnYyeDNRS1NSWSJ9.eyJhdWQiOiI1YTYxYTMwZC00MTAyLTQwMGUtYTVmNS05NDJlYzAxNDE3ODciLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vZTFkMDE4YTktOWRlZC00Yjg4LTllYTItNDJkYTAwY2Q1ZjVhL3YyLjAiLCJpYrRVl4UHJjRk9aVk5tX1N6UHNyX0ZrIiwib2lkIjoiZXQiOjE1ODk4ODA3MTIsIm5iZiI6MTU4OTg4MDcxMiwiZXhwIjoxNTg5ODg0NjEyLCJhaW8iOiJBVFFBeS84UEFBQUEzb0dsSkdWK1dYYlE0elZJNHZic2ZnejZvUWZqYy9XUkNtS1QrYlRORHVjM2dYdll4L3F4OTdEMm5VeTRUUUxNIiwiY19oYXNoIjoiU1k4alM1ZjlxMkg4TU9oSmVIcjNhQSIsIm5hbWUiOiJUaW0gU3RlaW4iLCJub25jZSI6IkZfWjctTmpGSU5jgyYWRkNGMtOGM2MS00YjYxLWJmYWItZmQzNmE2YzY0MjY5IiwicHJlZmVycmVkX3VzZXJuYW1lIjoidHN0ZWluQFZBTlRBLklPIiwic3ViIjoiZ3Q3RExJZ1Y0MEJDZlRxRXhhSlM5Z09qcjhlRWU3TFF0cTJLQk9yNThmbyIsInRpZCI6ImUxZDAxOGE5LTlkZWQtNGI4OC05ZWEyLTQyZGEwMGNkNWY1YSIsInV0aSI6IjlyRHBOM0hBLTAtU3Y2T25XWmpVQUEiLCJ2ZXIiOiIyLjAifQ.uCG5x4cesT2925Kr_lXloYWxgIsPfsRX2FKd4t8ASDeQXg9PdvjTsTvnzzBqFDtW77obSX7bO75a-0XjA9TIh4-kMTgJWm8PlnHCWaHRQgfNlTmjp99oUf0msZx6OhyZ0-xFMMe6DTShFfBhHjF2ds17zw-oynv6PaygSox4s94qvL2e8ULi2wfpm4AYQwxXeUQba9dhoQu8AsCozY-6NyWIGc2alzg7TK5qBpuY16BScGsUkmChGFZ9lF9vD-uM8x0JYg0G6Uvc_aDNIWnt9B7VRH-U9sIFXtL9doaJXvRl2aPQnj6x0rtfgfJ4zonrJZQEn7e8y7XPIcnU0gMO9g',
state: 'GNy7cIjlBvfga4FhQiapnWnDAn8itXtk',
session_state: 'c88bace3-4039-9922-6f06-dcd6ba1a62ac' }
Based on the documentation the response looks correct to me:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
I am grabbing this code straight from one of my projects in production. I was facing a similar problem before but it kind of resolved by itself when we were tweaking the done callback for some reason.
Maybe this can serve as an example.
Passport.js
'use strict';
//Dependencies
const passport = require('passport');
const OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
//Custom Modules
const msService = require('./msService');
const DB_Connection = require('../dbConnection');
//DB Setting
require('../models/MS_User');
const User_DB = DB_Connection.model('user');
//Winston Logger
const logger = require('../log');
const passLog = logger.get('passportLog');
//Azure AD Creds
const loginCredentials = require('../creds/oicdCreds');
// Session
//Take in outlook id => keep the session data small
passport.serializeUser((outlookid, done) => {
done(null, outlookid);
});
//Deserialize when needed by querying the DB for full user details
passport.deserializeUser((outlookid, done) => {
User_DB.findOne({ outlookId: outlookid })
.then(user => {
done(null, user);
})
.catch(err => passLog.error(`Error Deserializing User: ${outlookid}:` + ' ' + err));
});
//Export the passport module
module.exports = (passport) => {
//OpenIdConnect
passport.use(new OIDCStrategy(loginCredentials,
//Verify callback for passReqToCallback: false
(iss, sub, profile, access_token, refresh_token, params, done) => {
//Get Calendar ID
msService.getCalId(access_token, calId => {
//Create or update the user
User_DB.findOneAndUpdate({ outlookId: profile.oid }, {
name: profile.displayName,
outlookId: profile.oid,
email: profile._json.email,
lastLogin: profile._json.ipaddr,
accessToken: access_token,
calId: calId
}, { upsert: true, returnNewDocument: false })
.catch(err => passLog.error(`Error Adding / Rnewing User: ${profile.oid}:` + ' ' + err));
//Return Profile ID for Serialization
done(null, profile.oid);
});
}));
};
The routes
//AD OpenIdConenct
//Login
router.get('/auth/outlook/login',
//Using MS Azure OpenId Connect strategy (passport)
passport.authenticate('azuread-openidconnect')
);
//Callback Handling
//Using MS Azure OpenId Connect strategy (passport)
router.post('/auth/outlook/callback', passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/outlook/login' }), (req, res) => {
//Redis
client.keys('*', (err, keys) => {
sessionLog.info(`Login Active Session: ${keys.length}`);
});
res.redirect('/profile_info');
}
);
If you come by this in question in google I had the same error, and it turned out I was using the Azure Secret ID, not the Secret Value
Add loggingNoPII: false to your OIDC strategy, this will print out any errors.
I am running an express server and came across a middleware problem with passport authenticate which is something like this
passport.use('google', new GoogleStratergy({
clientID: "",
clientSecret: "",
callbackURL: "/Users/auth/google/callback",
passReqToCallback : true
}, (request, accessToken, refreshToken, profile, done) => {
Users.findOne({ UserID: profile.id }, function (err, user) {
if (err){
return done(err);
}
if (!user){
var User = new Users({});
User.setPassword(Math.random().toString(36).substring(5));
var jwt = User.generateJWT();
User.save()
.then(() => {
return done(null, User, jwt);
})
.catch(err => {
return done(err);
})
}
else {
var jwt = user.generateJWT();
return done(null, user, jwt);
}
});
}
));
just now I don't know how to access the objects passed in the done function when using the middleware in another route like this.
router.get('/something', passport.authenticate('google'), (req, res, next) => {
// Now I need to access User and jwt object here passed with done()
res.send("whatever");
})
Have you checked the req object? do this
router.get('/something', passport.authenticate('google'), (req, res, next) => {
console.log(req.user)
console.log(req.jwt)
// Now I need to access User and jwt object here passed with done()
res.send("whatever");
})
Hello I am needing some assistance with setting up a function to check if a user is logged in or not with Passport.js using the slack strategy.
I have a localstrategy which works fine but I also want to implement a sign in with slack as well.
I have added my app to slack and set the redirect URL. Everything is working fine however regardless if a user signs in or not they can still access the main page from the URL.
Example I have my login page 123.456.7.891:3000/login and when a user logs in through the local login or slack it redirects to 123.456.891:3000/index. Well if you know the URL for index you can just navigate to it.
For the local strategy I used this function to prevent that. It checks if the user is logged in or not.
function isLoggedIn(req, res, next){
if(req.isAuthenticated()){
return next();
}
req.flash("error", "Must be signed in first to access this page.");
res.redirect("/login");
}
And than I simply add the isLoggedIn function to the route like this.
app.get("/QAApplicationHub", isLoggedIn, (req, res) => {
Application.find({}, (err, allApplications) => {
if(err){
console.log(err);
} else {
res.render("index", { application: allApplications });
// username: req.user.name
}
});
});
The issue I am having with when a user logs in with slack is when they are redirected to the redirect URL it just takes them back to the login page stating that the user must be logged in to access the page. The message appears because I have flash set up to show the user the error. It seems that with my current code the isLoggedIn only checks for the local login and not slack.
So how can I implement the isLoggedIn function for both the local and slack strategy? Or what method is it that I need to implement for it to work for both.
This is my code for Passport-slack.
// Configure the Slack Strategy
passport.use(new SlackStrategy({
clientID: process.env.SLACK_CLIENT_ID = '123456',
clientSecret: process.env.SLACK_CLIENT_SECRET ='123abc',
}, (accessToken, scopes, team, extra, profiles, done) => {
done(null, profiles.user);
}));
//=============================
//LOCAL LOGIN MIDDLEWARE
//=============================
app.post("/login", passport.authenticate("local", {
successRedirect: "/QAApplicationHub",
failureRedirect: "/login",
failureFlash: true
}));
app.get("/logout", (req, res) => {
req.logout();
req.flash("success", "Successfuly signed out!")
res.redirect("/login");
});
// =============================
// PASSPORT-SLACK MIDDLEWARE
// =============================
// path to start the OAuth flow
app.post('/auth/slack', passport.authorize('slack', {
successRedirect: "/QAApplicationHub",
failureRedirect: "/login",
failureFlash: true
}));
// OAuth callback url
app.get('/auth/slack/callback',
passport.authorize('slack',
(req, res) => {
res.redirect('/QAApplicationHub')
}));
I believe users are stored in the req.user property. Try this:
function isLoggedIn(req, res, next){
if(!req.user){
req.flash("error", "Must be signed in first to access this page.");
res.redirect("/login");
} else {
return next();
}
}
So turns out I was missing quit a bit of code to make this complete. First I needed to add the user schema for slack. Second needed to add a function to create the user and save it to my db and a function to check if a user had already signed in with slack and to just compare the ID instead of making a new instance of the user to the DB. Also needed to update the serialize user and de-serialize user.
Here's my schema
let mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
let UserSchema = new mongoose.Schema({
local: {
name: String,
username: String,
password: String
},
slack: {
username: String,
slackid: Number,
name: String
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
And than the set up for the passport strategies.
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(null, user.id);
});
});
passport.use(new LocalStrategy(User.authenticate()));
// Configure the Slack Strategy
passport.use(
new SlackStrategy({
callbackURL: "/auth/slack/redirect",
clientID: keys.slack.clientID,
clientSecret: keys.slack.clientSecret
}, (accessToken, refreshToken, profile, done) => {
console.log(profile);
// Check if user already exist in DB
User.findOne({username: profile.displayName}).then((currentUser) => {
if(currentUser){
// already have the user
console.log('User is', currentUser);
done(null, currentUser);
} else {
// If not, create new user in DB
new User({
username: profile.displayName,
slackid: profile.id
}).save().then((newUser) => {
console.log("new user created: " + newUser);
done(null, newUser);
});
}
});
}));
Also needed to update the routes for the auth and call back.
app.post('/auth/slack',
passport.authenticate('Slack', {
scope:['identity.basic'],
}));
// OAuth callback url
app.get('/auth/slack/redirect', passport.authenticate('Slack'), (req, res) => {
res.redirect("/QAApplicationHub");
});
Hopes this helps others in need of passport.js help. For details info I recommend looking up The Net Ninja on YT. He helped me a lot with my code gap and helped understand what was actually going on.
I need some help with setting up Passport Consumer Strategy and integrating it into "locals" (Right now, we have the local strategy working just fine). We have tried several approaches but with no luck on it working 100%. The below code is not the complete code, we have taken out some of it so this post doesn't get too long. Any help with this would greatly be appreciate. There could be compensation as well if someone can get us over this hurdle.
So one question is, if the user is authenticated by the consumer key and secret, how does Passport store the session variables so they are used throughout the site?
Second question, how do we handle the user after it passes the authentication process?
Both local and consumer need to be working.
Consumer key and secret using a POST by a Provider <- I can show some of the post if needed.
This needs to be OAuth1 Only, as of right now, OAuth2 isn't an option.
This is for a single sign-on authentication.
I can supply a consumer session output if needed.
Ultimately, we would like the local strategy and the consumer strategy working with the same "locals" global variables. As far as I can tell, we can authenticate the consumer, retrieve the user from our DB, create a session and can tell if user is "ensureAuthenticated".
Here is what we have working right now.
Local strategy is authenticating correctly.
We render the pages with these local variables:
"Omitted most of the code to save time."
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
...
function renderPage(req, res, pageName, pageTitle){
res.render(pageName, {
pageName: pageTitle,
username: req.user ? req.user.username : '',
...
Consumer strategy is authenticating by a POST request from a "Provider"
We have tried adding the Consumer strategy to the authentication.
server.js
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
ADDED -> authentication.initConsumerStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
ADDED -> passport.authenticate('consumer', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
authentication.js (omitted code)
module.exports = function(siteConfig, defaultRedirectPage, server, sessionStore, log) {
var passport = require('passport')
...
, ConsumerStrategy = require('passport-http-oauth').ConsumerStrategy
, TokenStrategy = require('passport-http-oauth').TokenStrategy
, LocalStrategy = require('passport-local').Strategy;
var auth = {};
var authenticationRedirects = { successRedirect: '/', failureRedirect: '/login' };
passport.serializeUser(function(user, done) {done(null, user);});
passport.deserializeUser(function(obj, done) {done(null, obj);});
auth.authenticate = function(email, password, callback) {
email = email.toLowerCase();
userController.findUserByUsernameWithPermissions(email,
function(err, user) {
if (err) return callback(err);
if (!user) return callback(null, null, 'Incorrect username.');
bcrypt.compare(password, user.password_hash, function(err, res) {
if(err){return callback(err);
} else if (!res) {return callback(null, null, 'Incorrect password.');
} else {if (user.account_state>0) {callback(null, user);} else {return callback(null, null, '/reset?rand='+user._id);}}
});
}
);
}
auth.initLocalStrategyRoutes = function(app){
passport.use(new LocalStrategy(auth.authenticate));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (!user) return res.send({success: false, message: info});
req.logIn(user, function(err) {
if (err) { return next(err); }
res.send(req.user);
});
}) (req, res, next);
});
}
auth.initConsumerStrategyRoutes = function(app){
// passport.use(new LocalStrategy(auth.authenticate));
console.log('app: ', app)
passport.use('consumer', new ConsumerStrategy(
function(key, done) { console.log('starting ConsumerStrategy');
dbConsumerKey.findByConsumerKey({consumerKey: key}, function(err, consumerKey) {
if (err) { return done(err); }
if (!consumerKey) {
var errCode = dbError.find({name:'no_resource_link_id'}, function(err, errorCodes) {
console.log('statusText: ', errorCodes[0]["statusText"]);
return errorCodes[0]["statusText"];
});
return done(null, errCode);
} else {
if (!consumerKey[0]["consumerKey"]) { return done(err); }
if (!consumerKey[0]["consumerSecret"]) { return done(err); }
// return done(null, consumerKey[0]["consumerKey"], consumerKey[0]["consumerSecret"]);
return done(null, consumerKey[0], consumerKey[0]["consumerSecret"]);
}
});
},
function(requestToken, done) {
dbRequestTokens.find(requestToken, function(err, token) {
console.log('inside requestToken');
if (err) { return done(err); }
var info = { verifier: token.verifier,
clientID: token.clientID,
userID: token.userID,
approved: token.approved
}
done(null, token.secret, info);
});
},
function(timestamp, nonce, done) {
done(null, true)
}
));
};
auth.initTokenStrategyRoutes = function(app){}
auth.addUser = function(username, email, password, callback){auth.authenticate(username, "pass", callback);}
return auth;
};
The authentication.js strategy does validate the consumer key and secret. but it doesn't create the session variable we are wanting. We would like the consumer strategy code to be in the authentication.js file.
Now here is another approach, we created a separate files called consumerkey.js
This direction works to a point. We can output the passport session either on the screen or on the command line.
var passport = require('passport')
exports.launchLti = [
passport.authenticate('consumer', { session: false/true [tried both] }),
function(req, res) {
db.findByStudentUserId({lis_person_contact_email_primary:
req.body.lis_person_contact_email_primary}, function(err, user) {
req.logIn(user, function(err) {
req.user.username = user[0].lis_person_contact_email_primary;
...
// req.session.save(function(){
// res.redirect('/classes');
res.redirect(200,'/');
// });
});
})
// res.render('launch', {launch: launch});
}
}]
I solved this issue by changing some of my code structure.
app.pst('/launch/lti/:id', function(req, res, next) {
passport.authenticate('consumer', {failureRedirect: '/login'}),
dbConsumerKey.findByStudentUserId({},
function(err, user) {
if (err) console.log(err, user);
req.logIn(user, function(err) {
if (err) return err;
ADDED -> req.session.valid = true;
ADDED -> res.redirect('/');
});
}
});
});
and modifying the render page function to adapt to the incoming information.
function renderPage(req, res, pageName, pageTitle){
...
this is where the locals are created
...
this allowed me to use my current local strategy as is and adding a totally different strategy route but making the session correctly.