283 строки
8.4 KiB
JavaScript
283 строки
8.4 KiB
JavaScript
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const express = require('express');
|
|
const passport = require('passport');
|
|
const passportAzureAD = require('passport-azure-ad');
|
|
|
|
const OIDCStrategy = passportAzureAD.OIDCStrategy;
|
|
|
|
let authEnabled = false;
|
|
let configAuth = require('../config/auth.basic');
|
|
let configSetup = null;
|
|
let redirectPath = null;
|
|
|
|
if (fs.existsSync(path.join(__dirname, '..', 'config', 'setup.private.json'))) {
|
|
configSetup = require('../config/setup.private.json');
|
|
authEnabled = configSetup.enableAuthentication;
|
|
}
|
|
|
|
// array to hold logged in users
|
|
let users = [];
|
|
function findByEmail(email, fn) {
|
|
for (var i = 0, len = users.length; i < len; i++) {
|
|
let user = users[i];
|
|
|
|
console.info(`Current logged in user: ${user}`);
|
|
if (user.email === email) {
|
|
return fn(null, user);
|
|
}
|
|
}
|
|
return fn(null, null);
|
|
};
|
|
|
|
/**
|
|
* Initialize passport variable once authentication is enabled
|
|
*/
|
|
function initializePassport() {
|
|
// Passport session setup. (Section 2)
|
|
|
|
// 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.email);
|
|
});
|
|
|
|
passport.deserializeUser(function(id, done) {
|
|
findByEmail(id, function (err, user) {
|
|
done(err, user);
|
|
});
|
|
});
|
|
|
|
// Use the OIDCStrategy within Passport. (Section 2)
|
|
//
|
|
// Strategies in passport require a `validate` function, which accept
|
|
// credentials (in this case, an OpenID identifier), and invoke a callback
|
|
// with a user object.
|
|
passport.use(new OIDCStrategy({
|
|
redirectUrl: configSetup.redirectUrl,
|
|
allowHttpForRedirectUrl: configSetup.allowHttp,
|
|
realm: configAuth.realm,
|
|
clientID: configSetup.clientID,
|
|
clientSecret: configSetup.clientSecret,
|
|
oidcIssuer: configAuth.issuer,
|
|
identityMetadata: configAuth.identityMetadata,
|
|
skipUserProfile: configAuth.skipUserProfile,
|
|
responseType: configAuth.responseType,
|
|
responseMode: configAuth.responseMode,
|
|
validateIssuer: configAuth.validateIssuer,
|
|
issuer: configSetup.issuer
|
|
},
|
|
function(iss, sub, profile, accessToken, refreshToken, done) {
|
|
console.log(`passport arguments: ${arguments}`);
|
|
|
|
profile.email = profile.email || profile.upn;
|
|
|
|
if (!profile.email) {
|
|
return done(new Error("No email found"), null);
|
|
}
|
|
|
|
// asynchronous verification, for effect...
|
|
process.nextTick(function () {
|
|
findByEmail(profile.email, function(err, user) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
if (!user) {
|
|
// "Auto-registration"
|
|
users.push(profile);
|
|
return done(null, profile);
|
|
}
|
|
return done(null, user);
|
|
});
|
|
});
|
|
}
|
|
));
|
|
}
|
|
if (authEnabled) { initializePassport(); }
|
|
|
|
const router = new express.Router();
|
|
|
|
/**
|
|
* A path to enable initializing authentication configuration and add
|
|
* authentication to middleware pipeline
|
|
*/
|
|
router.get('/init', (req, res) => {
|
|
|
|
let configSetupContent = fs.readFileSync(path.join(__dirname, '..', 'config', 'setup.private.json'), 'utf8');
|
|
configSetup = JSON.parse(configSetupContent);
|
|
authEnabled = configSetup.enableAuthentication;
|
|
|
|
if (authEnabled) {
|
|
initializePassport();
|
|
addAuthRoutes();
|
|
}
|
|
|
|
res.json({ success: true });
|
|
});
|
|
|
|
/**
|
|
* Returning logged in account information
|
|
*/
|
|
router.get('/account', (req, res) => {
|
|
if (!authEnabled) {
|
|
return res.json({ account: null });
|
|
}
|
|
|
|
if (req.user) {
|
|
return res.json({ account: req.user });
|
|
}
|
|
|
|
if (req.query.force) {
|
|
return res.json({ account: null });
|
|
}
|
|
|
|
return setTimeout(() => {
|
|
return res.redirect(req.originalUrl + '?force=1');
|
|
}, 7000);
|
|
});
|
|
|
|
/**
|
|
* Dynamically adding authentication routes once passport is initialized
|
|
*/
|
|
function addAuthRoutes() {
|
|
|
|
router.get('/login',
|
|
(req, res, next) => {
|
|
console.log('aaa');
|
|
return next();
|
|
},
|
|
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
|
function(req, res) {
|
|
console.info('Login was called in the Sample');
|
|
res.redirect('/');
|
|
});
|
|
|
|
// POST /auth/openid
|
|
// Use passport.authenticate() as route middleware to authenticate the
|
|
// request. The first step in OpenID authentication will involve redirecting
|
|
// the user to their OpenID provider. After authenticating, the OpenID
|
|
// provider will redirect the user back to this application at
|
|
// /auth/openid/return
|
|
router.get('/openid',
|
|
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
|
function(req, res) {
|
|
console.info('Authentication was called in the Sample');
|
|
res.redirect('/');
|
|
});
|
|
|
|
// GET /auth/openid/return
|
|
// Use passport.authenticate() as route middleware to authenticate the
|
|
// request. If authentication fails, the user will be redirected back to the
|
|
// login page. Otherwise, the primary route function function will be called,
|
|
// which, in this example, will redirect the user to the home page.
|
|
router.get('/openid/return',
|
|
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
|
function(req, res) {
|
|
console.info('We received a return from AzureAD.');
|
|
res.redirect(redirectPath || '/');
|
|
redirectPath = null;
|
|
});
|
|
|
|
// GET /auth/openid/return
|
|
// Use passport.authenticate() as route middleware to authenticate the
|
|
// request. If authentication fails, the user will be redirected back to the
|
|
// login page. Otherwise, the primary route function function will be called,
|
|
// which, in this example, will redirect the user to the home page.
|
|
router.post('/openid/return',
|
|
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
|
function(req, res) {
|
|
console.info('We received a return from AzureAD.');
|
|
res.redirect(redirectPath || '/');
|
|
redirectPath = null;
|
|
});
|
|
|
|
router.get('/logout', function(req, res){
|
|
req.logout();
|
|
res.redirect('/');
|
|
});
|
|
}
|
|
if (authEnabled) {
|
|
console.log(`Registering auth routes`);
|
|
addAuthRoutes();
|
|
}
|
|
|
|
/**
|
|
* Adding all authentication middleware on process start.
|
|
* Some of the middlewares will only be activated once authentication is enabled.
|
|
* Including:
|
|
* Initialize passport variables
|
|
* call passport initalize in middleware
|
|
* call passport session in middleware
|
|
* call request authenticator
|
|
*/
|
|
let authenticationMiddleware = function (...excludePaths) {
|
|
|
|
let passportInitialize = null;
|
|
let passportSession = null;
|
|
function basicAuthCheck (req, res, next) {
|
|
|
|
if (!authEnabled) { return next(); }
|
|
|
|
// Only initialize passport middleware once authentication is enabled (passport is set)
|
|
// And middleware hasn't been initialized yet
|
|
if (authEnabled && passport && !passportSession) {
|
|
passportInitialize = passport.initialize();
|
|
passportSession = passport.session();
|
|
}
|
|
|
|
return next();
|
|
};
|
|
|
|
// Call passport initialize middleware only when it is available
|
|
function callPassportInitialize(req, res, next) {
|
|
if (!passportInitialize) { return next(); }
|
|
|
|
return passportInitialize.call(passportInitialize, req, res, next);
|
|
}
|
|
|
|
// Call passport session middleware only when it is available
|
|
function callPassportSession(req, res, next) {
|
|
if (!passportSession) { return next(); }
|
|
|
|
return passportSession.call(passportSession, req, res, next);
|
|
}
|
|
|
|
// Once authentication is enabled, ensure it is activated on all requests not under '/auth'
|
|
function ensureAuthenticated(req, res, next) {
|
|
if (!authEnabled || req.isAuthenticated()) { return next(); }
|
|
redirectPath = req.path;
|
|
res.redirect('/auth/login?redirect=' + encodeURIComponent(req.path));
|
|
}
|
|
|
|
// Returning an array of all middlewares to be called sequentially
|
|
return [
|
|
basicAuthCheck,
|
|
callPassportInitialize,
|
|
callPassportSession,
|
|
exclude(excludePaths, ensureAuthenticated)
|
|
]
|
|
};
|
|
|
|
function exclude(paths, middleware) {
|
|
return function(req, res, next) {
|
|
|
|
// Exclude a string from an array
|
|
if (Array.isArray(paths)) {
|
|
for (let pathIdx in paths) {
|
|
if (req.path.startsWith(paths[pathIdx])) { return next(); }
|
|
}
|
|
}
|
|
|
|
return middleware(req, res, next);
|
|
};
|
|
};
|
|
|
|
module.exports = {
|
|
|
|
// Returning a middleware array
|
|
authenticationMiddleware,
|
|
router
|
|
} |