-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(user): add certification routes
- Loading branch information
1 parent
aad8a75
commit 6d3dc41
Showing
15 changed files
with
437 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
DO_NOT_SEND_MAIL="True" |
36 changes: 36 additions & 0 deletions
36
cypress/e2e/signin_with_certification_dirigeant/fixtures.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
INSERT INTO users | ||
(id, email, email_verified, email_verified_at, encrypted_password, created_at, updated_at, | ||
given_name, family_name, phone_number, job, encrypted_totp_key, totp_key_verified_at, force_2fa) | ||
VALUES | ||
(1, '[email protected]', true, CURRENT_TIMESTAMP, | ||
'$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, | ||
'Jean', 'Certification', '0123456789', 'Dirigeant', | ||
null, null, false); | ||
|
||
INSERT INTO organizations | ||
(id, siret, created_at, updated_at) | ||
VALUES | ||
(1, '21340126800130', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); | ||
|
||
INSERT INTO users_organizations | ||
(user_id, organization_id, is_external, verification_type, has_been_greeted) | ||
VALUES | ||
(1, 1, false, 'domain', true); | ||
|
||
INSERT INTO oidc_clients | ||
(client_name, client_id, client_secret, redirect_uris, | ||
post_logout_redirect_uris, scope, client_uri, client_description, | ||
userinfo_signed_response_alg, id_token_signed_response_alg, | ||
authorization_signed_response_alg, introspection_signed_response_alg) | ||
VALUES | ||
('Oidc Test Client', | ||
'standard_client_id', | ||
'standard_client_secret', | ||
ARRAY [ | ||
'http://localhost:4000/login-callback' | ||
], | ||
ARRAY []::varchar[], | ||
'openid email profile organization', | ||
'http://localhost:4000/', | ||
'ProConnect test client. More info: https://github.com/numerique-gouv/proconnect-test-client.', | ||
null, null, null, null); |
40 changes: 40 additions & 0 deletions
40
cypress/e2e/signin_with_certification_dirigeant/index.cy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
describe("sign-in with a client requiring certification dirigeant", () => { | ||
beforeEach(() => { | ||
cy.visit("http://localhost:4000"); | ||
cy.setRequestedAcrs([ | ||
"https://proconnect.gouv.fr/assurance/certification-dirigeant", | ||
]); | ||
}); | ||
|
||
it("should sign-in an return the right acr value", function () { | ||
cy.get("button#custom-connection").click({ force: true }); | ||
cy.login("[email protected]"); | ||
|
||
cy.contains("Authentifier votre statut"); | ||
cy.contains("S’identifier avec").click(); | ||
|
||
cy.origin("https://fcp.integ01.dev-franceconnect.fr", () => { | ||
cy.contains("FIP1-LOW - eIDAS LOW").click(); | ||
}); | ||
cy.origin("https://fip1-low.integ01.fcp.fournisseur-d-identite.fr", () => { | ||
cy.contains("Mot de passe").click(); | ||
cy.focused().type("123"); | ||
cy.contains("Valider").click(); | ||
}); | ||
cy.origin("https://fcp.integ01.dev-franceconnect.fr", () => { | ||
cy.contains("Continuer sur FSPublic").click(); | ||
}); | ||
|
||
cy.contains("Vous allez vous connecter en tant que "); | ||
cy.contains("Angela Claire Louise DUBOIS"); | ||
|
||
cy.contains( | ||
"J'accepte que FranceConnect transmette mes données au service pour me connecter", | ||
).click(); | ||
cy.contains("Continuer").click(); | ||
|
||
cy.contains( | ||
'"acr": "https://proconnect.gouv.fr/assurance/certification-dirigeant"', | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,6 +117,12 @@ describe("sign-in with a client requiring certification dirigeant identity", () | |
cy.get("button#custom-connection").click({ force: true }); | ||
|
||
cy.login("[email protected]"); | ||
cy.contains("S’identifier avec").click(); | ||
cy.contains( | ||
"J'accepte que FranceConnect transmette mes données au service pour me connecter", | ||
).click(); | ||
cy.contains("Continuer").click(); | ||
cy.contains("Continuer").click(); | ||
|
||
cy.contains( | ||
'"acr": "https://proconnect.gouv.fr/assurance/certification-dirigeant"', | ||
|
@@ -154,7 +160,7 @@ describe("sign-in with a client requiring certification dirigeant and 2fa identi | |
}); | ||
}); | ||
|
||
describe("qign-in with a the requiring certification dirigeant and consistency-checked", () => { | ||
describe("sign-in with a client requiring certification dirigeant and consistency-checked", () => { | ||
beforeEach(() => { | ||
cy.visit("http://localhost:4000"); | ||
cy.setRequestedAcrs([ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// | ||
|
||
import type { NextFunction, Request, Response } from "express"; | ||
import crypto from "node:crypto"; | ||
import { z } from "zod"; | ||
import { csrfToken } from "../../middlewares/csrf-protection"; | ||
import getNotificationsFromRequest from "../../services/get-notifications-from-request"; | ||
|
||
// | ||
|
||
export async function getCertificationDirigeantController( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) { | ||
try { | ||
return res.render("user/certification-dirigeant", { | ||
csrfToken: csrfToken(req), | ||
pageTitle: "Certification dirigeant", | ||
}); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function postCertificationDirigeantController( | ||
_req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) { | ||
try { | ||
// return res.redirect("/users/certification-dirigeant/login-as"); | ||
const url = new URL( | ||
"https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize", | ||
); | ||
url.search = new URLSearchParams({ | ||
scope: [ | ||
"openid", | ||
"given_name", | ||
"family_name", | ||
"gender", | ||
"preferred_username", | ||
"birthdate", | ||
].join(" "), | ||
redirect_uri: `http://localhost:3000/login-callback`, | ||
response_type: "code", | ||
client_id: | ||
"211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e", | ||
state: `state${crypto.randomBytes(32).toString("hex")}`, | ||
nonce: `nonce${crypto.randomBytes(32).toString("hex")}`, | ||
}).toString(); | ||
|
||
return res.redirect(url.toString()); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
// | ||
|
||
export async function getCertificationDirigeantLoginAsController( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) { | ||
try { | ||
const body = await z.object({ code: z.string() }).parseAsync(req.query); | ||
const data = new URLSearchParams({ | ||
grant_type: "authorization_code", | ||
redirect_uri: `http://localhost:3000/login-callback`, | ||
client_id: | ||
"211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e", | ||
client_secret: | ||
"2791a731e6a59f56b6b4dd0d08c9b1f593b5f3658b9fd731cb24248e2669af4b", | ||
code: body.code, | ||
}).toString(); | ||
const responseToken = await fetch( | ||
"https://fcp.integ01.dev-franceconnect.fr/api/v1/token", | ||
{ | ||
method: "POST", | ||
headers: { "content-type": "application/x-www-form-urlencoded" }, | ||
body: data, | ||
}, | ||
); | ||
|
||
const { access_token: accessToken, id_token: _idToken } = await z | ||
.object({ | ||
access_token: z.string(), | ||
id_token: z.string(), | ||
}) | ||
.parseAsync(await responseToken.json()); | ||
|
||
const responseUserInfo = await fetch( | ||
"https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo", | ||
{ | ||
method: "GET", | ||
headers: { Authorization: `Bearer ${accessToken}` }, | ||
}, | ||
); | ||
|
||
const { given_name, family_name } = await z | ||
.object({ | ||
birthdate: z.string(), | ||
family_name: z.string(), | ||
gender: z.string(), | ||
given_name: z.string(), | ||
sub: z.string(), | ||
}) | ||
.parseAsync(await responseUserInfo.json()); | ||
|
||
// TODO(douglasduteil): handle FC logout | ||
// Should we directly logout from FC after this using the _idToken ? | ||
|
||
// TODO(douglasduteil): Redirect to another page to allow page reload / error notification | ||
// As the user can be redirected to the certification-dirigeant page and the code is onetime use only, | ||
// we should redirect to another page keeping the result of the FC userinfo request | ||
// Should we store the FranceConnect data in the session (for how long)? | ||
|
||
return res.render("user/certification-dirigeant-login-as", { | ||
csrfToken: csrfToken(req), | ||
notifications: await getNotificationsFromRequest(req), | ||
pageTitle: "Se connecter en tant que", | ||
name: `${given_name} ${family_name}`, | ||
}); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function postCertificationDirigeantLoginAsController( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) { | ||
try { | ||
const schema = z.object({ | ||
agreement: z.literal("on").optional(), | ||
}); | ||
|
||
const { agreement } = await schema.parseAsync(req.body); | ||
|
||
if (agreement !== "on") { | ||
return res.redirect( | ||
"/users/certification-dirigeant/login-as?notification=certification_franceconnect_data_transmission_agreement_required", | ||
); | ||
} | ||
|
||
// TODO(douglasduteil): get the FranceConnect data from the session | ||
// Should we alter the the database with the FranceConnect data ? | ||
// Should we store if the user already FranceConnected in the database ? | ||
req.session.__user_certified = true; | ||
|
||
// ~~Should we redirect to a "welcome" page for franceconnected users ?~~ | ||
// Should we go the organization selection page ? | ||
// return res.redirect("/users/sign-in"); | ||
// return res.redirect("/users/sign-in"); | ||
next(); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
// | ||
|
||
export async function getCertificationDirigeantRepresentingController( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) { | ||
try { | ||
const userOrganizations = [ | ||
{ | ||
id: "1", | ||
siret: "12345678901234", | ||
cached_libelle: "Organisation 1", | ||
cached_adresse: "123 rue de la paix", | ||
cached_libelle_activite_principale: "Activité principale 1", | ||
}, | ||
]; | ||
return res.render("user/select-organization", { | ||
csrfToken: csrfToken(req), | ||
illustration: "illu-password.svg", | ||
pageTitle: "Choisir une organisation", | ||
userOrganizations, | ||
}); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.