I have a question about deleting data in the database using Express and NodeJS as I have a form that is deleted.pug to get the ID input from the user then used that ID to find the record in the database to delete that.
I don't know why when I clicked on the "Delete Record" button in the index.pug, I get the error "Cannot GET /delete/". Therefore, I couldn't load the form for the user to input the id.
However, in the address bar, when I typed http://localhost:3000/api/delete/5, then the city with the id is 5 was deleted in the database. Can someone help me so that I can load the delete form to get the input from the user instead of letting them directly put the id in the URL? Thank you, and I appreciate it
api.js
const express = require('express');
const mysql = require('../config/db.config');
const router = express.Router();
router.get('/delete/:id', (req, res) => {
const sql = 'DELETE FROM tblCitiesImport WHERE id=?';
mysql.query(sql, req.params.id, (err) => {
if (err) throw err;
res.render('../views/deletesuccess');
});
});
module.exports = router;
delete.pug
html
head
body
form(action='/api/delete', method='POST')
p
| ID:
input(name='ID', value='', type='text')
input(value='Submit', type='submit')
index.pug
extends layout
block layout-content
div.View.WelcomeView
h1.Banner City Database Site
div.Message
div.Title
h3 IS219 Final
h1 Cities Project
span.Details Access the Cities Records Web Portal
div.NavButtons
if isAuthenticated
a(href="http://localhost:8000")
div.NavButton View Records
a(href="/user")
div.NavButton My Profile
a(href="/delete/")
div.NavButton Delete Record
a(href="/logout")
div.NavButton Logout
else
a(href="/login")
div.NavButton Log in
db structure
CREATE DATABASE citiesData;
use citiesData;
CREATE TABLE IF NOT EXISTS tblCitiesImport (
`id` int AUTO_INCREMENT,
`fldName` VARCHAR(21) CHARACTER SET utf8,
`fldLat` NUMERIC(6, 4),
`fldLong` NUMERIC(7, 4),
`fldCountry` VARCHAR(19) CHARACTER SET utf8,
`fldAbbreviation` VARCHAR(3) CHARACTER SET utf8,
`fldCapitalStatus` VARCHAR(7) CHARACTER SET utf8,
`fldPopulation` INT,
PRIMARY KEY (`id`)
);
app.js
const express = require('express');
const path = require('path');
const expressSession = require('express-session');
const passport = require('passport');
const Auth0Strategy = require('passport-auth0');
require('dotenv').config();
const open = require('open');
const bodyParser = require('body-parser');
const cors = require('cors');
const jwt = require('express-jwt');
const jwtAuthz = require('express-jwt-authz');
const jwksRsa = require('jwks-rsa');
const apiRouter = require('./routes/api');
const dbConn = require('./config/db.config');
const authRouter = require('./auth');
const citiesRoutes = require('./routes/cities.routes');
// App Variables
const app = express();
const port = process.env.PORT || '3000';
// Session Configuration
const session = {
secret: process.env.SESSION_SECRET,
cookie: {},
resave: false,
saveUninitialized: false,
};
if (app.get('env') === 'production') {
// Serve secure cookies, requires HTTPS
session.cookie.secure = true;
}
// Passport Configuration
const strategy = new Auth0Strategy(
{
domain: process.env.AUTH0_DOMAIN,
clientID: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
callbackURL: process.env.AUTH0_CALLBACK_URL,
},
(accessToken, refreshToken, extraParams, profile, done) => {
// Access tokens are used to authorize users to an API
// accessToken is the token to call the Auth0 API or a secured 3rd-party API
// extraParams.id_token has the JSON Web Token (JWT)
// profile has all the information from the user
return done(null, profile);
},
);
// App Configuration
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(express.static(path.join(__dirname, 'public')));
app.use(expressSession(session));
app.use(bodyParser.urlencoded({ extended: true }));
passport.use(strategy);
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
// Creating custom middleware with Express
app.use((req, res, next) => {
res.locals.isAuthenticated = req.isAuthenticated();
next();
});
// Authorization middleware. When used, the
// Access Token must exist and be verified against
// the Auth0 JSON Web Key Set
const checkJwt = jwt({
// Dynamically provide a signing key
// based on the kid in the header and
// the signing keys provided by the JWKS endpoint.
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: 'https://YOURSDOMAIN.us.auth0.com/.well-known/jwks.json'
}),
// Validate the audience and the issuer.
audience: 'http://localhost:3000/api/v1/cities',
issuer: ['https://YOURSDOMAIN.us.auth0.com/'],
algorithms: ['RS256'],
});
// Router Mounting
app.use('/', authRouter);
app.use('/api', apiRouter);
// Routes Definitions
const secured = (req, res, next) => {
if (req.user) {
return next();
}
req.session.returnTo = req.originalUrl;
res.redirect('/login');
};
app.get('/', (req, res) => {
res.render('index', { title: 'Home' });
});
app.get('/user', secured, (req, res, next) => {
const { _raw, _json, ...userProfile } = req.user;
res.render('user', {
title: 'Profile',
userProfile: userProfile,
});
});
// This route is not needed authentication
app.get('/api/public', (req, res) => {
res.json({
message: 'You are in the public endpoint! Authentication is not needed to see this.',
});
});
// This route is needed authentication
app.get('/api/private', checkJwt, (req, res) => {
res.json({
message: 'You are in the private endpoint! Authentication is needed to see this.',
});
});
// using as middleware
app.use('/api/v1/cities', citiesRoutes);
app.set('port', process.env.PORT || 8000);
app.listen(port, () => {
console.log(`Listening to requests on http://localhost:${port}`);
});
Related
I can't get the logout function to work correctly and remove the session from the mongodb.
I'm able to store the session. logout was initially req.session.destroy(); but that kept returning an error. (destroy undefined)
I'm pretty new to nodejs/javascript, I'm trying to learn my apologies in advance for any ignorance!
Any help would be appreciated!!
app.js
const express = require('express'), url = require('url');
const path = require('path');
const dotenv = require('dotenv');
const connectToDatabase = require('./src/db/mongoose');
const helmet = require('helmet');
const router = require('./src/routes/routes');
const openApiDocumentation = require('./src/swagger/openApiDocumentation');
const swaggerUi = require('swagger-ui-express');
const cors = require('cors');
const bodyParser = require("body-parser");
var app = express();
const session = require('express-session');
const MongoStore = require('connect-mongo');
const port = process.env.PORT || 3000;
dotenv.config();
connectToDatabase(); //This removed to simplify the connection
app.use(express.json());
app.use(express.static('src/css'));
app.use(express.static('src/js'));
app.use(express.static('scripts'));
app.use(express.static('controllers'));
app.use(express.static(__dirname + '/views'));
app.use(express.static("views"));
app.use('/', express.static(path.join(__dirname, 'views'),{extensions:['html']}));
app.use('/api', swaggerUi.serve, swaggerUi.setup(openApiDocumentation));
app.use(function(req, res, next){
res.locals.user = req.user;
next();
});
app.use("/v1/login", session({
name: 'auth',
secret: 'mykey',
httpOnly: true,
secure: true,
maxAge: 1000 * 60 * 60 * 7,
resave: false,
saveUninitialized: true,
store: MongoStore.create({
mongoUrl: '<redacted>'
})
}));
app.get('/name', (req, res) => {
let name;
if (!req.session) {
return res.status(404).send();
}
name = req.session.user.name;
return res.status(200).send({name});
})
app.use(function (req, res, next) {
res.locals.session = req.session;
next();
});
router.get("/vl/login", function(req, res) {
if (
typeof reqsession.user != "object" || (
typeof req.session.user.name == "undefined" &&
typeof req.session.user.password == "undefined")
) {
res.render("pages/index");
}
res.redirect("Login");
});
app.use(function(req, res, next) {
console.log('%s %s', req.method, req.url);
next();
});
app.engine('.html', require('ejs').__express);
app.set('view engine', 'ejs');
// index page
app.get('/', function(req, res) {
res.render('pages/index');
});
// about page
app.get('/about', function(req, res) {
res.render('pages/about');
});
app.use(function(req, res, next){
res.locals.user = req.user;
next();
});
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(helmet());
app.use('/v1', router);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.listen(port, () => console.log(`Server is running on Port: ${port}`));
userController.js
login: async (req, res) => {
const { userName, password } = req.body
const user = await userModel.findOne({ userName }).lean()
if (!user) {
return res.json({ status: 'error', error: 'Invalid username/password' })
}
if (await bcrypt.compare(password, user.password)) {
// the username, password combination is successful
const token = jwt.sign(
{
id: userModel._id,
userName: userModel.userName
},
JWT_SECRET
)
const { name } = req.body;
req.session.user = {
name,
isLoggedIn: true
}
try {
await req.session.save();
} catch (err) {
console.error('Error saving to session storage: ', err);
return next(new Error('Error creating user'));
}
return res.json({ status: 'ok', data: token })
}
res.json({ status: 'error', error: 'Invalid username/password' })
},
logout: async (req, res, next) => {
try {
await await req.session == null;
} catch (err) {
console.error('Error logging out:', err);
return next(new Error('Error logging out'));
}
return res.json({ status: 'ok'})
},
routes.js
const express = require('express');
const router = express.Router();
const newUser = require('../controllers/userController');
const company = require('../controllers/companyController');
const upload = require('../controllers/upload');
const auth = require('../middleware/auth');
/* GET response for '/'/*
router.get('/', (req, res) => {
res.redirect('/');
}) */
//generate token
router.get('/token', newUser.generateToken);
//login
router.post('/login', newUser.login);
//logout
router.post('/logout', newUser.logout);
//create service user
router.post('/addServiceUser', newUser.addServiceUser);
//Add user
router.post('/users', newUser.addUser);
//remove user
router.delete('/users/:id',auth, newUser.removeUser);
//Get All users
router.get('/users',auth, newUser.getAllUsers);
//Get User
router.get('/user/:id',auth, newUser.getUser);
//Get first name
router.get('/users/:id/firstName',auth, newUser.getUserFirstName);
//Set first name
router.put('/users/:id/firstName',auth, newUser.setUserFirstName);
//Get last name
router.get('/users/:id/lastName',auth, newUser.getUserLastName);
//Set last name
router.put('/users/:id/lastName',auth, newUser.setUserLastName);
//Get user email
router.get('/users/:id/email',auth, newUser.getUserEmail);
//set user email
router.put('/users/:id/email',auth, newUser.setUserEmail);
//Get user phone
router.get('/users/:id/phone',auth, newUser.getUserPhone);
//set user phone
router.put('/users/:id/phone',auth, newUser.setUserPhone);
//get user age
router.get('/users/:id/age',auth, newUser.getUserAge);
//set user age
router.put('/users/:id/age',auth, newUser.setUserAge);
//set user status
router.put('/users/:id/status',auth, newUser.setUserStatus);
//get user status
router.get('/users/:id/status',auth, newUser.getUserStatus);
//set user level
router.put('/users/:id/level',auth, newUser.setUserLevel);
//get user level
router.get('/users/:id/level',auth, newUser.getUserLevel);
//get user gender
router.get('/users/:id/gender',auth, newUser.getUserGender);
//set user gender
router.put('/users/:id/gender',auth, newUser.setUserGender);
//set user address
router.put('/users/:id/address',auth, newUser.setUserAddress);
//get user address
router.get('/users/:id/address',auth, newUser.getUserAddress);
//get active users
router.get('/users/status/active',auth, newUser.getActiveUsers);
//get inactive users
router.get('/users/status/inactive',auth, newUser.getInActiveUsers);
//get non admin users
router.get('/users/level/intern',auth, newUser.getInternUsers);
//get mentor users
router.get('/users/level/mentor',auth, newUser.getMentorUsers);
//Get Avatar
router.get('/users/:id/avatar',auth, newUser.getUserAvatar);
//Set Avatar
router.put('/users/:id/avatar',auth, upload.single('avatar'), newUser.setUserAvatar);
//Delete Avatar
router.delete('/users/:id/avatar',auth, newUser.removeUserAvatar);
//Add Companies
router.post('/companies/:id/team', auth, company.setUserTeamName);
module.exports= router;
It looks like in your logout function, the way your attempting to "logout" the user is setting req.session to null.
As you've figured out this doesn't act how you've expected. The correct way to logout a user would be calling req.session.destroy() usually accompanied by a redirect back to the login page:
return res.redirect("/login");
This will completely remove or, destroy the session and log the user out.
Hello i am trying to understand and apply "passport-azure-ad" function which i found on https://learn.microsoft.com/en-us/graph/tutorials/node?tutorial-step=1 to my own web applicaton.
Instead of "hbs"template engine what they use in the tutorial i use "jade". I registered the application in Azure. When running the web-application I and noticed that the signin function is working (redirected to the login page of Microsoft to enter my credentials). But when i leave the credentials page it returns to the http://localhost:3000/error instead of **http://localhost:3000/succes ** what i expected.
Can you please help me?
Below i put the javascript files:
app.js
require('dotenv').config();
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var session = require('express-session');
var flash = require('connect-flash');
var passport = require('passport');
var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
var graph = require('./graph');
// Configure simple-oauth2
const oauth2 = require('simple-oauth2').create({
client: {
id: process.env.OAUTH_APP_ID,
secret: process.env.OAUTH_APP_PASSWORD
},
auth: {
tokenHost: process.env.OAUTH_AUTHORITY,
authorizePath: process.env.OAUTH_AUTHORIZE_ENDPOINT,
tokenPath: process.env.OAUTH_TOKEN_ENDPOINT
}
});
// Configure passport
// In-memory storage of logged-in users
// For demo purposes only, production apps should store
// this in a reliable storage
var users = {};
// Passport calls serializeUser and deserializeUser to
// manage users
passport.serializeUser(function(user, done) {
// Use the OID property of the user as a key
users[user.profile.oid] = user;
done (null, user.profile.oid);
});
passport.deserializeUser(function(id, done) {
done(null, users[id]);
});
// 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) {
if (!profile.oid) {
return done(new Error("No OID found in user profile."), null);
}
try{
const user = await graph.getUserDetails(accessToken);
if (user) {
// Add properties to profile
profile['email'] = user.mail ? user.mail : user.userPrincipalName;
console.info(users)
}
} catch (err) {
done(err, null);
}
// Create a simple-oauth2 token from raw tokens
let oauthToken = oauth2.accessToken.create(params);
// Save the profile and tokens in user storage
users[profile.oid] = { profile, oauthToken };
return done(null, users[profile.oid]);
}
// Configure OIDC strategy
passport.use(new OIDCStrategy(
{
identityMetadata: `${process.env.OAUTH_AUTHORITY}${process.env.OAUTH_ID_METADATA}`,
clientID: process.env.OAUTH_APP_ID,
responseType: 'code id_token',
responseMode: 'form_post',
redirectUrl: process.env.OAUTH_REDIRECT_URI,
allowHttpForRedirectUrl: true,
clientSecret: process.env.OAUTH_APP_PASSWORD,
validateIssuer: false,
passReqToCallback: false,
scope: process.env.OAUTH_SCOPES.split(' ')
},
signInComplete
));
//setup routes
//require('./routes/routes')(app, passport);
var indexRouter = require('./routes/index');
var authRouter = require('./routes/auth');
var app = express();
app.use(session({
secret: 'your_secret_value_here',
resave: false,
saveUninitialized: false,
unset: 'destroy'
}));
// Flash middleware
app.use(flash());
// Set up local vars for template layout
app.use(function(req, res, next) {
// Read any flashed errors and save
// in the response locals
res.locals.error = req.flash('error_msg');
// Check for simple error string and
// convert to layout's expected format
var errs = req.flash('error');
for (var i in errs){
res.locals.error.push({message: 'An error occurred', debug: errs[i]});
}
next();
});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Initialize passport
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next) {
// Set the authenticated user in the
// template locals
if (req.user) {
res.locals.user = req.user.profile;
}
next();
});
app.use('/', indexRouter);
app.use('/auth', authRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
auth.js
var express = require('express');
var passport = require('passport');
var router = express.Router();
/* GET auth callback. */
router.get('/signin',
function (req, res, next) {
passport.authenticate('azuread-openidconnect',
{
response: res,
prompt: 'login',
failureRedirect: '/',
failureFlash: true,
successRedirect: '/'
}
)(req,res,next);
}
);
router.post('/callback',
function(req, res, next) {
passport.authenticate('azuread-openidconnect',
{
response: res,
failureRedirect: '/Error',
failureFlash: true,
successRedirect: '/Succes'
}
)(req,res,next);
}
);
graph.js
var graph = require('#microsoft/microsoft-graph-client');
require('isomorphic-fetch');
module.exports = {
getUserDetails: async function(accessToken) {
const client = getAuthenticatedClient(accessToken);
const user = await client.api('/me').get();
return user;
}
};
function getAuthenticatedClient(accessToken) {
// Initialize Graph client
const client = graph.Client.init({
// Use the provided access token to authenticate
// requests
authProvider: (done) => {
done(null, accessToken);
}
});
return client;
}
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!
I have a node.js script, using passport-saml, that simulates an SP. My goal is to connect it to this TestShib IdP but I get the following error: SAML 2 SSO profile is not configured for relying party.
Based on what I read here, I know that I need to provide the SP metadata, but I do not know how. I know that passport-saml has the function: generateServiceProviderMetadata(decryptionCert) and I while I have the certificates required, I do not know how to make it all work.
Also, I want to avoid having to register my SP if that is possible.
Here is my script:
const https = require('https');
const fs = require('fs');
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const passport = require('passport');
const saml = require('passport-saml');
/*
---------------------------------------------------------------------------------------------------
-- certificates
---------------------------------------------------------------------------------------------------
*/
// for https server
const https_cert = fs.readFileSync('certificate.pem', 'utf-8');
const https_pvk = fs.readFileSync('privatekey.pem', 'utf-8');
// from idp's metadata
const idp_cert_1 = 'MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==';
const idp_cert_2 = 'MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==';
/*
---------------------------------------------------------------------------------------------------
-- passport-saml setup
---------------------------------------------------------------------------------------------------
*/
const saml_strategy = new saml.Strategy(
{
'callbackUrl': 'https://localhost:44300/login/callback',
'entryPoint': 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO',
'issuer': 'https://localhost:44300',
'decryptionPvk': https_pvk,
'cert': [idp_cert_1, idp_cert_2]
},
function (profile, done)
{
console.log('passport.use() profile: %s \n', JSON.stringify(profile));
return done(
null,
{
'nameIDFormat': profile.nameIDFormat,
'nameID': profile.nameID
}
);
}
);
passport.serializeUser(function (user, done) {
console.log('passport.serializeUser() user: %s \n', JSON.stringify(user));
done(null, user);
});
passport.deserializeUser(function (user, done) {
console.log('passport.deserializeUser() user: %s \n', JSON.stringify(user));
done(null, user);
});
passport.use(saml_strategy);
/*
---------------------------------------------------------------------------------------------------
-- express setup
---------------------------------------------------------------------------------------------------
*/
const app = express();
// configure view engine to render EJS templates
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
// additional settings for logging and parsing
app.use(morgan('dev'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({'extended': true}));
app.use(session({ 'secret': 'this-is-secret', 'resave': false, 'saveUninitialized': false }));
// initialize Passport and restore authentication state, if any, from the session
app.use(passport.initialize());
app.use(passport.session());
/*
---------------------------------------------------------------------------------------------------
-- routes
---------------------------------------------------------------------------------------------------
*/
app.get(
'/',
function (req, res) {
if (req.isAuthenticated()) {
console.log('GET [/] user authenticated! req.user: %s \n', JSON.stringify(req.user));
res.render('home', { 'user': req.user });
} else {
console.log('GET [/] user not authenticated! \n');
res.render('home', { 'user': null });
}
}
);
app.get(
'/login',
passport.authenticate('saml', { 'successRedirect': '/', 'failureRedirect': '/login' })
);
app.post(
'/login/callback',
passport.authenticate('saml', { 'failureRedirect': '/', 'failureFlash': true }),
function(req, res) {
console.log('POST [/login] \n');
res.redirect('/');
}
);
app.get(
'/profile',
function(req, res){
if (req.isAuthenticated()) {
console.log('GET [/profile] user authenticated! req.user: %s \n', JSON.stringify(req.user));
res.render('profile', { 'user': req.user });
} else {
console.log('GET [/profile] user not authenticated! \n');
res.redirect('/login');
}
}
);
app.get(
'/logout',
function(req, res) {
console.log('GET [/logout] \n');
passport._strategy('saml').logout(
req,
function(err, requestUrl) {
req.logout();
res.redirect('/');
}
);
}
);
/*
---------------------------------------------------------------------------------------------------
-- start https server
---------------------------------------------------------------------------------------------------
*/
const server = https.createServer({
'key': https_pvk,
'cert': https_cert
}, app);
server.listen(44300, function() {
console.log('Listening on https://localhost:%d', server.address().port)
});
Thank you in advance for your help and guidance!
I know that passport-saml has the function: generateServiceProviderMetadata(decryptionCert) and I while I have the certificates required, I do not know how to make it all work.
For the sake of completeness, I'll list all the steps needed. But you've already done the first 2 so you can skip them:
Create the cert/key
In your SamlStrategy, define decryptionPvk
decryptionPvk: fs.readFileSync('./credentials/key.pem', 'utf-8'),
Lastly, you can create an endpoint that exposes your metadata (this is the part you're missing)
app.get('/metadata',
function(req, res) {
const decryptionCert = fs.readFileSync('./credentials/cert.pem', 'utf-8');
res.type('application/xml');
res.send((myStrategy.generateServiceProviderMetadata(decryptionCert)));
}
);
Here's what that would look like reformatted to match the sample code you posted:
app.get(
'/metadata',
function(req, res) {
res.type('application/xml');
res.send((saml_strategy.generateServiceProviderMetadata(https_cert)));
}
);
Once that's done you should be able to access your metadata from the /metadata path.
Also, I want to avoid having to register my SP if that is possible.
I'm not a SAML expert, but you probably won't be able to avoid registration. This would only work with an IdP that was set up to trust all SPs, but that wouldn't make much sense because a key part of SAML is the two-way trust that's established between the IdP and SP.
I have tested my whole app locally and it works perfectly, but the moment I deploy it to google app engine, The entire Oauth2 process by logging in with google stops working. I do not know what the problem is.
One use case I am testing write now is to have a ejs file with a link that says login. If you click on that link it should use google oauth2 to log you in and the page that is shown is your display name (obtained from your google account) after you log in. When I refresh this site, this display name disappears signifying that it has logged out (for some reason) even though it is supposed to stay logged in. On my localhost copy, it works completely, it stays logged in and the display name stays in tact.
const express = require('express');
const Datastore = require('#google-cloud/datastore');
const bodyParser = require('body-parser');
const passport = require('passport');
const session = require('express-session');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const app = express();
const datastore = Datastore();
app.enable('trust proxy');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(require('cookie-parser')());
const projectId = 'ece6102-guestbookapp';
const GOOGLE_CLIENT_ID = "";
const GOOGLE_CLIENT_SECRET = "";
const Logging = require('#google-cloud/logging');
function getLogEntries () {
// Instantiates a client
const logging = Logging();
const options = {
pageSize: 10,
filter: 'resource.type="cloud_function"'
};
// Retrieve the latest Cloud Function log entries
// See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging
return logging.getEntries(options)
.then(([entries]) => {
console.log('Entries:');
entries.forEach((entry) => console.log(entry));
return entries;
});
}
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// Use the GoogleStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Google
// profile), and invoke a callback with a user object.
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "/auth/google/callback",
//NOTE :
//Carefull ! and avoid usage of Private IP, otherwise you will get the device_id device_name issue for Private IP during authentication
//The workaround is to set up thru the google cloud console a fully qualified domain name such as http://mydomain:3000/
//then edit your /etc/hosts local file to point on your private IP.
//Also both sign-in button + callbackURL has to be share the same url, otherwise two cookies will be created and lead to lost your session
//if you use it.
passReqToCallback : true
},
function(request, accessToken, refreshToken, profile, done) {
console.log("Calling into this");
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's Google profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Google account with a user record in your database,
// and return that user instead.
return done(null, profile);
});
}
));
function insertMessage (message, guestbook, user) {
let mess = 'An anonymous person wrote:';
if (user) {
mess = user.displayName + ' wrote:';
}
return datastore.save({
key: datastore.key(guestbook),
data: {
timestamp: new Date(),
username: mess,
message: message
}
});
}
function getMessages (guestbook) {
const query = datastore.createQuery(guestbook)
.order('timestamp', { descending: true })
.limit(10);
return datastore.runQuery(query)
.then((results) => {
return results[0];
})
.catch((error) => {
});
}
app.set('views', './views');
app.set('view engine', 'ejs');
app.use(express.static('public'));
app.use( session({
secret: 'cookie_secret',
resave: true,
saveUninitialized: true
}));
app.use( passport.initialize());
app.use( passport.session());
app.get('/', (req, res) => {
console.log(req.query);
res.render('index', {
user: req.user
});
// if ('book' in req.query) {
// getMessages(req.query['book']).then((results) => {
// res.render('index', {
// messages: results,
// guestbookname: req.query['book'],
// user: req.user
// });
// });
// } else {
// getMessages('message').then((results) => {
// res.render('index', {
// messages: results,
// guestbookname: 'message',
// user: req.user
// });
// });
// }
});
app.post('/addEntry', (req, res) => {
console.log(req.user);
insertMessage(req.body.entry, req.body.book, req.user)
.then(() => getMessages())
.then((messages) => {
res.redirect('/?book=' + req.body.book);
});
});
app.get('/auth/google', passport.authenticate('google', { scope: [
'https://www.googleapis.com/auth/plus.login']
}));
app.get('/auth/google/callback', passport.authenticate('google'), (req, res) => {
res.redirect('/');
});
app.get('/auth/logout', (req, res) => {
req.logout();
res.redirect('/');
});
const server = app.listen(8080, () => {
const host = server.address().address;
const port = server.address().port;
console.log(`Example app listening at http://${host}:${port}`);
});
This is a copy of the code I have written and some pics as to what its supposed to do:
This the the first image with the login button
This is the second image after logging in