How do I authenticate a React app using passport? - node.js

I have a React app.
I also have an Express server, that using passport-saml I can authenticate against the company's PingID SSO IdP.
I would like to get the React app, to somehow call the Express app, to authenticate.
The Express Server and the React app are running on the same host.
Here's what I have:
// Express App - rendering code pulled out
const express = require('express');
var passport = require('passport');
var Strategy = require('passport-saml').Strategy;
var fs = require('fs')
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const app = express();
const port = process.env.PORT || 4005;
passport.use('saml2', new Strategy({
path: 'http://MYSERVER:4005/assert',
entryPoint: 'https://sso.connect.pingidentity.com/sso/idp/SSO.saml2?XXXXXXXX',
issuer: 'MYAPP',
audience: 'MYAPP',
},
function(profile, cb) {
return cb(null, profile);
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSession({
secret: '123xyz',
resave: true,
saveUninitialized: true
}));
// Initialize Passport and restore authentication state, if any, from the session.
app.use(passport.initialize());
app.use(passport.session());
app.get('/login/idp', () =>{
passport.authenticate('saml2')
console.log('Authentication called');
});
app.get('/login', () =>{
console.log('Authentication failed, try again');
});
app.post('/assert',
passport.authenticate('saml2', { failureRedirect: '/login' }),
function(req, res) {
console.log('Authentication succeeded');
console.log(req.user)
res.redirect('/');
});
app.listen(port, () => console.log(`Listening on port ${port}`));
In my React app's package.json I have:
{
...
"proxy": "http://localhost:4005/",
...
}
The outline of the toy Create React App is:
// Create React App
import React, { useState } from 'react';
import './App.css';
function App() {
const handleLogin = async e => {
e.preventDefault();
const response = await fetch('/login/idp', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
};
return (
<div className="App">
<form onSubmit={handleLogin}>
<button type="submit">Login</button>
</form>
</div>
);
}
export default App;
I can click happily on the button, and the console shows that the Express server's GET is triggered, but nothing comes back to the React client.
Is proxying the way to go? Do I have the right approach? If so, how do I get the result back from the Express app into the React app?

I have a solution, but it seems like an awful hack. However, it works, and I need to get this over the line. If anyone can suggest an improvement or alternative approach, I'd be grateful.
We start with a basic Express server (hosted at 4005), that can validate the user via Passport-SAML SSO:
const express = require('express');
const jwt = require('jsonwebtoken')
const passport = require('passport');
const Strategy = require('passport-saml').Strategy;
require('dotenv').config()
const signature = process.env.SIGNATURE
const expiresIn = process.env.EXPIRESIN
// Simplification: actually there's a db look-up here
// based on req.user in order to get just the id
// but you get the idea
const createToken = user =>
jwt.sign({ user.email }, signature, { expiresIn: expiresIn })
passport.use('saml2', new Strategy({
path: 'http://localhost:4005/assert',
entryPoint: 'https://sso.connect.pingidentity.com/sso/idp/SSO.saml2?idpid=XXXX_YOURVALUEHERE_XXXX',
issuer: 'XXXX_YOURIDHERE_XXXX',
audience: 'XXXX_YOURIDHERE_XXXX',
},
function(profile, cb) {
return cb(null, profile);
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// Create a new Express application.
var app = express();
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.get('/login/idp', passport.authenticate('saml2'));
app.post('/assert',
passport.authenticate('saml2',
{ failureRedirect: `http://localhost:3000/?error=unauthenticated` } ),
function(req, res) {
const token = createToken(req.user)
res.redirect(`http://localhost:3000/signinOK?token=${token}`);
});
app.listen(4005);
Then in the React src folder, add the required setupProxy.js:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/login',
createProxyMiddleware({
target: 'http://localhost:4005',
headers: {
"Connection": "keep-alive"
}
})
);
};
Then in the React app (hosted at port 3000) we create a simple button component for the front page:
import React from 'react'
import { Button } from '#material-ui/core'
function StartBtn() {
return (
<Button type="submit" variant="contained" color="primary" >
Login
</Button>
)
}
export default StartBtn
At this point, we stick the <StartBtn /> on the front page, and rig up a Route that responds to http://localhost:3000/signinOK?token=... by grabbing the token, using that as the value in any subsequent bearer: authentications, and redirecting to the main site.
The flow is as follows:
User loads front page, and clicks the <StartBtn/>;
Link is redirected thanks to setupProxy.js to the Express server;
Express server attempts the Passport-SAML authentication;
The result of the authentication process is a POST call from the IdP (PingID Authentication Server) to the Express server, on the /assert route.
The result either succeeds or fails, but in both cases re-directs to the React app.
In case of success, the user details are returned as JWT; or
In case of failure, an error is returned.
I'll come back to this answer, if I can find ways to improve it, or expand on the JWT stage.
I hope that someone either (a) finds this useful, or (b) invents a time-machine, goes back and posts this 3 weeks ago, so that I can save more of my remaining hair follicles. Or (c) tells me the way I should have done it.

Related

CORS error while implementing google Oauth2 (MERN)

I have implemented local as well as google login using passport.js in a mern web application. The local authentication is working fine with the frontend but I am getting errors when using the Google strategy.
Error:
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?.........' (redirected from 'http://localhost:5000/auth/google') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:84)
Issue shown by the console:
Indicate whether a cookie is intended to be set in a cross-site context by specifying its SameSite attribute.
Indicate whether to send a cookie in a cross-site request by specifying its SameSite attribute
I tested the google strategy using POSTMAN and it was working fine but when requesting from my frontend there seems to be some issue.
server.js
require("dotenv").config();
const express=require("express");
const cors=require("cors");
const mongoose=require("mongoose");
const session=require("express-session");
const passport=require("passport");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const errorController=require("./controllers/errorController")
const app=express();
const port = process.env.PORT || 5000;
app.use(
cors({
origin: "http://localhost:3000", // <-- location of the react app were connecting to
credentials: true,
})
);
app.use(express.static("public"));
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(session({
secret:process.env.SECRET,
resave:false,
saveUninitialized:false,
}));
app.use(passport.initialize());
app.use(passport.session());
const User=require("./models/user.model");
passport.use(User.createStrategy());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "http://localhost:5000/auth/google/keeper"
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
mongoose.connect(process.env.MONGO_URI, {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify:false, useCreateIndex:true});
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDB database connection established successfully");
});
const loginRouter=require("./routes/login");
const registerRouter=require("./routes/register");
const logoutRouter=require("./routes/logout");
const authRouter=require("./routes/auth");
app.use("/login", loginRouter);
app.use("/register", registerRouter);
app.use("/logout", logoutRouter);
app.use("/auth/google", authRouter);
app.use(errorController);
app.listen(port, function(){
console.log("server started on port 5000");
});
auth.js (handles the google login related routes)
const router=require("express").Router();
const passport=require("passport");
router.get("/", passport.authenticate("google", { scope: ["profile"] }));
router.get("/keeper",
passport.authenticate("google", { failureRedirect: "/login" }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect("/");
});
module.exports=router;
Axios request from the frontend
function googleLogin(event){
Axios({
method: "GET",
withCredentials: true,
url: "http://localhost:5000/auth/google",
})
.then(function(res){
console.log(res);
})
.catch(function(err){
console.log(err);
})
event.preventDefault();
}
button that triggers the request
<button className="btn btn-danger" onClick={googleLogin}>Sign in with Google</button>
You can't make an axios call to the /auth/google URL!!
Here's my solution...
// step 1:
// onClick handler function of the button should use window.open instead
// of axios or fetch
const googleLogin = () => window.open("http://[server:port]/auth/google", "_self")
//step 2:
// on the server's redirect route add this successRedirect object with correct url.
// Remember! it's your clients root url!!!
router.get(
'/google/redirect',
passport.authenticate('google',{
successRedirect: "[your CLIENT root url/ example: http://localhost:3000]"
})
)
// step 3:
// create a new server route that will send back the user info when called after the authentication
// is completed. you can use a custom authenticate middleware to make sure that user has indeed
// been authenticated
router.get('/getUser',authenticated, (req, res)=> res.send(req.user))
// here is an example of a custom authenticate express middleware
const authenticated = (req,res,next)=>{
const customError = new Error('you are not logged in');
customError.statusCode = 401;
(!req.user) ? next(customError) : next()
}
// step 4:
// on your client's app.js component make the axios or fetch call to get the user from the
// route that you have just created. This bit could be done many different ways... your call.
const [user, setUser] = useState()
useEffect(() => {
axios.get('http://[server:port]/getUser',{withCredentials : true})
.then(response => response.data && setUser(response.data) )
},[])
Explanation....
step 1 will load your servers auth url on your browser and make the auth request.
step 2 then reload the client url on the browser when the authentication is
complete.
step 3 makes an api endpoint available to collect user info to update the react state
step 4 makes a call to the endpoint, fetches data and updates the users state.

MERN OAuth Implementation does not redirect to the google login page

I'm currently running a webserver using the MERN stack, and I'm trying to get OAuth login working properly. However, when I click the "login with google" button, react loads the homepage (but the URL changes). Fetching the URL directly gets a 302 response from the server, but my front-end doesn't change.
Server.js
const express = require('express');
const path = require('path');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const logger = require('morgan');
const cors = require('cors');
const secure = require('express-force-https');
const passport = require('passport');
const cookieSession = require('cookie-session');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
const dbRoute = process.env.MONGODB_URI || 'NO DB ROUTE PROVIDED';
// db setup
mongoose.connect(
dbRoute,
{
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: process.env.DATABASE_NAME,
}
);
let db = mongoose.connection;
db.once('open', () => console.log("Connected to the database"));
db.on('error', console.error.bind(console, "MongoDB connection error: "));
// middleware
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false })); // body parsing
app.use(bodyParser.json());
app.use(logger("dev"));
app.use(express.static(path.join(__dirname, "client", "build"))); // for serving up the clientside code
app.use(secure); // ensure that the connection is using https
app.use(cookieSession({ // cookies!
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
keys:['vcxzkjvasddkvaosd'] // yeah i'm sure that's secure enough
}));
// models
require('./models/rule');
require('./models/affix');
require('./models/user');
// passport security
require('./config/passport');
app.use(passport.initialize());
app.use(passport.session());
// routes
app.use(require('./routes'));
// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
app.listen(port);
console.log(`Server listening on ${port}`);
Route (There are a few index files in different folders, so the full path for this route it /api/user/google)
const mongoose = require('mongoose');
const passport = require('passport');
const router = require('express').Router();
const auth = require('../auth');
const User = mongoose.model('User');
router.get('/google',
passport.authenticate('google', {
scope: ['profile', 'email']
})
);
router.get('/google/callback',
passport.authenticate('google', { failureRedirect: '/affixes'}),
(req, res) => {
res.redirect('/?token=' + req.user.token);
}
);
Passport.js
const mongoose = require('mongoose');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
require('dotenv').config();
const User = mongoose.model('User');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id).then((user) => {
done(null, user);
})
});
passport.use(new GoogleStrategy({
clientID: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
callbackURL: '/api/user/google/callback',
proxy: true
},
(accessToken, refreshToken, profile, done) => {
User.findOne({ googleId: profile.id })
.then((existingUser) => {
if (existingUser) {
done(null, existingUser);
} else {
new User({ googleId: profile.id }).save()
.then((user) => done(null, user));
}
});
}
));
Frontend login page (has a fetch button and a link button. As described above, different behavior)
import React from 'react';
import {
ComingSoon
} from '../Common';
import {
Button
} from '#material-ui/core';
const handleClick = () => {
fetch('/api/user/google')
}
export default function Login() {
return (
<>
<Button onClick={handleClick}>
Login with Google
</Button>
<button>Log in with Google</button>
</>
);
}
Update: Looks like some kind of CORS issue, although I still don't know how to fix it. Browser spits out
Access to fetch at '...' (redirected from 'http://localhost:3000/api/user/google') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Adding the requested header gives me
Access to fetch at '...' (redirected from 'http://localhost:3000/api/user/google') from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
It turns out I was quite wrong about the nature of this issue! The problem was that my fetch requests to my OAuth endpoint were calling my frontend, not my backend because the request included text/html in its Accept header. Using the react advanced proxy setup to route to the proper URI fixed the issue.
See: https://github.com/facebook/create-react-app/issues/5103 and https://github.com/facebook/create-react-app/issues/8550

