Deploying express and node js app to google app engine - node.js

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

Related

Problem with the Routing Path Express, NodeJS

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

Passport-google-oauth not working with mongodb atlas but working fine with local mongodb

I am making google sign in authentication with passport-google-oauth20 strategy. So, i have created a separate file to setup the google strategy for passport and in this file I am saving the user profile details in mongodb. When I am using the local mongodb everything is working fine. But, when I use mongodb atlas. it connects to the uri but when i click on sign in via google , it takes forever to redirect.
passport-setup.js
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const keys = require('./keys');
const User = require('../models/user-model');
console.log('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(null, user);
});
});
passport.use(
new GoogleStrategy({
// options for google strategy
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret,
callbackURL: '/auth/google/redirect'
}, (accessToken, refreshToken, profile, done) => {
// check if user already exists in our own db
User.findOne({googleId: profile.id}).then((currentUser) => {
console.log('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
if(currentUser){
// already have this user
console.log(profile.emails[0].value);
// console.log('user is: ', email);
done(null, currentUser);
} else {
// if not, create user in our db
new User({
googleId: profile.id,
username: profile.displayName,
gmail: profile.emails[0].value
}).save().then((newUser) => {
console.log('created new user: ', newUser);
done(null, newUser);
});
}
});
})
);
I am connecting mongodb in app.js and I have saved the URI in a separate file. In app.js, i have created auth routes for which there is a separate file from which i am attaching the necessary information.
auth-routes.js
router.get('/google', passport.authenticate('google', {
scope:
['https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/userinfo.email']
}));
// callback route for google to redirect to
// hand control to passport to use code to grab profile info
router.get('/google/redirect', passport.authenticate('google'), (req, res) => {
// res.send(req.user);
res.redirect('/profile');
});
app.js
const express = require('express');
const cookieSession = require('cookie-session');
const passport = require('passport');
const authRoutes = require('./routes/auth-routes');
const profileRoutes = require('./routes/profile-routes');
const passportSetup = require('./config/passport-setup');
const mongoose = require('mongoose');
const keys = require('./config/keys');
const app = express();
// set view engine
app.set('view engine', 'ejs');
// set up session cookies
app.use(cookieSession({
maxAge: 24 * 60 * 60 * 1000,
keys: [keys.session.cookieKey]
}));
// initialize passport
app.use(passport.initialize());
app.use(passport.session());
// connect to mongodb
mongoose.connect(keys.mongodb.dbURI, () => {
console.log('connected to mongodb');
});
// set up routes
app.use('/auth', authRoutes);
app.use('/profile', profileRoutes);
// create home route
app.get('/', (req, res) => {
res.render('home');
});
app.listen(3000, () => {
console.log('app now listening for requests on port 3000');
});

after successfull redirect from authorize page of twitter login not able to get user detials using passport.js

I am using passport.js for twitter social login. In my Web app after a successful login, it will populate the tweets accordingly and will reply to the tweets as per the loggedIn user credentials. Currently, I am able to successfully redirect after login, but not able to fetch the user details which passport gives after successful login i.e (username, email). As a result, I am not able to go further.
Below is the node server.js file.
Kindly help me with this issue.
server.js
const express = require('express');
const TwitterStrategy = require('passport-twitter').Strategy;
const twitConfig = require('./config/auth').twitterAuth;
const passport = require('passport');
const path = require('path');
const bodyParser = require('body-parser');
// Create a new Express application.
const app = express();
// require('./routes/')(app);
// Use application-level middleware for common functionality, including
// logging, parsing, and session handling.
app.use(require('cookie-parser')());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }));
//initialize passport strategy for twitter login.
passport.use(new TwitterStrategy({
consumerKey: twitConfig.consumer_key,
consumerSecret: twitConfig.consumer_secret,
callbackURL: twitConfig.callbackURL
}, (token, tokenSecret, profile, callback) => {
console.log('arguments in passport.use>>>>>>>>>>>>>>>', arguments);
console.log('profile in twitterStrategy>>>>>>>>>>>>>', profile,callback);
process.nextTick(() => {
if (err) {
console.log('err in passport TwitterStrategy>>>>>>>>>>>',err)
return callback()
} else {
console.log('inside the else condition no error found>>>>>>>>>>',profile)
}
})
return callback(null, profile);
}));
const twit = require('twit');
const T = new twit(twitConfig);
passport.serializeUser(function(user, callback) {
console.log('arguments in serializeUser passport.use>>>>>>>>>>>>>>>', arguments);
callback(null, user);
})
passport.deserializeUser(function (obj, callback) {
console.log('arguments in deserializeUser passport.use>>>>>>>>>>>>>>>', arguments);
callback(null, obj);
});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
//load the routes
app.use(require('./routes/twitterApi'));
app.use(require('./routes/token'));
app.use(passport.initialize());
app.use(passport.session());
app.get('/', function (req, res) {
console.log('user in />>>>>>>>>>>>>>', req);
res.render('index', {user: req.user})
})
app.get('/twitter/login', passport.authenticate('twitter'), (req, res) => {
console.log('consoe.log req>>>>>>>>>>>>>', req);
console.log('res >>>>>>>>>>',res);
});
app.get('/twitter/return', passport.authenticate('twitter', { failureRedirect: '/' }), function (req, res) {
console.log('inside the return failure');
res.redirect('http://localhost:8080/');
})
// listen for requests :)
const listener = app.listen(process.env.PORT || 9000, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
Here is an async/await example of how to perform a database operation on the User, after authenticating with passport strategy.
passport.use(new TwitterStrategy({
consumerKey: twitConfig.consumer_key,
consumerSecret: twitConfig.consumer_secret,
callbackURL: twitConfig.callbackURL
}, async (token, tokenSecret, profile, callback) => {
try {
// Perform some kind of db lookup/create with profile object
const user = await User.findOne({
where: {
email: profile.email
}
})
// now your user object will be passed to your callbackURL
return callback(null, user)
}
catch(error) {
return callback(error, error.message);
}

Passport does not call the authentication strategy

today I was trying to get a passport authentication working. The email and password is static now but I will change that later. I have a lot of debug messages but only the ones outside of the Strategy. No errors or warnings regarding passport are displayed.
I have already tried to use different body parser modes (extented = true, extented = false).
Strategy
const LocalStrategy = require('passport-local').Strategy;
module.exports = function(passport) {
passport.use(
new LocalStrategy((email, password, done) => {
console.log('Authentication started');
var user = null;
if(email == 'test#mytest.com') {
if(password == 'test') {
user = {
email
}
console.log('Authenticated')
return done(null, user);
}
}
console.log('Error')
return done(null, user, {message: 'EMail or Password was wrong'});
})
);
passport.serializeUser(function(user, done) {
done(null, user.email);
});
passport.deserializeUser(function(id, done) {
done(err, user);
});
};
app.js (contains only important parts)
const express = require('express');
const expressSession = require('express-session')
const bodyParser = require('body-parser');
const expressLayouts = require('express-ejs-layouts');
const app = express();
const https = require('https');
const http = require('http');
app.use(expressSession({ secret: 'secret' }));
// Body Parser
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
// Passport
const passport = require('passport');
require('./config/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
// View Engine
app.set('view engine', 'ejs');
app.use(expressLayouts);
app.get('/applications', (req,res) => {
res.render('applications', {
user: req.user
});
});
app.post('/applications', (req, res, next) => {
console.log(req.body);
passport.authenticate('local', {
successRedirect: '/applications',
failureRedirect: '/',
failureFlash: false
})(req, res, next);
});
https.createServer(httpsOptions, app)
.listen(7443, () => {
console.log('HTTPS Server started on Port 7443')
});
http.createServer(app)
.listen(7080, () => {
console.log('HTTP Server started on Port 7080')
});
Make sure you are using the proper fields in your POST request. I noticed that in your strategy, you use the variables email and password. While your variable names aren't important, the fields you send in your POST request are. By default, passport-local uses the POST fields username and password. If one of these fields aren't present, the authentication will fail. You can change this to use email instead like so:
passport.use(
new LocalStrategy({
usernameField: 'email'
}, (email, password, done) => {
console.log('Authentication started');
// Your authentication strategy
})
);
Assuming you have the right POST fields, in order to use req.user in requests, you must have properly set up your passport.deserializeUser function. Testing your code, the authentication strategy is working fine for me, however I receive a reference error upon deserializeUser.

How to provide SP metadata to TestShib IdP using passport-saml?

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.

Resources