OIDC token exchange

pull/203/head
Lorenz Brun 4 years ago
parent 6f99d99683
commit 71e73494a5

@ -0,0 +1,64 @@
module.exports = {
inputs: {
code: {
type: 'string',
required: true,
}
},
exits: {
invalidEmailOrUsername: {
responseType: 'unauthorized',
},
invalidPassword: {
responseType: 'unauthorized',
},
},
async fn(inputs) {
const client = sails.hooks.oidc.getClient();
const tokenSet = await client.callback(sails.config.custom.baseUrl + "/login", { code: inputs.code });
const userInfo = await client.userinfo(tokenSet);
const now = new Date();
let isAdmin = false;
if (sails.config.custom.oidcAdminRoles.includes('*'))
isAdmin = true;
else {
if (Array.isArray(userInfo[sails.config.custom.oidcRolesAttribute])) {
const userRoles = new Set(userInfo[sails.config.custom.oidcRolesAttribute]);
isAdmin = sails.config.custom.oidcAdminRoles.findIndex(role => userRoles.has(role)) > -1;
}
}
const newUser = {
email: userInfo.email,
password: "$sso$", // Prohibit password login for SSO accounts
isAdmin,
name: userInfo.name,
username: userInfo.sub,
subscribeToOwnCards: false,
createdAt: now,
updatedAt: now,
};
const user = await User.findOrCreate({ username: userInfo.sub }, newUser);
const controlledFields = ["email", "password", "isAdmin", "name", "username"];
const updateFields = {};
for (const field of controlledFields) {
if (user[field] !== newUser[field]) {
updateFields[field] = newUser[field];
}
}
if (Object.keys(updateFields).length > 0) {
updateFields.updatedAt = now;
await User.updateOne({ id: user.id }).set(updateFields);
}
return {
item: sails.helpers.utils.signToken(user.id),
};
},
};

@ -0,0 +1,35 @@
const openidClient = require('openid-client');
module.exports = function oidcServiceHook(sails) {
let client = null;
return {
/**
* Runs when this Sails app loads/lifts.
*/
async initialize() {
if (sails.config.custom.oidcIssuer) {
const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
client = new issuer.Client({
client_id: sails.config.custom.oidcClientId,
client_secret: sails.config.custom.oidcClientSecret,
redirect_uris: [sails.config.custom.baseUrl + "/login"],
response_types: ['code'],
response_mode: ['fragment'],
});
sails.log.info('OIDC hook has been loaded successfully');
}
},
getClient() {
return client;
},
isActive() {
return client !== null;
}
};
};

@ -28,4 +28,10 @@ module.exports.custom = {
attachmentsPath: path.join(sails.config.paths.public, 'attachments'), attachmentsPath: path.join(sails.config.paths.public, 'attachments'),
attachmentsUrl: `${process.env.BASE_URL}/attachments`, attachmentsUrl: `${process.env.BASE_URL}/attachments`,
oidcIssuer: process.env.OIDC_ISSUER,
oidcClientId: process.env.OIDC_CLIENT_ID,
oidcClientSecret: process.env.OIDC_CLIENT_SECRET,
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE ?? 'groups',
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES.split(",") ?? [],
}; };

@ -35,4 +35,5 @@ module.exports.policies = {
// 'boards/delete': ['is-authenticated', 'is-admin'], // 'boards/delete': ['is-authenticated', 'is-admin'],
'access-tokens/create': true, 'access-tokens/create': true,
'access-tokens/exchange': true,
}; };

@ -10,6 +10,7 @@
module.exports.routes = { module.exports.routes = {
'POST /api/access-tokens': 'access-tokens/create', 'POST /api/access-tokens': 'access-tokens/create',
'POST /api/access-tokens/exchange': 'access-tokens/exchange',
'GET /api/users': 'users/index', 'GET /api/users': 'users/index',
'POST /api/users': 'users/create', 'POST /api/users': 'users/create',

File diff suppressed because it is too large Load Diff

@ -46,6 +46,7 @@
"knex": "^0.95.9", "knex": "^0.95.9",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.29.1", "moment": "^2.29.1",
"openid-client": "^5.1.2",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sails": "^1.4.4", "sails": "^1.4.4",
"sails-hook-orm": "^3.0.2", "sails-hook-orm": "^3.0.2",

Loading…
Cancel
Save