Line data Source code
1 : // @ts-check
2 : const
3 1 : { defaultTo, get, includes, isString, some } = require('lodash'),
4 1 : debug = require('debug')('auth'),
5 1 : passport = require('passport'),
6 1 : cookieSession = require('cookie-session'),
7 1 : { Issuer, Strategy } = require('openid-client');
8 :
9 : /**
10 : * @typedef {import('express').Request} Request
11 : * @typedef {import('express').Response} Response
12 : * @typedef {import('express').NextFunction} NextFunction
13 : * @typedef {import('express').IRouter} IRouter
14 : */
15 :
16 : /* to serialize info in session */
17 1 : passport.serializeUser(function(user, done) {
18 0 : done(null, user);
19 : });
20 :
21 1 : passport.deserializeUser(function(user, done) {
22 0 : done(null, user);
23 : });
24 :
25 : /**
26 : * @param {string|string[]} role required role or list of roles (one of)
27 : * @param {Request} req incoming request
28 : * @param {Response} res outgoing reply
29 : * @param {NextFunction} next routing next callback
30 : */
31 : function roleCheck(role, req, res, next) {
32 0 : const isStringRole = isString(role);
33 0 : const userRoles = get(req.user, 'cern_roles');
34 :
35 0 : if (!req.user) {
36 0 : debug('unauth user -> /auth');
37 0 : return res.redirect(defaultTo(req.baseUrl, '') + '/auth');
38 : }
39 0 : else if (isStringRole && includes(userRoles, role)) {
40 0 : return next();
41 : }
42 0 : else if (!isStringRole && some(role, (r) => includes(userRoles, r))) {
43 0 : return next();
44 : }
45 : else {
46 0 : debug('permission denied');
47 0 : return res.render('denied', { baseUrl: req.baseUrl, role });
48 : }
49 : }
50 :
51 1 : module.exports = {
52 : /**
53 : *
54 : * @param {IRouter} app
55 : * @param {AppServer.Config} config
56 : * @param {(user: any) => any} [loginCallback]
57 : */
58 : async register(app, config, loginCallback) {
59 1 : var basePath = get(config, 'basePath', '');
60 :
61 : const oidcIssuer =
62 1 : await Issuer.discover('https://auth.cern.ch/auth/realms/cern');
63 1 : const client = new oidcIssuer.Client({
64 : client_id: get(config, 'auth.clientID'), /* eslint-disable-line camelcase */
65 : client_secret: get(config, 'auth.clientSecret'), /* eslint-disable-line camelcase */
66 : redirect_uris: [ get(config, 'auth.callbackURL') ], /* eslint-disable-line camelcase */
67 : post_logout_redirect_uris: [ get(config, 'auth.logoutURL') ] /* eslint-disable-line camelcase */
68 : });
69 :
70 1 : passport.use('oidc', new Strategy({ client },
71 : /** @type {import('openid-client').StrategyVerifyCallbackUserInfo<any>} */
72 : function(tokenSet, userInfo, done) {
73 0 : debug('user authenticated: %s', get(userInfo, 'email'));
74 0 : const user = tokenSet.claims();
75 0 : done(null, user);
76 0 : if (loginCallback) {
77 0 : loginCallback(user);
78 : }
79 : }));
80 :
81 1 : app.use(cookieSession({
82 : name: 'oidc:auth.cern.ch',
83 : secret: get(config, 'auth.clientSecret', 'default'),
84 : path: get(config, 'basePath') || '/',
85 : signed: true,
86 : overwrite: true
87 : }));
88 1 : app.use(passport.initialize());
89 1 : app.use(passport.session());
90 1 : app.get('/auth', passport.authenticate('oidc'));
91 1 : app.get('/auth/callback', passport.authenticate('oidc', {
92 : successRedirect: basePath + '/',
93 : failureRedirect: basePath + '/auth'
94 : }));
95 1 : app.get('/auth/logout', function(req, res) {
96 0 : req.session = null;
97 0 : res.redirect(client.endSessionUrl());
98 : });
99 1 : app.get('/auth/me', function(req, res) { res.send(req.user); });
100 : },
101 : roleCheck: roleCheck
102 : };
|