request session not persistent - express-session

I'm trying to have a session containing user data in the node.js/express FW.
I'm using express-session. I'm not using session store yet.
I have 2 pages in the client (angular) where I iterate between - Login and Dashboard. The idea is to create the session after successful login, then routing to the dashboard page. In the dashboard page I have an anchor with routinlink to the login:
<a [routerLink]="['/login']" >BackToLogin</a>
When navigating back to the loginPage (when activating a route), I execute a service with an end-point to the express server which check if the request has a session with a request in it (I expect it to be). The problem is that I see that the session is not the same session (the id changes)
See my code:
Node.js side - server.js file:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const session = require ('express-session');
var cookieParser = require('cookie-parser');
const SESS_NAME = 'sid';
app.use(session({
name:SESS_NAME,
key: 'user_sid',
resave:false,
saveUninitialized:false,
secure: process.env.NODE_ENV ==="production",
secret:'<some random text>',
cookie:{
httpOnly: true,
secure: process.env.NODE_ENV ==="production",
expires: 60000
}
}));
app.use(bodyParser.text());
app.use(bodyParser);
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(cors()); //No limitation for test reasons
app.use(cookieParser());
//disabled on purpose
//var sessionManagement = require('./middleware/sessionManagement');
// API
app.use("/", require("./api/v1/routes.js"))//This file includes:
/*
const express = require('express');
const router = express.Router();
router.use("/login", require('./login'));
router.use("/session", require('./session'));
module.exports = router;
*/
...etc
app.listen(config.port, () => console.log(`Process ${process.pid}: Listening on port ${config.port}`));
login.js on the server: responsible for validating user and store user data in session:
const express = require('express');
const router = express.Router();
const schema = require('./objectSchemaJson.schema.json');
const scehmaCheck = require('../../middleware/checkForSchema')(schema);//this is
a schema check (middleware) - if suceeded continue (next)
const storeSession = (req, dataResult) =>
{
if (<dataResult return with valid use data>) //This is "where the magic happanes"
{
req.session.user = {
username: <get userName from dataResult>,
ID: <Get ID from dataResult>,
Role: <Get Role from dataResult>
}
}
}
router.use("/", scehmaCheck, (req, res, next) => {
return GetUserDataFROmDB(req.body).then((dataResult) => { //reaching the DB - not mentioned here on purpose
storeSession(req, dataResult); // This is where the session set with user data
res.status(200).json(dataResult);
}).catch((err) => {
next({
details: err
})
});
});
module.exports = router;
This is the end point on the server that responsible for getting the session - session.js - This is where the problem appears - the res.session has a session ID which is different that the one I created after the login
const express = require('express');
const router = express.Router();
hasSession : function(req, res) //This is where the problem appears - the res.session has a session ID which is different that the one I created after the login
{
if (req.session.user)
{
res.status(200).json(
{
recordsets: [{Roles: req.session.Roles, UserName: req.session.user.username}]
});
}
else{
res.status(200).json({});
}
}
router.use("/", (req, res, next) => { return sessionManagement.hasSession(req, res, next)});
module.exports = router;
Client side:
//HTML:
<div>
<label>Username:</label>
<input type="text" name="username" [(ngModel)]="userName" />
</div>
<div>
<label>Password:</label>
<input type="password" name="password" [(ngModel)]="password"/>
</div>
<div>
<button (click)="login()">Login</button>
</div>
//COMPONENT:
login()
{
this.srv.login(this.userName, this.password).subscribe(result =>
{
if (<result is valid>)
{
this.router.navigate(['/dashboard']);
}
}
);
}
//This reach the node.js endpoint and routing to the session.js end point - it is executes when the router-outlet activated in the app.component:
/*
onActivate(componentRef : any)
{
if (componentRef instanceof LoginComponent)
{
componentRef.getSession();
}
}
*/
getSession() : void
{
this.sessionService.getSession().subscribe( result =>
{
if (<result is valid>)
{
this.router.navigate(['/dashboard']);
}
});
}
I found a similar question on github - no solution yet:
https://github.com/expressjs/session/issues/515
but it might be a cookie <-> server configuration issue.
Found the problem - the root cause was that the client didn't send a cookie when making an httprequest.
2 things needed to be done in order to solve the problem:
1. CORS Definition
Set the CORS definition to creadentials: true along with the origin (the host name of the client, which is probably with a different port\hostname):
app.use(cors({
origin: config.origin,
credentials: true
}));
2. Set crendentials
For every http rest method (get and post, in my case) add withCredentials property with a value of true:
return this.http.get<any>(<path>, { withCredentials: true })
or
return this.http.post<any>(<path>, <body>, { withCredentials:true })

