Add register and update hooks for oauth2 and openid drivers (#18131)

* Add register and update hooks for oauth2 and openid drivers

* Add register and update hooks for ldap and saml drivers

* Update contributors.yml

Add username nicam to contributors.yml

---------

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
Pascal Helfenstein
2023-04-12 01:47:40 +02:00
committed by GitHub
parent bce0c1c002
commit b220413819
5 changed files with 141 additions and 47 deletions

View File

@@ -3,6 +3,8 @@ import { Router } from 'express';
import Joi from 'joi';
import type { Client, Error, LDAPResult, SearchCallbackResponse, SearchEntry } from 'ldapjs';
import ldap from 'ldapjs';
import getDatabase from '../../database/index.js';
import emitter from '../../emitter.js';
import env from '../../env.js';
import { RecordNotUniqueException } from '../../exceptions/database/record-not-unique.js';
import {
@@ -267,10 +269,23 @@ export class LDAPAuthDriver extends AuthDriver {
const userId = await this.fetchUserId(userInfo.dn);
if (userId) {
// Run hook so the end user has the chance to augment the
// user that is about to be updated
let updatedUserPayload = await emitter.emitFilter(
`auth.update`,
{},
{ identifier: userInfo.dn, provider: this.config['provider'], providerPayload: { userInfo, userRole } },
{ database: getDatabase(), schema: this.schema, accountability: null }
);
// Only sync roles if the AD groups are configured
if (groupDn) {
await this.usersService.updateOne(userId, { role: userRole?.id ?? defaultRoleId ?? null });
updatedUserPayload = { role: userRole?.id ?? defaultRoleId ?? null, ...updatedUserPayload };
}
// Update user to update properties that might have changed
await this.usersService.updateOne(userId, updatedUserPayload);
return userId;
}
@@ -278,15 +293,26 @@ export class LDAPAuthDriver extends AuthDriver {
throw new InvalidCredentialsException();
}
const userPayload = {
provider: this.config['provider'],
first_name: userInfo.firstName,
last_name: userInfo.lastName,
email: userInfo.email,
external_identifier: userInfo.dn,
role: userRole?.id ?? defaultRoleId,
};
// Run hook so the end user has the chance to augment the
// user that is about to be created
const updatedUserPayload = await emitter.emitFilter(
`auth.create`,
userPayload,
{ identifier: userInfo.dn, provider: this.config['provider'], providerPayload: { userInfo, userRole } },
{ database: getDatabase(), schema: this.schema, accountability: null }
);
try {
await this.usersService.createOne({
provider: this.config['provider'],
first_name: userInfo.firstName,
last_name: userInfo.lastName,
email: userInfo.email,
external_identifier: userInfo.dn,
role: userRole?.id ?? defaultRoleId,
});
await this.usersService.createOne(updatedUserPayload);
} catch (e) {
if (e instanceof RecordNotUniqueException) {
logger.warn(e, '[LDAP] Failed to register user. User not unique');

View File

@@ -6,6 +6,8 @@ import flatten from 'flat';
import jwt from 'jsonwebtoken';
import { Client, errors, generators, Issuer } from 'openid-client';
import { getAuthProvider } from '../../auth.js';
import getDatabase from '../../database/index.js';
import emitter from '../../emitter.js';
import env from '../../env.js';
import { RecordNotUniqueException } from '../../exceptions/database/record-not-unique.js';
import {
@@ -138,15 +140,35 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
throw new InvalidCredentialsException();
}
const userPayload = {
provider,
first_name: userInfo[this.config['firstNameKey']],
last_name: userInfo[this.config['lastNameKey']],
email: email,
external_identifier: identifier,
role: this.config['defaultRoleId'],
auth_data: tokenSet.refresh_token && JSON.stringify({ refreshToken: tokenSet.refresh_token }),
};
const userId = await this.fetchUserId(identifier);
if (userId) {
// Update user refreshToken if provided
if (tokenSet.refresh_token) {
await this.usersService.updateOne(userId, {
auth_data: JSON.stringify({ refreshToken: tokenSet.refresh_token }),
});
}
// Run hook so the end user has the chance to augment the
// user that is about to be updated
const updatedUserPayload = await emitter.emitFilter(
`auth.update`,
{},
{
identifier,
provider: this.config['provider'],
providerPayload: { accessToken: tokenSet.access_token, userInfo },
},
{ database: getDatabase(), schema: this.schema, accountability: null }
);
// Update user to update refresh_token and other properties that might have changed
await this.usersService.updateOne(userId, updatedUserPayload);
return userId;
}
@@ -156,16 +178,21 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
throw new InvalidCredentialsException();
}
// Run hook so the end user has the chance to augment the
// user that is about to be created
const updatedUserPayload = await emitter.emitFilter(
`auth.create`,
userPayload,
{
identifier,
provider: this.config['provider'],
providerPayload: { accessToken: tokenSet.access_token, userInfo },
},
{ database: getDatabase(), schema: this.schema, accountability: null }
);
try {
await this.usersService.createOne({
provider,
first_name: userInfo[this.config['firstNameKey']],
last_name: userInfo[this.config['lastNameKey']],
email: email,
external_identifier: identifier,
role: this.config['defaultRoleId'],
auth_data: tokenSet.refresh_token && JSON.stringify({ refreshToken: tokenSet.refresh_token }),
});
await this.usersService.createOne(updatedUserPayload);
} catch (e) {
if (e instanceof RecordNotUniqueException) {
logger.warn(e, '[OAuth2] Failed to register user. User not unique');

View File

@@ -6,6 +6,8 @@ import flatten from 'flat';
import jwt from 'jsonwebtoken';
import { Client, errors, generators, Issuer } from 'openid-client';
import { getAuthProvider } from '../../auth.js';
import getDatabase from '../../database/index.js';
import emitter from '../../emitter.js';
import env from '../../env.js';
import { RecordNotUniqueException } from '../../exceptions/database/record-not-unique.js';
import {
@@ -161,15 +163,35 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
throw new InvalidCredentialsException();
}
const userPayload = {
provider,
first_name: userInfo['given_name'],
last_name: userInfo['family_name'],
email: email,
external_identifier: identifier,
role: this.config['defaultRoleId'],
auth_data: tokenSet.refresh_token && JSON.stringify({ refreshToken: tokenSet.refresh_token }),
};
const userId = await this.fetchUserId(identifier);
if (userId) {
// Update user refreshToken if provided
if (tokenSet.refresh_token) {
await this.usersService.updateOne(userId, {
auth_data: JSON.stringify({ refreshToken: tokenSet.refresh_token }),
});
}
// Run hook so the end user has the chance to augment the
// user that is about to be updated
const updatedUserPayload = await emitter.emitFilter(
`auth.update`,
{},
{
identifier,
provider: this.config['provider'],
providerPayload: { accessToken: tokenSet.access_token, userInfo },
},
{ database: getDatabase(), schema: this.schema, accountability: null }
);
// Update user to update refresh_token and other properties that might have changed
await this.usersService.updateOne(userId, updatedUserPayload);
return userId;
}
@@ -181,16 +203,21 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
throw new InvalidCredentialsException();
}
// Run hook so the end user has the chance to augment the
// user that is about to be created
const updatedUserPayload = await emitter.emitFilter(
`auth.create`,
userPayload,
{
identifier,
provider: this.config['provider'],
providerPayload: { accessToken: tokenSet.access_token, userInfo },
},
{ database: getDatabase(), schema: this.schema, accountability: null }
);
try {
await this.usersService.createOne({
provider,
first_name: userInfo['given_name'],
last_name: userInfo['family_name'],
email: email,
external_identifier: identifier,
role: this.config['defaultRoleId'],
auth_data: tokenSet.refresh_token && JSON.stringify({ refreshToken: tokenSet.refresh_token }),
});
await this.usersService.createOne(updatedUserPayload);
} catch (e) {
if (e instanceof RecordNotUniqueException) {
logger.warn(e, '[OpenID] Failed to register user. User not unique');

View File

@@ -4,6 +4,8 @@ import express, { Router } from 'express';
import * as samlify from 'samlify';
import { getAuthProvider } from '../../auth.js';
import { COOKIE_OPTIONS } from '../../constants.js';
import getDatabase from '../../database/index.js';
import emitter from '../../emitter.js';
import env from '../../env.js';
import { RecordNotUniqueException } from '../../exceptions/database/record-not-unique.js';
import { InvalidCredentialsException, InvalidProviderException } from '../../exceptions/index.js';
@@ -63,15 +65,26 @@ export class SAMLAuthDriver extends LocalAuthDriver {
const firstName = payload[givenNameKey ?? 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'];
const lastName = payload[familyNameKey ?? 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'];
const userPayload = {
provider,
first_name: firstName,
last_name: lastName,
email: email,
external_identifier: identifier.toLowerCase(),
role: this.config['defaultRoleId'],
};
// Run hook so the end user has the chance to augment the
// user that is about to be created
const updatedUserPayload = await emitter.emitFilter(
`auth.create`,
userPayload,
{ identifier: identifier.toLowerCase(), provider: this.config['provider'], providerPayload: { ...payload } },
{ database: getDatabase(), schema: this.schema, accountability: null }
);
try {
return await this.usersService.createOne({
provider,
first_name: firstName,
last_name: lastName,
email: email,
external_identifier: identifier.toLowerCase(),
role: this.config['defaultRoleId'],
});
return await this.usersService.createOne(updatedUserPayload);
} catch (error) {
if (error instanceof RecordNotUniqueException) {
logger.warn(error, '[SAML] Failed to register user. User not unique');

View File

@@ -11,6 +11,7 @@
- u12206050
- that1matt
- jaads
- nicam
- denkhaus
- joselcvarela
- knulpi