Skip to content

Commit 74a2102

Browse files
authored
Merge pull request #1311 from numerique-gouv/douglasduteil/feat-remove-null-and-empty-field-in-claim
🐛 fix: remove null and empty field in claim
2 parents 22fdc9d + 6acdee9 commit 74a2102

File tree

6 files changed

+149
-52
lines changed

6 files changed

+149
-52
lines changed

cypress/e2e/signin_from_proconnect_federation_client/fixtures.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ INSERT INTO users
22
(id, email, email_verified, email_verified_at, encrypted_password, created_at, updated_at, given_name, family_name,
33
phone_number, job)
44
VALUES
5-
(1, 'unused1@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Jean', '0123456789', 'Sbire'),
6-
(2, 'unused2@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Jean', '0123456789', 'Sbire');
5+
(1, 'unused1@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Jack', '0123456789', 'Sbire'),
6+
(2, 'unused2@yopmail.com', true, CURRENT_TIMESTAMP, '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Jean', 'Luck', '', 'Sbire');
77

88

99
INSERT INTO organizations

cypress/e2e/signin_from_proconnect_federation_client/index.cy.ts

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
11
//
22

3-
describe("sign-in from proconnect federation client", () => {
4-
it("should seed the database once", function () {
5-
cy.seed();
6-
});
3+
it("should seed the database once", function () {
4+
cy.seed();
5+
});
76

7+
describe("sign-in from proconnect federation client", () => {
88
it("should sign-in", () => {
99
cy.visit("http://localhost:4001");
10-
cy.get("button.proconnect-button").click();
10+
cy.contains("S’identifier avec ProConnect").click();
1111

12-
cy.get('[name="password"]').type("password123");
13-
cy.get('[action="/users/sign-in"] [type="submit"]')
14-
.contains("S’identifier")
15-
.click();
12+
cy.contains("Renseignez votre mot de passe").click();
13+
cy.focused().type("password123");
14+
cy.contains("S’identifier").click();
1615

16+
cy.title().should("equal", "proconnect-federation-client - ProConnect");
1717
cy.contains("proconnect-federation-client");
18-
cy.contains("unused1@yopmail.com");
19-
cy.contains("21340126800130");
18+
19+
cy.contains('"given_name": "Jean"');
20+
cy.contains('"usual_name": "Jack"');
21+
cy.contains('"email": "unused1@yopmail.com"');
22+
cy.contains('"siret": "21340126800130"');
23+
cy.contains('"phone_number": "0123456789"');
24+
cy.contains('"phone_number_verified": false');
25+
cy.contains('"is_service_public": true');
26+
cy.contains('"is_public_service": true');
2027
});
2128

2229
it("should not prompt for password if a session is already opened", () => {
2330
cy.visit("/");
2431
cy.login("unused1@yopmail.com");
2532

2633
cy.visit("http://localhost:4001");
27-
cy.get("button.proconnect-button").click();
34+
cy.contains("S’identifier avec ProConnect").click();
2835

2936
cy.contains("proconnect-federation-client");
3037
cy.contains("unused1@yopmail.com");
@@ -35,7 +42,7 @@ describe("sign-in from proconnect federation client", () => {
3542
cy.login("unused2@yopmail.com");
3643

3744
cy.visit("http://localhost:4001");
38-
cy.get("button.proconnect-button").click();
45+
cy.contains("S’identifier avec ProConnect").click();
3946

4047
cy.get('[name="password"]').type("password123");
4148
cy.get('[action="/users/sign-in"] [type="submit"]')
@@ -48,7 +55,7 @@ describe("sign-in from proconnect federation client", () => {
4855

4956
it("should go back to the Federation client when hitting the change email button", () => {
5057
cy.visit("http://localhost:4001");
51-
cy.get("button.proconnect-button").click();
58+
cy.contains("S’identifier avec ProConnect").click();
5259

5360
cy.get("#change-email-address").click();
5461

@@ -75,5 +82,35 @@ describe("sign-in with a client requiring 2fa identity", () => {
7582
},
7683
},
7784
}));
85+
cy.contains("Connexion personnalisée").click({ force: true });
86+
});
87+
});
88+
89+
describe("sign-in partial user info", () => {
90+
beforeEach(() => {
91+
cy.visit("http://localhost:4001");
92+
cy.updateCustomParams((customParams) => ({
93+
...customParams,
94+
login_hint: "unused2@yopmail.com",
95+
}));
96+
cy.contains("Connexion personnalisée").click({ force: true });
97+
98+
cy.contains("Renseignez votre mot de passe").click();
99+
cy.focused().type("password123");
100+
cy.contains("S’identifier").click();
101+
});
102+
103+
it("should sign-in without passing a phone_number", () => {
104+
cy.title().should("equal", "proconnect-federation-client - ProConnect");
105+
cy.contains("proconnect-federation-client");
106+
107+
cy.contains('"given_name": "Jean"');
108+
cy.contains('"usual_name": "Luck"');
109+
cy.contains('"email": "unused2@yopmail.com"');
110+
cy.contains('"siret": "21340126800130"');
111+
cy.contains('"phone_number": ""').should("not.exist");
112+
cy.contains('"phone_number_verified": false');
113+
cy.contains('"is_service_public": true');
114+
cy.contains('"is_public_service": true');
78115
});
79116
});

cypress/e2e/signin_from_standard_client/index.cy.ts

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,68 +7,88 @@ describe("sign-in from standard client", () => {
77

88
it("should sign-in without org selection when having only one organization", function () {
99
cy.visit("http://localhost:4000");
10-
cy.get("button.proconnect-button").click();
10+
cy.contains("S’identifier avec ProConnect").click();
1111

12+
cy.title().should("include", "S'inscrire ou se connecter - ");
1213
cy.login("unused1@yopmail.com");
1314

15+
cy.title().should("equal", "standard-client - ProConnect");
1416
cy.contains("standard-client");
15-
cy.contains("unused1@yopmail.com");
16-
cy.contains("Commune de lamalou-les-bains - Mairie");
17+
cy.contains('"email": "unused1@yopmail.com"');
18+
cy.contains('"label": "Commune de lamalou-les-bains - Mairie"');
1719

1820
// then it should prompt for organization
19-
cy.get("button#select-organization").click();
21+
cy.contains("button", "Changer d’organisation").click();
22+
23+
cy.title().should("include", "Choisir une organisation - ");
2024
cy.contains("Votre organisation de rattachement");
21-
cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").click();
25+
cy.getByLabel(
26+
"Commune de lamalou-les-bains - Mairie (choisir cette organisation)",
27+
).click();
28+
29+
cy.title().should("equal", "standard-client - ProConnect");
2230
cy.contains("standard-client");
23-
cy.contains("Commune de lamalou-les-bains - Mairie");
31+
cy.contains('"label": "Commune de lamalou-les-bains - Mairie"');
2432

2533
// then it should update userinfo
26-
cy.contains("Jean Un");
27-
cy.get("button#update-userinfo").click();
34+
cy.contains('"family_name": "Jean Un"');
35+
cy.contains("button", "Mettre à jour mes informations").click();
36+
37+
cy.title().should("include", "Renseigner votre identité - ");
2838
cy.contains("Renseigner son identité");
29-
cy.get('[name="family_name"]').type("Moustaki");
30-
cy.get('[type="submit"]').click();
39+
cy.contains("Nom").click();
40+
cy.focused().clear().type("Moustaki");
41+
cy.contains("Valider").click();
42+
43+
cy.title().should("equal", "standard-client - ProConnect");
3144
cy.contains("standard-client");
32-
cy.contains("Moustaki");
45+
cy.contains('"family_name": "Moustaki"');
3346
});
3447

3548
it("should sign-in with org selection when having two organization", function () {
3649
cy.visit("http://localhost:4000");
37-
cy.get("button.proconnect-button").click();
50+
cy.contains("S’identifier avec ProConnect").click();
3851

52+
cy.title().should("include", "S'inscrire ou se connecter - ");
3953
cy.login("unused2@yopmail.com");
4054

41-
cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").contains(
42-
"Commune de lamalou-les-bains - Mairie",
43-
);
44-
cy.get(".fr-grid-row .fr-col-12:last-child .fr-tile__link").contains(
45-
"Commune de clamart - Mairie",
55+
cy.title().should("include", "Choisir une organisation - ");
56+
cy.getByLabel(
57+
"Commune de lamalou-les-bains - Mairie (choisir cette organisation)",
4658
);
4759

48-
cy.get(".fr-grid-row .fr-col-12:last-child .fr-tile__link").click();
60+
cy.getByLabel(
61+
"Commune de clamart - Mairie (choisir cette organisation)",
62+
).click();
4963

64+
cy.title().should("equal", "standard-client - ProConnect");
5065
cy.contains("standard-client");
51-
cy.contains("unused2@yopmail.com");
52-
cy.contains("Commune de clamart - Mairie");
66+
cy.contains('"email": "unused2@yopmail.com"');
67+
cy.contains('"label": "Commune de clamart - Mairie"');
5368

5469
// then it should prompt for organization
55-
cy.get("button#select-organization").click();
70+
cy.contains("button", "Changer d’organisation").click();
71+
cy.title().should("include", "Choisir une organisation - ");
5672
cy.contains("Votre organisation de rattachement");
57-
cy.get(".fr-grid-row .fr-col-12:first-child .fr-tile__link").click();
73+
cy.getByLabel(
74+
"Commune de lamalou-les-bains - Mairie (choisir cette organisation)",
75+
).click();
5876

77+
cy.title().should("equal", "standard-client - ProConnect");
5978
cy.contains("standard-client");
60-
cy.contains("Commune de lamalou-les-bains - Mairie");
79+
cy.contains('"email": "unused2@yopmail.com"');
80+
cy.contains('"label": "Commune de lamalou-les-bains - Mairie"');
6181
});
6282

6383
it("should not prompt for password if a session is already opened", () => {
6484
cy.visit("/");
85+
cy.title().should("include", "S'inscrire ou se connecter - ");
6586
cy.login("unused1@yopmail.com");
6687

6788
cy.visit("http://localhost:4000");
68-
cy.get("button.proconnect-button").click();
69-
89+
cy.contains("S’identifier avec ProConnect").click();
7090
cy.contains("standard-client");
71-
cy.contains("unused1@yopmail.com");
91+
cy.contains('"email": "unused1@yopmail.com"');
7292
});
7393

7494
it("should bypass consent prompt", () => {
@@ -79,6 +99,7 @@ describe("sign-in from standard client", () => {
7999
}));
80100
cy.get("button#custom-connection").click({ force: true });
81101

102+
cy.title().should("include", "S'inscrire ou se connecter - ");
82103
cy.login("unused1@yopmail.com");
83104
cy.contains("standard-client");
84105
});
@@ -96,6 +117,7 @@ describe("sign-in from standard client", () => {
96117
}));
97118
cy.get("button#custom-connection").click({ force: true });
98119

120+
cy.title().should("include", "S'inscrire ou se connecter - ");
99121
cy.login("unused1@yopmail.com");
100122
cy.contains("standard-client");
101123
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
3+
import { z } from "zod";
4+
5+
//
6+
7+
export const UserClaimsSchema = z.object({
8+
email_verified: z.boolean(),
9+
email: z.string(),
10+
family_name: z.string().optional(),
11+
given_name: z.string().optional(),
12+
job: z.string().optional(),
13+
phone_number_verified: z.boolean(),
14+
phone_number: z.string().optional(),
15+
sub: z.string(),
16+
uid: z.string(),
17+
updated_at: z.date(),
18+
usual_name: z.string().optional(),
19+
});
20+
21+
export type UserClaims = z.output<typeof UserClaimsSchema>;

packages/identite/src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22

3+
export * from "./claims.js";
34
export * from "./contexts.js";
45
export * from "./dirigeant.js";
56
export * from "./email-domain.js";

src/services/oidc-account-adapter.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
//
2+
3+
import {
4+
UserClaimsSchema,
5+
type UserClaims,
6+
} from "@gouvfr-lasuite/proconnect.identite/types";
17
import * as Sentry from "@sentry/node";
28
import { to } from "await-to-js";
3-
import { isEmpty } from "lodash-es";
9+
import { isEmpty, omitBy } from "lodash-es";
410
import type { FindAccount } from "oidc-provider";
511
import { findByUserId as getUsersOrganizations } from "../repositories/organization/getters";
612
import { getSelectedOrganizationId } from "../repositories/redis/selected-organization";
@@ -30,19 +36,29 @@ export const findAccount: FindAccount = async (_ctx, sub) => {
3036
job,
3137
} = user;
3238

33-
const personalClaims = {
34-
sub: id.toString(), // it is essential to always return a sub claim
35-
uid: id.toString(), // for ProConnect Federation use only
36-
email,
39+
const userClaims = {
3740
email_verified,
38-
updated_at,
39-
given_name,
41+
email,
4042
family_name,
41-
usual_name: family_name, // for ProConnect Federation use only
42-
phone_number,
43-
phone_number_verified: false,
43+
given_name,
4444
job,
45+
phone_number_verified: false,
46+
phone_number,
47+
sub: id.toString(), // it is essential to always return a sub claim
48+
uid: id.toString(), // for ProConnect Federation use only
49+
updated_at,
50+
usual_name: family_name,
4551
};
52+
const personalClaims: UserClaims = UserClaimsSchema.parse(
53+
omitBy(
54+
userClaims,
55+
(value) =>
56+
// NOTE(douglasduteil): a Claim SHOULD NOT be present with a null or empty string value
57+
// \see https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
58+
value === null || value === "",
59+
),
60+
);
61+
4662
const organizations = await getUsersOrganizations(id);
4763
if (mustReturnOneOrganizationInPayload(scope)) {
4864
const [selectedOrganizationIdErr, selectedOrganizationId] = await to(

0 commit comments

Comments
 (0)