Skip to content

Commit d157d96

Browse files
committed
feat(auth): add OIDC strategy to passport
1 parent d3d04a0 commit d157d96

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

src/service/passport/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const local = require('./local');
22
const activeDirectory = require('./activeDirectory');
3+
const oidc = require('./oidc');
34
const config = require('../../config');
45
const authenticationConfig = config.getAuthentication();
56
let _passport;
@@ -14,6 +15,9 @@ const configure = async () => {
1415
case 'local':
1516
_passport = await local.configure();
1617
break;
18+
case 'openidconnect':
19+
_passport = await oidc.configure();
20+
break;
1721
default:
1822
throw Error(`uknown authentication type ${type}`);
1923
}

src/service/passport/oidc.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
const configure = async () => {
2+
const passport = require('passport');
3+
const { Strategy: OIDCStrategy } = require('passport-openidconnect');
4+
const db = require('../../db');
5+
6+
const config = require('../../config').getAuthentication();
7+
const oidcConfig = config.oidcConfig;
8+
9+
passport.use(
10+
new OIDCStrategy(oidcConfig, async function verify(issuer, profile, cb) {
11+
try {
12+
const user = await db.findUserByOIDC(profile.id);
13+
14+
if (!user) {
15+
const email = safelyExtractEmail(profile);
16+
if (!email) {
17+
return cb(new Error('No email found in OIDC profile'));
18+
}
19+
20+
const username = getUsername(email);
21+
const newUser = {
22+
username: username,
23+
email: email,
24+
oidcId: profile.id,
25+
};
26+
27+
await db.createUser(
28+
newUser.username,
29+
null,
30+
newUser.email,
31+
'Edit me',
32+
false,
33+
newUser.oidcId,
34+
);
35+
36+
return cb(null, newUser);
37+
}
38+
return cb(null, user);
39+
} catch (err) {
40+
return cb(err);
41+
}
42+
}),
43+
);
44+
45+
passport.serializeUser((user, cb) => {
46+
cb(null, user.oidcId || user.username);
47+
});
48+
49+
passport.deserializeUser(async (id, cb) => {
50+
try {
51+
const user = (await db.findUserByOIDC(id)) || (await db.findUser(id));
52+
cb(null, user);
53+
} catch (err) {
54+
cb(err);
55+
}
56+
});
57+
58+
passport.type = 'openidconnect';
59+
return passport;
60+
};
61+
62+
module.exports.configure = configure;
63+
64+
/**
65+
* Extracts email from OIDC profile.
66+
* This function is necessary because OIDC providers have different ways of storing emails.
67+
* @param {object} profile the profile object from OIDC provider
68+
* @return {string | null} the email address
69+
*/
70+
const safelyExtractEmail = (profile) => {
71+
if (profile.emails && profile.emails.length > 0) {
72+
return profile.emails[0].value;
73+
}
74+
75+
if (profile.email) {
76+
return profile.email;
77+
}
78+
return null;
79+
};
80+
81+
/**
82+
* Generates a username from email address.
83+
* This helps differentiate users within the specific OIDC provider.
84+
* Note: This is incompatible with multiple providers. Ideally, users are identified by
85+
* OIDC ID (requires refactoring the database).
86+
* @param {string} email the email address
87+
* @return {string} the username
88+
*/
89+
const getUsername = (email) => {
90+
return email ? email.split('@')[0] : '';
91+
};

0 commit comments

Comments
 (0)