Passport req.user not persisting across requests -

Using passport.js local strategy I am trying to use the req.user to obtain current user id so that I can store recipes in the database with the users id. The problem seems to be around the deserialization part of the passport.js file I have in my config file in my app. Whenever I hit the /api/saveRecipe route for some reason it gets deserialized and the req user is then no longer available.
Notes: I am authenticating on my backend server using react on the front end.
Below is my server.js file
Problem: req.user is available after calling passport.authenticate('local') but once api/saveRecipe route is hit req.user is no longer available.
After researching this subject on S.O. it appears that it most often has to do with order in the server file setup but i have looked and reviewed and i believe my setup correct...
const express = require("express");
const bodyParser = require("body-parser");
const session = require("express-session");
const routes = require("./routes");
// Requiring passport as we've configured it
let passport = require("./config/passport");
const sequelize = require("sequelize");
// const routes = require("./routes");
const app = express();
var db = require("./models");
const PORT = process.env.PORT || 3001;
// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// passport stuff
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static("public"));
// We need to use sessions to keep track of our user's login status
// app.use(cookieParser('cookit'));
app.use(
session({
secret: "cookit",
name: "cookit_Cookie"
})
);
app.use(passport.initialize());
app.use(passport.session());
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/public"));
}
// the view files are JavaScript files, hence the extension
app.set('view engine', 'js');
// the directory containing the view files
app.set('pages', './');
// Add routes, both API and view
app.use(routes);
// Syncing our database and logging a message to the user upon success
db.connection.sync().then(function() {
console.log("\nDB connected\n")
// Start the API server
app.listen(PORT, function() {
console.log(`🌎 ==> API Server now listening on PORT ${PORT}!`);
});
});
module.exports = app;
my passport.js code
//we import passport packages required for authentication
var passport = require("passport");
var LocalStrategy = require("passport-local").Strategy;
//
//We will need the models folder to check passport against
var db = require("../models");
// Telling passport we want to use a Local Strategy. In other words, we want login with a username/email and password
passport.use(
new LocalStrategy(
// Our user will sign in using an email, rather than a "username"
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},
function(req, username, password, done) {
// console.log(`loggin in with email: ${username} \n and password: ${password}`)
// When a user tries to sign in this code runs
db.User.findOne({
where: {
email: username
}
}).then(function(dbUser) {
// console.log(dbUser)
// If there's no user with the given email
if (!dbUser) {
return done(null, false, {
message: "Incorrect email."
});
}
// If there is a user with the given email, but the password the user gives us is incorrect
else if (!dbUser.validPassword(password)) {
return done(null, false, {
message: "Incorrect password."
});
}
// If none of the above, return the user
return done(null, dbUser);
});
}
)
);
// serialize determines what to store in the session data so we are storing email, ID and firstName
passport.serializeUser(function(user, done) {
console.log(`\n\n serializing ${user.id}\n`)
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log(`\n\n DEserializing ${id}\n`)
db.User.findOne({where: {id:id}}, function(err, user) {
done(err, user);
});
});
// Exporting our configured passport
module.exports = passport;
const router = require("express").Router();
const controller = require("../../controllers/controller.js");
const passport = require("../../config/passport");
router.post(
"/login",
passport.authenticate("local", { failureRedirect: "/login" }),
function(req, res) {
console.log(`req body -${req.body}`);
res.json({
message: "user authenticated",
});
}
);
router.post("/saveRecipe", (req, res) => {
console.log(req.user)
if (req.isAuthenticated()) {
controller.saveRecipe;
} else {
res.json({ message: "user not signed in" });
}
});
module.exports = router;
The problem is in your router.post('login'). Try changing it to something like this:
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/profile',
failureRedirect: '/login/failed'})
)
This will correctly set the req.user in your next requests!

