Skip to content

Commit

Permalink
[TECH] Migrer la route POST /api/oidc/user/check-reconciliation vers …
Browse files Browse the repository at this point in the history
…src/identity-access-management (PIX-12618)

 #9008
  • Loading branch information
pix-service-auto-merge authored May 23, 2024
2 parents dedba38 + b831eb7 commit c7897e6
Show file tree
Hide file tree
Showing 19 changed files with 344 additions and 275 deletions.
26 changes: 0 additions & 26 deletions api/lib/application/authentication/oidc/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,6 @@ const register = async function (server) {

server.route([
...adminRoutes,
{
method: 'POST',
path: '/api/oidc/user/check-reconciliation',
config: {
auth: false,
validate: {
payload: Joi.object({
data: Joi.object({
attributes: Joi.object({
email: Joi.string().email().required(),
password: Joi.string().required(),
'identity-provider': Joi.string().required(),
'authentication-key': Joi.string().required(),
}),
type: Joi.string(),
}),
}),
},
handler: oidcController.findUserForReconciliation,
notes: [
"- Cette route permet d'identifier un utilisateur Pix provenant de la double mire OIDC.\n" +
"- Elle renvoie un objet contenant des informations sur l'utilisateur.",
],
tags: ['api', 'oidc'],
},
},
{
method: 'POST',
path: '/api/oidc/user/reconcile',
Expand Down
21 changes: 0 additions & 21 deletions api/lib/application/authentication/oidc/oidc-controller.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
import { PIX_ADMIN } from '../../../../src/authorization/domain/constants.js';
import { oidcAuthenticationServiceRegistry, usecases } from '../../../domain/usecases/index.js';
import * as oidcSerializer from '../../../infrastructure/serializers/jsonapi/oidc-serializer.js';

const findUserForReconciliation = async function (
request,
h,
dependencies = {
oidcSerializer,
},
) {
const { email, password, identityProvider, authenticationKey } = request.deserializedPayload;

const result = await usecases.findUserForOidcReconciliation({
email,
password,
identityProvider,
authenticationKey,
});

return h.response(dependencies.oidcSerializer.serialize(result)).code(200);
};

const reconcileUser = async function (
request,
Expand Down Expand Up @@ -73,7 +53,6 @@ const reconcileUserForAdmin = async function (
};

const oidcController = {
findUserForReconciliation,
reconcileUser,
reconcileUserForAdmin,
};
Expand Down
4 changes: 2 additions & 2 deletions api/lib/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ import * as stageCollectionForTargetProfileRepository from '../../../src/evaluat
import * as stageRepository from '../../../src/evaluation/infrastructure/repositories/stage-repository.js';
import { authenticationSessionService } from '../../../src/identity-access-management/domain/services/authentication-session.service.js';
import { OidcAuthenticationServiceRegistry } from '../../../src/identity-access-management/domain/services/oidc-authentication-service-registry.js';
import * as pixAuthenticationService from '../../../src/identity-access-management/domain/services/pix-authentication-service.js';
import * as refreshTokenService from '../../../src/identity-access-management/domain/services/refresh-token-service.js';
import { pixAuthenticationService } from '../../../src/identity-access-management/domain/services/pix-authentication-service.js';
import { refreshTokenService } from '../../../src/identity-access-management/domain/services/refresh-token-service.js';
import * as oidcProviderRepository from '../../../src/identity-access-management/infrastructure/repositories/oidc-provider-repository.js';
import * as organizationForAdminRepository from '../../../src/organizational-entities/infrastructure/repositories/organization-for-admin-repository.js';
import * as campaignManagementRepository from '../../../src/prescription/campaign/infrastructure/repositories/campaign-management-repository.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BadRequestError, UnauthorizedError } from '../../../../lib/application/http-errors.js';
import * as oidcSerializer from '../../../../lib/infrastructure/serializers/jsonapi/oidc-serializer.js';
import { usecases } from '../../domain/usecases/index.js';
import * as oidcProviderSerializer from '../../infrastructure/serializers/jsonapi/oidc-identity-providers.serializer.js';

Expand Down Expand Up @@ -64,6 +65,25 @@ async function createUser(request, h) {
return h.response({ access_token, logout_url_uuid }).code(200);
}

/**
* @param request
* @param h
* @param dependencies
* @return {Promise<*>}
*/
async function findUserForReconciliation(request, h, dependencies = { oidcSerializer }) {
const { email, password, identityProvider, authenticationKey } = request.deserializedPayload;

const result = await usecases.findUserForOidcReconciliation({
email,
password,
identityProvider,
authenticationKey,
});

return h.response(dependencies.oidcSerializer.serialize(result)).code(200);
}

/**
* @typedef {function} getAuthorizationUrl
* @param request
Expand Down Expand Up @@ -116,13 +136,15 @@ async function getRedirectLogoutUrl(request, h) {
/**
* @typedef {Object} OidcProviderController
* @property {authenticateOidcUser} authenticateOidcUser
* @property {findUserForReconciliation} findUserForReconciliation
* @property {getAuthorizationUrl} getAuthorizationUrl
* @property {getIdentityProviders} getIdentityProviders
* @property {getRedirectLogoutUrl} getRedirectLogoutUrl
*/
export const oidcProviderController = {
authenticateOidcUser,
createUser,
findUserForReconciliation,
getAuthorizationUrl,
getIdentityProviders,
getRedirectLogoutUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,30 @@ export const oidcProviderRoutes = [
tags: ['identity-access-management', 'api', 'oidc'],
},
},
{
method: 'POST',
path: '/api/oidc/user/check-reconciliation',
config: {
auth: false,
validate: {
payload: Joi.object({
data: Joi.object({
attributes: Joi.object({
email: Joi.string().email().required(),
password: Joi.string().required(),
'identity-provider': Joi.string().required(),
'authentication-key': Joi.string().required(),
}),
type: Joi.string(),
}),
}),
},
handler: (request, h) => oidcProviderController.findUserForReconciliation(request, h),
notes: [
"- Cette route permet d'identifier un utilisateur Pix provenant de la double mire OIDC.\n" +
"- Elle renvoie un objet contenant des informations sur l'utilisateur.",
],
tags: ['identity-access-management', 'api', 'oidc'],
},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import { cryptoService } from '../../../shared/domain/services/crypto-service.js
import * as userLoginRepository from '../../../shared/infrastructure/repositories/user-login-repository.js';
import { PasswordNotMatching } from '../errors.js';

/**
* @typedef {function} getUserByUsernameAndPassword
* @param {Object} params
* @param {string} params.username
* @param {string} params.password
* @param {UserRepository} params.userRepository
* @param {Object} params.dependencies
* @return {Promise<User|UserNotFoundError|PasswordNotMatching>}
*/
async function getUserByUsernameAndPassword({
username,
password,
Expand Down Expand Up @@ -45,4 +54,8 @@ async function getUserByUsernameAndPassword({
return foundUser;
}

export { getUserByUsernameAndPassword };
/**
* @typedef {Object} PixAuthenticationService
* @property {getUserByUsernameAndPassword} getUserByUsernameAndPassword
*/
export const pixAuthenticationService = { getUserByUsernameAndPassword };
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ const userRefreshTokensTemporaryStorage = temporaryStorage.withPrefix('user-refr

const REFRESH_TOKEN_EXPIRATION_DELAY_ADDITION_SECONDS = 60 * 60; // 1 hour

function _prefixForUser(userId) {
return `${userId}:`;
}

/**
* @typedef {function} createRefreshTokenFromUserId
* @param {Object} params
* @param {string} params.userId
* @param {string} params.source
* @param {function} params.uuidGenerator
* @return {Promise<string>}
*/
async function createRefreshTokenFromUserId({ userId, source, uuidGenerator = randomUUID }) {
const expirationDelaySeconds = config.authentication.refreshTokenLifespanMs / 1000;
const refreshToken = `${_prefixForUser(userId)}${uuidGenerator()}`;
Expand All @@ -34,19 +38,37 @@ async function createRefreshTokenFromUserId({ userId, source, uuidGenerator = ra
return refreshToken;
}

/**
* @typedef {function} createAccessTokenFromRefreshToken
* @param {Object} params
* @param {string} params.refreshToken
* @return {Promise<{expirationDelaySeconds: number, accessToken: string}>}
*/
async function createAccessTokenFromRefreshToken({ refreshToken }) {
const { userId, source } = (await refreshTokenTemporaryStorage.get(refreshToken)) || {};
if (!userId) throw new UnauthorizedError('Refresh token is invalid', 'INVALID_REFRESH_TOKEN');
return tokenService.createAccessTokenFromUser(userId, source);
}

/**
* @typedef {function} revokeRefreshToken
* @param {Object} params
* @param {string} params.refreshToken
* @return {Promise<void>}
*/
async function revokeRefreshToken({ refreshToken }) {
const { userId } = (await refreshTokenTemporaryStorage.get(refreshToken)) || {};
if (!userId) return;
await userRefreshTokensTemporaryStorage.lrem({ key: userId, valueToRemove: refreshToken });
await refreshTokenTemporaryStorage.delete(refreshToken);
}

/**
* @typedef {function} revokeRefreshTokensForUserId
* @param {Object} params
* @param {string|number} params.userId
* @return {Promise<void>}
*/
async function revokeRefreshTokensForUserId({ userId }) {
const refreshTokens = await userRefreshTokensTemporaryStorage.lrange(userId);
await userRefreshTokensTemporaryStorage.delete(userId);
Expand All @@ -55,11 +77,24 @@ async function revokeRefreshTokensForUserId({ userId }) {
});
}

export {
/**
* @typedef {Object} RefreshTokenService
* @property {createAccessTokenFromRefreshToken} createAccessTokenFromRefreshToken
* @property {createRefreshTokenFromUserId} createRefreshTokenFromUserId
* @property {*} refreshTokenTemporaryStorage
* @property {revokeRefreshToken} revokeRefreshToken
* @property {revokeRefreshTokensForUserId} revokeRefreshTokensForUserId
* @property {*} userRefreshTokensTemporaryStorage
*/
export const refreshTokenService = {
createAccessTokenFromRefreshToken,
createRefreshTokenFromUserId,
refreshTokenTemporaryStorage,
revokeRefreshToken,
revokeRefreshTokensForUserId,
userRefreshTokensTemporaryStorage,
};

function _prefixForUser(userId) {
return `${userId}:`;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import { AuthenticationKeyExpired, DifferentExternalIdentifierError } from '../errors.js';

import { AuthenticationKeyExpired, DifferentExternalIdentifierError } from '../../../../lib/domain/errors.js';

/**
* @typedef {function} findUserForOidcReconciliation
* @param {Object} params
* @param {string} params.authenticationKey
* @param {string} params.email
* @param {string} params.password
* @param {string} params.identityProvider
* @param {AuthenticationSessionService} params.authenticationSessionService
* @param {PixAuthenticationService} params.pixAuthenticationService
* @param {AuthenticationMethodRepository} params.authenticationMethodRepository
* @param {UserRepository} params.userRepository
* @return {Promise<{fullNameFromPix: string, authenticationMethods: AuthenticationMethod[], fullNameFromExternalIdentityProvider: string, email: string, username: string}>}
*/
const findUserForOidcReconciliation = async function ({
authenticationKey,
email,
Expand Down
5 changes: 3 additions & 2 deletions api/src/identity-access-management/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { injectDependencies } from '../../../shared/infrastructure/utils/depende
import { importNamedExportsFromDirectory } from '../../../shared/infrastructure/utils/import-named-exports-from-directory.js';
import { oidcProviderRepository } from '../../infrastructure/repositories/oidc-provider-repository.js';
import { authenticationSessionService } from '../services/authentication-session.service.js';
import * as pixAuthenticationService from '../services/pix-authentication-service.js';
import * as refreshTokenService from '../services/refresh-token-service.js';
import { pixAuthenticationService } from '../services/pix-authentication-service.js';
import { refreshTokenService } from '../services/refresh-token-service.js';
import { addOidcProviderValidator } from '../validators/add-oidc-provider.validator.js';

const path = dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -62,6 +62,7 @@ const usecases = injectDependencies(usecasesWithoutInjectedDependencies, depende
* @property {addOidcProvider} addOidcProvider
* @property {authenticateOidcUser} authenticateOidcUser
* @property {createOidcUser} createOidcUser
* @property {findUserForOidcReconciliation} findUserForOidcReconciliation
* @property {getAllIdentityProviders} getAllIdentityProviders
* @property {getAuthorizationUrl} getAuthorizationUrl
* @property {getReadyIdentityProviders} getReadyIdentityProviders
Expand Down

This file was deleted.

Loading

0 comments on commit c7897e6

Please sign in to comment.