Skip to content

Commit cab0b4f

Browse files
[FEATURE] Modifier l'interface de la double mire SSO pour inclure toutes les données récupérées des utilisateurs (PIX-16303)
#11861
2 parents 9fbe64a + b48f4dc commit cab0b4f

File tree

19 files changed

+248
-159
lines changed

19 files changed

+248
-159
lines changed

admin/app/routes/authentication/login-oidc.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ export default class LoginOidcRoute extends Route {
7676
const error = new JSONApiError(apiError.detail, apiError);
7777

7878
const shouldUserCreateAnAccount = error.code === 'SHOULD_VALIDATE_CGU';
79-
const { authenticationKey, email } = error.meta ?? {};
79+
const { authenticationKey, userClaims } = error.meta ?? {};
8080
if (shouldUserCreateAnAccount && authenticationKey) {
81-
return { shouldUserCreateAnAccount, authenticationKey, email, identityProviderSlug };
81+
return { shouldUserCreateAnAccount, authenticationKey, email: userClaims.email, identityProviderSlug };
8282
}
8383

8484
if (error.status === '403' && error.code === 'PIX_ADMIN_ACCESS_NOT_ALLOWED') {

admin/tests/unit/routes/authentication/login-oidc-test.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ module('Unit | Route | login-oidc', function (hooks) {
164164
authenticationKey: 'key',
165165
givenName: 'Mélusine',
166166
familyName: 'TITEGOUTTE',
167-
email: 'melu@example.net',
167+
userClaims: {
168+
email: 'melu@example.net',
169+
},
168170
},
169171
},
170172
],

api/src/identity-access-management/application/oidc-provider/oidc-provider.controller.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,8 @@ async function authenticateOidcUser(request, h) {
4242
// TODO utiliser un message en anglais au lieu du français
4343
const message = "L'utilisateur n'a pas de compte Pix";
4444
const responseCode = 'SHOULD_VALIDATE_CGU';
45-
const { authenticationKey, givenName, familyName, email } = result;
46-
const meta = { authenticationKey, givenName, familyName };
4745

48-
if (email) {
49-
Object.assign(meta, { email });
50-
}
51-
52-
throw new UnauthorizedError(message, responseCode, meta);
46+
throw new UnauthorizedError(message, responseCode, result);
5347
}
5448

5549
/**

api/src/identity-access-management/domain/usecases/authenticate-oidc-user.usecase.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import lodash from 'lodash';
2+
13
import { ForbiddenAccess } from '../../../shared/domain/errors.js';
24

5+
const { omit } = lodash;
6+
37
/**
48
* @typedef {function} authenticateOidcUser
59
* @param {Object} params
@@ -63,8 +67,17 @@ async function authenticateOidcUser({
6367

6468
if (!user) {
6569
const authenticationKey = await authenticationSessionService.save({ userInfo, sessionContent });
66-
const { firstName: givenName, lastName: familyName, email } = userInfo;
67-
return { authenticationKey, givenName, familyName, email, isAuthenticationComplete: false };
70+
71+
const userClaims = omit(userInfo, ['externalIdentityId']);
72+
73+
return {
74+
authenticationKey,
75+
userClaims,
76+
isAuthenticationComplete: false,
77+
// TODO: The properties givenName and familyName are kept for backward compatibility with the Front. They will be removed soon.
78+
givenName: userClaims.firstName,
79+
familyName: userClaims.lastName,
80+
};
6881
}
6982

7083
await _assertUserHasAccessToApplication({ requestedApplication, user, adminMemberRepository });

api/tests/identity-access-management/unit/application/oidc-provider.controller.test.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,20 @@ describe('Unit | Identity Access Management | Application | Controller | oidc-pr
8383
it('returns UnauthorizedError', async function () {
8484
// given
8585
const authenticationKey = 'aaa-bbb-ccc';
86-
const givenName = 'Mélusine';
87-
const familyName = 'TITEGOUTTE';
86+
const firstName = 'Mélusine';
87+
const lastName = 'TITEGOUTTE';
8888
const email = 'melu@example.net';
89-
usecases.authenticateOidcUser.resolves({ authenticationKey, givenName, familyName, email });
89+
const userClaims = {
90+
firstName,
91+
lastName,
92+
email,
93+
};
94+
usecases.authenticateOidcUser.resolves({
95+
authenticationKey,
96+
userClaims,
97+
givenName: firstName,
98+
familyName: lastName,
99+
});
90100

91101
// when
92102
const error = await catchErr(oidcProviderController.authenticateOidcUser)(request, hFake);
@@ -95,7 +105,12 @@ describe('Unit | Identity Access Management | Application | Controller | oidc-pr
95105
expect(error).to.be.an.instanceOf(UnauthorizedError);
96106
expect(error.message).to.equal("L'utilisateur n'a pas de compte Pix");
97107
expect(error.code).to.equal('SHOULD_VALIDATE_CGU');
98-
expect(error.meta).to.deep.equal({ authenticationKey, givenName, familyName, email });
108+
expect(error.meta).to.deep.equal({
109+
authenticationKey,
110+
userClaims,
111+
givenName: firstName,
112+
familyName: lastName,
113+
});
99114
});
100115
});
101116
});

api/tests/identity-access-management/unit/domain/usecases/authenticate-oidc-user.usecase.test.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,13 @@ describe('Unit | Identity Access Management | Domain | UseCase | authenticate-oi
231231
expect(authenticationSessionService.save).to.have.been.calledWithExactly({ userInfo, sessionContent });
232232
expect(result).to.deep.equal({
233233
authenticationKey,
234+
userClaims: {
235+
firstName: 'Mélusine',
236+
lastName: 'TITEGOUTTE',
237+
email: 'melu@example.net',
238+
},
234239
givenName: 'Mélusine',
235240
familyName: 'TITEGOUTTE',
236-
email: 'melu@example.net',
237241
isAuthenticationComplete: false,
238242
});
239243
});

mon-pix/app/components/authentication/login-or-register-oidc.hbs

+35-28
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,46 @@
22
<div class="login-or-register-oidc-form__container">
33
<div class="login-or-register-oidc-form__register-container">
44
<h2 class="login-or-register-oidc-form__subtitle">{{t "pages.login-or-register-oidc.register-form.title"}}</h2>
5-
<div>
6-
<p class="login-or-register-oidc-form__description">
7-
{{! template-lint-disable "no-bare-strings" }}
8-
{{t "pages.login-or-register-oidc.register-form.description"}}
9-
<em>{{this.identityProviderOrganizationName}}</em>&nbsp;:
10-
</p>
11-
<div class="login-or-register-oidc-form__information">
12-
<ul>
13-
<li>{{t "pages.login-or-register-oidc.register-form.information.given-name" givenName=this.givenName}}</li>
14-
<li>{{t "pages.login-or-register-oidc.register-form.information.family-name" familyName=this.familyName}}</li>
15-
</ul>
5+
{{#if this.userClaimsToDisplay.length}}
6+
<div>
7+
<p class="login-or-register-oidc-form__description">
8+
{{! template-lint-disable "no-bare-strings" }}
9+
{{t "pages.login-or-register-oidc.register-form.description"}}
10+
<em>{{this.identityProviderOrganizationName}}</em>&nbsp;:
11+
</p>
12+
<div class="login-or-register-oidc-form__information">
13+
<ul>
14+
{{#each this.userClaimsToDisplay as |userClaimToDisplay|}}
15+
<li>{{userClaimToDisplay}}</li>
16+
{{/each}}
17+
</ul>
18+
</div>
19+
</div>
20+
<div class="login-or-register-oidc-form__cgu-container">
21+
<PixCheckbox {{on "change" this.onChange}}>
22+
<:label>{{t
23+
"common.cgu.message"
24+
cguUrl=this.cguUrl
25+
dataProtectionPolicyUrl=this.dataProtectionPolicyUrl
26+
htmlSafe=true
27+
}}</:label>
28+
</PixCheckbox>
1629
</div>
17-
</div>
18-
<div class="login-or-register-oidc-form__cgu-container">
19-
<PixCheckbox {{on "change" this.onChange}}>
20-
<:label>{{t
21-
"common.cgu.message"
22-
cguUrl=this.cguUrl
23-
dataProtectionPolicyUrl=this.dataProtectionPolicyUrl
24-
htmlSafe=true
25-
}}</:label>
26-
</PixCheckbox>
27-
</div>
2830

29-
{{#if this.registerErrorMessage}}
31+
{{#if this.registerErrorMessage}}
32+
<PixNotificationAlert @type="error" class="login-or-register-oidc-form__cgu-error">
33+
{{this.registerErrorMessage}}
34+
</PixNotificationAlert>
35+
{{/if}}
36+
37+
<PixButton @type="submit" @triggerAction={{this.register}} @isLoading={{this.isRegisterLoading}}>
38+
{{t "pages.login-or-register-oidc.register-form.button"}}
39+
</PixButton>
40+
{{else}}
3041
<PixNotificationAlert @type="error" class="login-or-register-oidc-form__cgu-error">
31-
{{this.registerErrorMessage}}
42+
{{this.userClaimsErrorMessage}}
3243
</PixNotificationAlert>
3344
{{/if}}
34-
35-
<PixButton @type="submit" @triggerAction={{this.register}} @isLoading={{this.isRegisterLoading}}>
36-
{{t "pages.login-or-register-oidc.register-form.button"}}
37-
</PixButton>
3845
</div>
3946

4047
<div class="login-or-register-oidc-form__divider"></div>

mon-pix/app/components/authentication/login-or-register-oidc.js

+35-8
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@ export default class LoginOrRegisterOidcComponent extends Component {
3535
return this.oidcIdentityProviders[this.args.identityProviderSlug]?.organizationName;
3636
}
3737

38-
get givenName() {
39-
return this.args.givenName;
40-
}
41-
42-
get familyName() {
43-
return this.args.familyName;
44-
}
45-
4638
get currentLanguage() {
4739
return this.intl.primaryLocale;
4840
}
@@ -55,6 +47,41 @@ export default class LoginOrRegisterOidcComponent extends Component {
5547
return this.url.dataProtectionPolicyUrl;
5648
}
5749

50+
get userClaimsErrorMessage() {
51+
const { userClaims } = this.args;
52+
53+
if (!userClaims) {
54+
return this.intl.t(`pages.login-or-register-oidc.register-form.information.error`);
55+
} else {
56+
return null;
57+
}
58+
}
59+
60+
get userClaimsToDisplay() {
61+
const { userClaims } = this.args;
62+
63+
const result = [];
64+
65+
if (userClaims) {
66+
const { firstName, lastName, ...rest } = userClaims;
67+
result.push(`${this.intl.t(`pages.login-or-register-oidc.register-form.information.firstName`)} ${firstName}`);
68+
result.push(`${this.intl.t(`pages.login-or-register-oidc.register-form.information.lastName`)} ${lastName}`);
69+
70+
Object.entries(rest).map(([key, value]) => {
71+
let label = `${this.intl.t(`pages.login-or-register-oidc.register-form.information.${key}`)}`;
72+
const translation = `${this.intl.t(`pages.login-or-register-oidc.register-form.information.${key}`)}`;
73+
74+
if (translation.includes('Missing translation')) {
75+
label = `${key} :`;
76+
}
77+
78+
return result.push(`${label} ${value}`);
79+
});
80+
}
81+
82+
return result;
83+
}
84+
5885
@action
5986
async login(event) {
6087
event.preventDefault();

mon-pix/app/controllers/authentication/login-or-register-oidc.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ import { action } from '@ember/object';
33
import { service } from '@ember/service';
44
import { tracked } from '@glimmer/tracking';
55

6+
import { SessionStorageEntry } from '../../utils/session-storage-entry';
7+
8+
const oidcUserAuthenticationStorage = new SessionStorageEntry('oidcUserAuthentication');
9+
610
export default class LoginOrRegisterOidcController extends Controller {
7-
queryParams = ['authenticationKey', 'identityProviderSlug', 'givenName', 'familyName'];
11+
queryParams = ['identityProviderSlug'];
812

913
@service url;
1014
@service oidcIdentityProviders;
@@ -15,7 +19,6 @@ export default class LoginOrRegisterOidcController extends Controller {
1519
@service currentDomain;
1620

1721
@tracked showOidcReconciliation = false;
18-
@tracked authenticationKey = null;
1922
@tracked identityProviderSlug = null;
2023
@tracked email = '';
2124
@tracked fullNameFromPix = '';
@@ -27,6 +30,14 @@ export default class LoginOrRegisterOidcController extends Controller {
2730
return this.url.showcase;
2831
}
2932

33+
get oidcUserAuthenticationStorage() {
34+
return oidcUserAuthenticationStorage.get();
35+
}
36+
37+
get userClaims() {
38+
return this.oidcUserAuthenticationStorage?.userClaims;
39+
}
40+
3041
get isInternationalDomain() {
3142
return !this.currentDomain.isFranceDomain;
3243
}
@@ -35,6 +46,10 @@ export default class LoginOrRegisterOidcController extends Controller {
3546
return this.intl.primaryLocale;
3647
}
3748

49+
get authenticationKey() {
50+
return this.oidcUserAuthenticationStorage?.authenticationKey;
51+
}
52+
3853
@action
3954
onLanguageChange(language) {
4055
this.locale.setLocale(language);

mon-pix/app/routes/authentication/login-oidc.js

+11-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import ENV from 'mon-pix/config/environment';
66
import { createTranslatedApplicationError } from 'mon-pix/errors/factories/create-application-error';
77
import JSONApiError from 'mon-pix/errors/json-api-error';
88

9+
import { SessionStorageEntry } from '../../utils/session-storage-entry';
10+
11+
const oidcUserAuthenticationStorage = new SessionStorageEntry('oidcUserAuthentication');
12+
913
export default class LoginOidcRoute extends Route {
1014
@service intl;
1115
@service location;
@@ -37,23 +41,21 @@ export default class LoginOidcRoute extends Route {
3741

3842
async model(params, transition) {
3943
const queryParams = transition.to.queryParams;
44+
4045
const identityProviderSlug = params.identity_provider_slug;
4146
if (queryParams.code) {
4247
return this._handleCallbackRequest(queryParams.code, queryParams.state, queryParams.iss, identityProviderSlug);
4348
}
4449
}
4550

46-
afterModel({ shouldValidateCgu, authenticationKey, identityProviderSlug, givenName, familyName } = {}) {
47-
const shouldCreateAnAccountForUser = shouldValidateCgu && authenticationKey;
51+
afterModel({ shouldValidateCgu, identityProviderSlug } = {}) {
52+
const shouldCreateAnAccountForUser = shouldValidateCgu && oidcUserAuthenticationStorage.get().authenticationKey;
4853

4954
if (!shouldCreateAnAccountForUser) return;
5055

5156
return this.router.replaceWith('authentication.login-or-register-oidc', {
5257
queryParams: {
53-
authenticationKey,
5458
identityProviderSlug,
55-
givenName,
56-
familyName,
5759
},
5860
});
5961
}
@@ -76,9 +78,10 @@ export default class LoginOidcRoute extends Route {
7678
const error = new JSONApiError(apiError.detail, apiError);
7779

7880
const shouldValidateCgu = error.code === 'SHOULD_VALIDATE_CGU';
79-
const { authenticationKey, givenName, familyName } = error.meta ?? {};
80-
if (shouldValidateCgu && authenticationKey) {
81-
return { shouldValidateCgu, authenticationKey, identityProviderSlug, givenName, familyName };
81+
82+
if (shouldValidateCgu && error.meta.authenticationKey) {
83+
oidcUserAuthenticationStorage.set(error.meta);
84+
return { shouldValidateCgu, identityProviderSlug };
8285
}
8386

8487
throw error;

mon-pix/app/templates/authentication/login-or-register-oidc.hbs

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
<Authentication::LoginOrRegisterOidc
2222
@identityProviderSlug={{this.identityProviderSlug}}
2323
@authenticationKey={{this.authenticationKey}}
24-
@givenName={{this.givenName}}
25-
@familyName={{this.familyName}}
24+
@userClaims={{this.userClaims}}
2625
@onLogin={{this.onLogin}}
2726
/>
2827
{{/if}}

mon-pix/mirage/routes/authentication/oidc/index.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ export default function (config) {
88
{},
99
{
1010
errors: [
11-
{ code: 'SHOULD_VALIDATE_CGU', meta: { authenticationKey: 'key', familyName: 'PIX', givenName: 'test' } },
11+
{
12+
code: 'SHOULD_VALIDATE_CGU',
13+
meta: { authenticationKey: 'key', userClaims: { lastName: 'PIX', firstName: 'test' } },
14+
},
1215
],
1316
},
1417
);

mon-pix/tests/acceptance/authentication/login-or-register-oidc-test.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ module('Acceptance | Login or register oidc', function (hooks) {
1919
const screen = await visit('/connexion/oidc-partner?code=oidc_example_code&state=auth_session_state');
2020

2121
// then
22-
assert.strictEqual(
23-
currentURL(),
24-
'/connexion/oidc?authenticationKey=key&familyName=PIX&givenName=test&identityProviderSlug=oidc-partner',
25-
);
22+
assert.strictEqual(currentURL(), '/connexion/oidc?identityProviderSlug=oidc-partner');
2623
assert.dom(screen.getByRole('button', { name: 'Sélectionnez une langue' })).exists();
2724
});
2825
});

0 commit comments

Comments
 (0)