integrating authentication to my app using Auth0 and node.js

i'm developing application on cloud9 enviroment. using:
node 4.43
express 4.13.4
i have integrated my Demo Auth0 account into my on-dev application.
i am able to login (being redirected to the first page of my app), but when i'm printing req.isAuthenticated() i'm getting false. also req.user is undefined.
i have followed the quick start of auth0 for node.js.
i'm attaching the three files that are mainly invovled:
app.js:
var express = require('express'),
app = express(),
BodyParser = require("body-parser"),
mongoose = require("mongoose"),
student = require ("./models/student"),
students_class = require("./models/class"),
// =============
// auth0
// =============
passport = require('passport'),
strategy = require('./models/setup-passport'),
cookieParser = require('cookie-parser'),
session = require('express-session');
app.use(cookieParser());
app.use(session({ secret: 'FpvAOOuCcSBLL3AlGxwpNh5x-U46YCRoyBKWJhTPnee2UELMd_gjdbKcbhpIHZoA', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
app.get('/login',passport.authenticate('auth0', { failureRedirect: '/url-if-something-fails' }),
function(req, res) {
res.send(req.user);
if (!req.user) {
throw new Error('user null');
}
res.redirect("/", {username: req.user});
});
mongoose.connect("mongodb://localhost/myapp");
// ============================
// routes
// ============================
var classRoutes = require("./routes/class"),
indexRoutes = require("./routes/index"),
studentRoutes = require("./routes/student"),
assocRroutes = require ("./routes/assoc");
// ============================================
// configuring the app
// ============================================
app.set("view engine", "ejs");
app.use(express.static ("public"));
app.use(BodyParser.urlencoded({extended: true}));
app.use(classRoutes);
app.use (indexRoutes);
app.use(studentRoutes);
app.use(assocRroutes);
app.listen(process.env.PORT, process.env.IP, function() {
console.log('Attendance Server is Running ....');
});
setup-passport.js
var passport = require('passport');
var Auth0Strategy = require('passport-auth0');
var strategy = new Auth0Strategy({
domain: 'me.auth0.com',
clientID: 'my-client-id',
clientSecret: 'FpvAOOuCcSBLL3AlGxwpNh5x-U46YCRoyBKWJhTPnee2UELMd_gjdbKcbhpIHZoA',
callbackURL: '/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;
index.js (the actual fisrt page where i want to re-direct after successful login:
var express = require("express");
var passport = require('passport');
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn();
var router = express.Router();
var student = require ("../models/student");
//INDEX
router.get("/callback", function(req, res) {
student.find({}, function(err, student) {
console.log(req.isAuthenticated())
if (err) {
console.log(err);
} else {
res.render("home/index.ejs", {
students: student
});
}
});
});
module.exports = router;
any suggestion what could go wrong?
also wierd for me that on app.js, the guide is initializing the variable strategy but actually never seem to use it.
BUMP
You are not calling passport.authenticate() in the /callback endpoint. See for comparison: https://auth0.com/docs/quickstart/webapp/nodejs#5-add-auth0-callback-handler
// Auth0 callback handler
app.get('/callback',
passport.authenticate('auth0', { failureRedirect: '/url-if-something-fails' }),
function(req, res) {
if (!req.user) {
throw new Error('user null');
}
res.redirect("/user");
});

Resources