1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-08-08 06:55:30 +02:00

feat: Support OIDC signed UserInfo responses

Some OIDC providers support signed UserInfo response, to enhance
security. The OIDC client should be free to ask for the user info
sgnature, however in certain situations (e.g egov applications)
where security matters, the OIDC providers might chose to enforce
this sugnature.

Planka was not supported signed UserInfo response, which resulted
in an misleading exception 'invalidCodeOrNonce'.

Introduce the proper configurations to parametrize the OIDC client,
and a dedicated exception to improve the developer experience.

Specifications:

"The UserInfo Claims MUST be returned as the members of a JSON
object unless a signed or encrypted response was requested
during Client Registration."
This commit is contained in:
lebaudantoine 2024-07-15 18:49:06 +02:00
parent a6c8f1bc23
commit 9aaaca1b8d
4 changed files with 14 additions and 0 deletions

View file

@ -13,6 +13,9 @@ const Errors = {
MISSING_VALUES: {
missingValues: 'Unable to retrieve required values (email, name)',
},
INVALID_USERINFO_SIGNATURE: {
invalidUserInfoSignature: "Invalid signature on userInfo due to client misconfiguration"
}
};
module.exports = {
@ -40,6 +43,9 @@ module.exports = {
missingValues: {
responseType: 'unprocessableEntity',
},
invalidUserInfoSignature: {
responseType: 'unauthorized',
},
},
async fn(inputs) {
@ -51,6 +57,7 @@ module.exports = {
sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`);
return Errors.INVALID_CODE_OR_NONCE;
})
.intercept('invalidUserInfoSignature', () => Errors.INVALID_USERINFO_SIGNATURE)
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE)
.intercept('missingValues', () => Errors.MISSING_VALUES);

View file

@ -11,6 +11,7 @@ module.exports = {
},
exits: {
invalidUserInfoSignature: {},
invalidCodeOrNonce: {},
missingValues: {},
emailAlreadyInUse: {},
@ -34,6 +35,10 @@ module.exports = {
);
userInfo = await client.userinfo(tokenSet);
} catch (e) {
if (e instanceof SyntaxError && e.message.includes('Unexpected token e in JSON at position 0')) {
sails.log.warn('Error while exchanging OIDC code: userInfo response is signed.');
throw 'invalidUserInfoSignature';
}
sails.log.warn(`Error while exchanging OIDC code: ${e}`);
throw 'invalidCodeOrNonce';
}

View file

@ -30,6 +30,7 @@ module.exports = function defineOidcHook(sails) {
client_secret: sails.config.custom.oidcClientSecret,
redirect_uris: [sails.config.custom.oidcRedirectUri],
response_types: ['code'],
userinfo_signed_response_alg: sails.config.custom.oidcUserinfoSignedResponseAlg,
});
},

View file

@ -39,6 +39,7 @@ module.exports.custom = {
oidcIssuer: process.env.OIDC_ISSUER,
oidcClientId: process.env.OIDC_CLIENT_ID,
oidcClientSecret: process.env.OIDC_CLIENT_SECRET,
oidcUserinfoSignedResponseAlg: process.env.OIDC_USERINFO_SIGNED_RESPONSE_ALG,
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
oidcResponseMode: process.env.OIDC_RESPONSE_MODE || 'fragment',
oidcDefaultResponseMode: process.env.OIDC_DEFAULT_RESPONSE_MODE === 'true',