230 lines
8.3 KiB
JavaScript
230 lines
8.3 KiB
JavaScript
const passport = require('passport');
|
|
const expressSession = require('express-session');
|
|
const config = require('./config');
|
|
|
|
// set up database for express session
|
|
const MongoStore = require('connect-mongo')(expressSession);
|
|
const mongoose = require('mongoose');
|
|
const db = require("./mongo");
|
|
|
|
// Start QuickStart here
|
|
|
|
var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
|
|
|
|
/******************************************************************************
|
|
* Set up passport in the app
|
|
******************************************************************************/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// To support persistent login sessions, Passport needs to be able to
|
|
// serialize users into and deserialize users out of the session. Typically,
|
|
// this will be as simple as storing the user ID when serializing, and finding
|
|
// the user by ID when deserializing.
|
|
//-----------------------------------------------------------------------------
|
|
passport.serializeUser(function(user, done) {
|
|
done(null, user.oid);
|
|
});
|
|
|
|
passport.deserializeUser(function(oid, done) {
|
|
_findByOid(oid, function (err, user) {
|
|
done(err, user);
|
|
});
|
|
});
|
|
|
|
var _findByOid = async function(oid, fn) {
|
|
var mongouser = await db.user.getOid(oid);
|
|
if (mongouser && mongouser.oid === oid){
|
|
return fn(null, mongouser);
|
|
} else {
|
|
return fn(null, null);
|
|
}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Use the OIDCStrategy within Passport.
|
|
//
|
|
// Strategies in passport require a `verify` function, which accepts credentials
|
|
// (in this case, the `oid` claim in id_token), and invoke a callback to find
|
|
// the corresponding user object.
|
|
//
|
|
// The following are the accepted prototypes for the `verify` function
|
|
// (1) function(iss, sub, done)
|
|
// (2) function(iss, sub, profile, done)
|
|
// (3) function(iss, sub, profile, access_token, refresh_token, done)
|
|
// (4) function(iss, sub, profile, access_token, refresh_token, params, done)
|
|
// (5) function(iss, sub, profile, jwtClaims, access_token, refresh_token, params, done)
|
|
// (6) prototype (1)-(5) with an additional `req` parameter as the first parameter
|
|
//
|
|
// To do prototype (6), passReqToCallback must be set to true in the config.
|
|
//-----------------------------------------------------------------------------
|
|
passport.use(new OIDCStrategy({
|
|
identityMetadata: config.creds.identityMetadata,
|
|
clientID: config.creds.clientID,
|
|
responseType: config.creds.responseType,
|
|
responseMode: config.creds.responseMode,
|
|
redirectUrl: config.creds.redirectUrl,
|
|
allowHttpForRedirectUrl: config.creds.allowHttpForRedirectUrl,
|
|
clientSecret: config.creds.clientSecret,
|
|
validateIssuer: config.creds.validateIssuer,
|
|
isB2C: config.creds.isB2C,
|
|
issuer: config.creds.issuer,
|
|
passReqToCallback: config.creds.passReqToCallback,
|
|
scope: config.creds.scope,
|
|
loggingLevel: config.creds.loggingLevel,
|
|
nonceLifetime: config.creds.nonceLifetime,
|
|
nonceMaxAmount: config.creds.nonceMaxAmount,
|
|
useCookieInsteadOfSession: config.creds.useCookieInsteadOfSession,
|
|
cookieEncryptionKeys: config.creds.cookieEncryptionKeys,
|
|
clockSkew: config.creds.clockSkew,
|
|
},
|
|
function(iss, sub, profile, jwtClaims, accessToken, refreshToken, params, done) {
|
|
|
|
if ( !profile.oid ) {
|
|
return done(new Error("No oid found"), null);
|
|
}
|
|
console.log("accessToken", accessToken);
|
|
console.log("iss", iss);
|
|
console.log("sub", sub);
|
|
console.log("refreshToken", refreshToken);
|
|
console.log("jwtClaims", jwtClaims);
|
|
console.log("params", params);
|
|
console.log("profile", profile);
|
|
// asynchronous verification, for effect...
|
|
process.nextTick(function () {
|
|
_findByOid(profile.oid, async function(err, user) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
if (!user) {
|
|
// "Auto-registration"
|
|
user = await db.user.add({
|
|
"oid": profile.oid,
|
|
"upn": profile.upn,
|
|
"displayName": profile.displayName
|
|
});
|
|
return done(null, user);
|
|
}
|
|
return done(null, user);
|
|
});
|
|
});
|
|
}
|
|
));
|
|
|
|
module.exports.init = function(app){
|
|
|
|
// set up session middleware
|
|
if (config.useMongoDBSessionStore) {
|
|
//mongoose.connect(config.databaseUri);
|
|
app.use(expressSession({
|
|
secret: 'secret',
|
|
cookie: {maxAge: config.mongoDBSessionMaxAge * 1000},
|
|
store: new MongoStore({
|
|
mongooseConnection: mongoose.connection,
|
|
clear_interval: config.mongoDBSessionMaxAge
|
|
})
|
|
}));
|
|
} else {
|
|
app.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false }));
|
|
}
|
|
|
|
|
|
// Initialize Passport! Also use passport.session() middleware, to support
|
|
// persistent login sessions (recommended).
|
|
app.use(passport.initialize());
|
|
app.use(passport.session());
|
|
|
|
app.get('/login',
|
|
function(req, res, next) {
|
|
passport.authenticate('azuread-openidconnect',
|
|
{
|
|
response: res, // required
|
|
resourceURL: config.resourceURL, // optional. Provide a value if you want to specify the resource.
|
|
//customState: 'my_state', // optional. Provide a value if you want to provide custom state value.
|
|
failureRedirect: '/',
|
|
session: false
|
|
}
|
|
)(req, res, next);
|
|
},
|
|
function(req, res) {
|
|
res.redirect('/');
|
|
}
|
|
);
|
|
|
|
// 'GET returnURL'
|
|
// `passport.authenticate` will try to authenticate the content returned in
|
|
// query (such as authorization code). If authentication fails, user will be
|
|
// redirected to '/' (home page); otherwise, it passes to the next middleware.
|
|
app.get('/auth/openid/return',
|
|
function(req, res, next) {
|
|
passport.authenticate('azuread-openidconnect',
|
|
{
|
|
response: res, // required
|
|
failureRedirect: '/'
|
|
}
|
|
)(req, res, next);
|
|
},
|
|
function(req, res) {
|
|
console.log('We received a return from AzureAD.');
|
|
res.redirect('/provisions');
|
|
}
|
|
);
|
|
|
|
// 'POST returnURL'
|
|
// `passport.authenticate` will try to authenticate the content returned in
|
|
// body (such as authorization code). If authentication fails, user will be
|
|
// redirected to '/' (home page); otherwise, it passes to the next middleware.
|
|
app.post('/auth/openid/return',
|
|
function(req, res, next) {
|
|
passport.authenticate('azuread-openidconnect',
|
|
{
|
|
response: res, // required
|
|
failureRedirect: '/'
|
|
}
|
|
)(req, res, next);
|
|
},
|
|
function(req, res) {
|
|
console.log('We received a return from AzureAD.');
|
|
res.redirect('/provisions');
|
|
}
|
|
);
|
|
|
|
// 'logout' route, logout from passport, and destroy the session with AAD.
|
|
app.get('/logout', function(req, res){
|
|
req.session.destroy(function(err) {
|
|
req.logOut();
|
|
res.redirect(config.destroySessionUrl);
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports.ensureAuthenticatedDoLogin = function(req, res, next) {
|
|
if ( req.isAuthenticated() ) {
|
|
return next();
|
|
}
|
|
res.redirect('/login');
|
|
};
|
|
|
|
module.exports.ensureAuthenticated = function(req, res, next) {
|
|
if ( req.isAuthenticated() ) {
|
|
return next();
|
|
}
|
|
res.status(401).send({"error": "Unauthorized"});
|
|
};
|
|
|
|
module.exports.ensureAuthenticatedAndAdmin = function(req, res, next) {
|
|
if ( req.isAuthenticated() && (req.user.role === 'admin' || req.user.role === 'superadmin') ) {
|
|
return next();
|
|
}
|
|
res.status(401).send({"error": "Unauthorized"});
|
|
};
|
|
|
|
module.exports.ensureAuthenticatedAndIsMe = function (req, res, next) {
|
|
if ( req.isAuthenticated() ) {
|
|
if ( req.user._id == req.params.userId || req.user.role === 'admin' || req.user.role === 'superadmin' ) {
|
|
return next();
|
|
} else {
|
|
return res.status(401).send("Error: Unauthorized");
|
|
}
|
|
}
|
|
return res.status(401).send("Error: Unauthorized");
|
|
}; |