Skip to content

Commit

Permalink
feat: acr can be used to force identity with IAL=2
Browse files Browse the repository at this point in the history
  • Loading branch information
rdubigny committed Oct 18, 2024
1 parent bc4c527 commit 2057625
Show file tree
Hide file tree
Showing 20 changed files with 519 additions and 46 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/end-to-end.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
- signin_from_standard_client
- signin_with_email_verification_renewal
- signin_with_magic_link
- signin_with_right_acr
- signin_with_totp
- signup_entreprise_unipersonnelle
- update_personal_information
Expand All @@ -67,6 +68,7 @@ jobs:
MCP_CLIENT_SECRET: standard_client_secret
MCP_PROVIDER: ${{ env.MONCOMPTEPRO_HOST }}
MCP_SCOPES: openid email profile organization
ACR_VALUE_FOR_2FA: urn:dinum:ac:classes:consistency-checked-2fa
STYLESHEET_URL: ""
moncomptepro-proconnect-federation-client:
image: ghcr.io/numerique-gouv/moncomptepro-test-client
Expand Down Expand Up @@ -95,6 +97,19 @@ jobs:
MCP_PROVIDER: ${{ env.MONCOMPTEPRO_HOST }}
MCP_SCOPES: openid email profile phone organizations
STYLESHEET_URL: ""
moncomptepro-acr-client:
image: ghcr.io/numerique-gouv/moncomptepro-test-client
ports:
- 4003:3000
env:
SITE_TITLE: moncomptepro-acr-client
HOST: http://localhost:4003
MCP_CLIENT_ID: acr_client_id
MCP_CLIENT_SECRET: acr_client_secret
MCP_PROVIDER: ${{ env.MONCOMPTEPRO_HOST }}
MCP_SCOPES: openid email profile organization
ACR_VALUE_FOR_2FA: urn:dinum:ac:classes:consistency-checked
STYLESHEET_URL: ""
redis:
image: redis:7.2
ports:
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,21 @@ d’usurpations d’identités liés aux attaques par _phishing_ par exemple.

Vous pouvez tester la cinématique via le lien suivant : https://test.moncomptepro.beta.gouv.fr/#force-2fa

Pour ce faire, vous devez passer les paramètres `claims={"id_token":{"acr":{"essential":true,value:"https://refeds.org/profile/mfa"}}}` comme suit :
Pour ce faire, vous devez passer les paramètres `claims={"id_token":{"acr":{"essential":true,value:"urn:dinum:ac:classes:consistency-checked-2fa"}}}` comme suit :

https://app-sandbox.moncomptepro.beta.gouv.fr/oauth/authorize?client_id=client_id&scope=openid%20email%20profile%20organization&response_type=code&redirect_uri=https%3A%2F%2Ftest.moncomptepro.beta.gouv.fr%2Flogin-callback&claims=%7B%22id_token%22%3A%7B%22acr%22%3A%7B%22essential%22%3Atrue%2C%22value%22%3A%22https%3A%2F%2Frefeds.org%2Fprofile%2Fmfa%22%7D%7D%7D

Les valeurs `acr` utilisées par ProConnect Identité sont les suivantes :

- `eidas1` authentification simple facteur avec une identité de niveau faible.
- `https://refeds.org/profile/mfa` authentification par double facteur sans preuve d’identité particulière.
- `eidas1` authentification simple facteur avec une identité de niveau faible ;
- `urn:dinum:ac:classes:self-asserted` : identité déclarative ;
- `urn:dinum:ac:classes:self-asserted-2fa` : identité déclarative ;
- `urn:dinum:ac:classes:consistency-checked` : identité déclarative + un des tests de cohérence suivant :
- contrôle du référencement du nom de domaine
- code à usage unique envoyé par courrier postal au siège social
- code à usage unique envoyé par email à l'adresse de contact référencée dans un annuaire de référence
- identité du dirigeant d'association conforme
- `urn:dinum:ac:classes:consistency-checked-2fa` : `urn:dinum:ac:classes:consistency-checked` + authentification à double facteur

## 3. 👋 Contribuer à ProConnect Identité

Expand Down
1 change: 1 addition & 0 deletions cypress/e2e/signin_with_right_acr/env.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DO_NOT_SEND_MAIL="True"
57 changes: 57 additions & 0 deletions cypress/e2e/signin_with_right_acr/fixtures.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
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', 'IAL2 AAL2', '0123456789', 'Sbire',
'kuOSXGk68H2B3pYnph0uyXAHrmpbWaWyX/iX49xVaUc=.VMPBZSO+eAng7mjS.cI2kRY9rwhXchcKiiaMZIg==',
CURRENT_TIMESTAMP, false
),
(2, '[email protected]', true, CURRENT_TIMESTAMP,
'$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
'Jean', 'IAL1 AAL2', '0123456789', 'Sbire',
'kuOSXGk68H2B3pYnph0uyXAHrmpbWaWyX/iX49xVaUc=.VMPBZSO+eAng7mjS.cI2kRY9rwhXchcKiiaMZIg==',
CURRENT_TIMESTAMP, false
),
(3, '[email protected]', true, CURRENT_TIMESTAMP,
'$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
'Jean', 'IAL2 AAL1', '0123456789', 'Sbire',
null, null, false),
(4, '[email protected]', true, CURRENT_TIMESTAMP,
'$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
'Jean', 'IAL1 AAL1', '0123456789', 'Sbire',
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),
(2, 1, false, null, true),
(3, 1, false, 'domain', true),
(4, 1, false, null, 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',
'acr_client_id',
'acr_client_secret',
ARRAY [
'http://localhost:4003/login-callback'
],
ARRAY [
'http://localhost:4003/'
],
'openid email profile organization',
'http://localhost:4003/',
'MonComptePro test client. More info: https://github.com/numerique-gouv/moncomptepro-test-client.',
null, 'RS256', null, null);
31 changes: 31 additions & 0 deletions cypress/e2e/signin_with_right_acr/index.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//

describe("sign-in with a client requiring consistency-checked identity", () => {
it("should sign-in an return the right acr value", function () {
cy.visit("http://localhost:4003");
cy.get("button#force-2fa").click();

cy.login("[email protected]");

cy.contains('"acr": "urn:dinum:ac:classes:consistency-checked"');
});
it("should return an error with ial1", function () {
cy.visit("http://localhost:4003");
cy.get("button#force-2fa").click();

cy.login("[email protected]");

cy.contains("access_denied (none of the requested ACRs could be obtained)");
});

// TODO add tests:
// - log with a client requiring consistency-checked and consistency-checked-mfa
// - with a consistency checked user and MFA => see the right acr returned
// - with a self-asserted user and MFA => see an error
// - log with a client not requiring any acr
// - with a self-asserted user => see acr self-asserted
// - with a consistency checked user => see acr consistency-checked
// - log with acr_values=eidas1 and ENABLE_FIXED_ACR=True
// - with all type of acr => see the right acr
// these tests required the mcp-test-client to be modifiable like fc-mock
});
1 change: 0 additions & 1 deletion cypress/e2e/signin_with_totp/env.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
FEATURE_AUTHENTICATE_BROWSER=True
FEATURE_SEND_MAIL=False
FEATURE_RATE_LIMIT=True
8 changes: 7 additions & 1 deletion cypress/e2e/signin_with_totp/fixtures.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ VALUES
'kuOSXGk68H2B3pYnph0uyXAHrmpbWaWyX/iX49xVaUc=.VMPBZSO+eAng7mjS.cI2kRY9rwhXchcKiiaMZIg==',
CURRENT_TIMESTAMP, true
),
(2, 'unused2@yopmail.com', true, CURRENT_TIMESTAMP,
(2, '181eb568-ca3d-4995-8b06-a717a83421fd@mailslurp.com', true, CURRENT_TIMESTAMP,
'$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
'Jean', 'Jean', '0123456789', 'Sbire',
'kuOSXGk68H2B3pYnph0uyXAHrmpbWaWyX/iX49xVaUc=.VMPBZSO+eAng7mjS.cI2kRY9rwhXchcKiiaMZIg==',
CURRENT_TIMESTAMP, false
),
(3, '[email protected]', true, CURRENT_TIMESTAMP,
'$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
'Jean', 'Jean', '0123456789', 'Sbire',
'kuOSXGk68H2B3pYnph0uyXAHrmpbWaWyX/iX49xVaUc=.VMPBZSO+eAng7mjS.cI2kRY9rwhXchcKiiaMZIg==',
CURRENT_TIMESTAMP, true
);

INSERT INTO organizations
Expand Down
70 changes: 65 additions & 5 deletions cypress/e2e/signin_with_totp/index.cy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { getVerificationCodeFromEmail } from "#cypress/support/get-from-email";

describe("sign-in with TOTP on untrusted browser", () => {
beforeEach(() => {
cy.mailslurp().then((mailslurp) =>
mailslurp.inboxController.deleteAllInboxEmails({
inboxId: "181eb568-ca3d-4995-8b06-a717a83421fd",
}),
);
});
it("should sign-in with password and TOTP", function () {
cy.visit("http://localhost:4000");
cy.get("button.proconnect-button").click();
Expand All @@ -12,26 +21,77 @@ describe("sign-in with TOTP on untrusted browser", () => {
cy.visit("http://localhost:4000");
cy.get("button.proconnect-button").click();

cy.login("[email protected]");
cy.login("[email protected]");

cy.contains(
"Information : pour garantir la sécurité de votre compte, nous avons besoin d’authentifier votre navigateur.",
);

cy.contains("Vérifier votre email");
cy.mailslurp()
.then((mailslurp) =>
mailslurp.waitForLatestEmail(
"181eb568-ca3d-4995-8b06-a717a83421fd",
60000,
true,
),
)
.then(getVerificationCodeFromEmail)
// fill out the verification form and submit
.then((code) => {
cy.get('[name="verify_email_token"]').type(code);
cy.get('[type="submit"]').click();
});

cy.contains("moncomptepro-standard-client");
});

it("should sign-in with password and TOTP when forced by SP", function () {
cy.visit("http://localhost:4000");
cy.get("button#force-2fa").click();

cy.mfaLogin("[email protected]");
cy.mfaLogin("[email protected]");

cy.contains('"amr": [\n "pwd",\n "totp",\n "mfa"\n ],');
});

it("should only show totp step when already logged", function () {
cy.visit("http://localhost:4000");
cy.get("button.proconnect-button").click();

cy.login("[email protected]");

cy.mailslurp()
.then((mailslurp) =>
mailslurp.waitForLatestEmail(
"181eb568-ca3d-4995-8b06-a717a83421fd",
60000,
true,
),
)
.then(getVerificationCodeFromEmail)
// fill out the verification form and submit
.then((code) => {
cy.get('[name="verify_email_token"]').type(code);
cy.get('[type="submit"]').click();
});

cy.contains("moncomptepro-standard-client");

cy.get("button#force-2fa").click();

cy.contains("Valider en deux étapes");

cy.fillTotpFields();

cy.contains('"amr": [\n "pwd",\n "totp",\n "mfa"\n ],');
});

it("should trigger totp rate limiting", function () {
cy.visit("/users/start-sign-in");

cy.login("unused1@yopmail.com");
cy.login("unused3@yopmail.com");

for (let i = 0; i < 4; i++) {
for (let i = 0; i < 5; i++) {
cy.get("[name=totpToken]").type("123456");
cy.get(
'[action="/users/2fa-sign-in-with-authenticator-app"] [type="submit"]',
Expand Down
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ services:
MCP_CLIENT_SECRET: standard_client_secret
MCP_PROVIDER: http://localhost:3000
MCP_SCOPES: openid email profile organization
ACR_VALUE_FOR_2FA: "urn:dinum:ac:classes:consistency-checked-2fa"
STYLESHEET_URL:
network_mode: "host"

Expand Down Expand Up @@ -67,6 +68,20 @@ services:
STYLESHEET_URL:
network_mode: "host"

moncomptepro-acr-client:
image: ghcr.io/numerique-gouv/moncomptepro-test-client
environment:
PORT: 4003
SITE_TITLE: moncomptepro-acr-client
HOST: http://localhost:4003
MCP_CLIENT_ID: acr_client_id
MCP_CLIENT_SECRET: acr_client_secret
MCP_PROVIDER: http://localhost:3000
MCP_SCOPES: openid email profile organization
ACR_VALUE_FOR_2FA: urn:dinum:ac:classes:consistency-checked
STYLESHEET_URL:
network_mode: "host"

maildev:
image: soulteary/maildev
network_mode: "host"
Expand Down
4 changes: 4 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export const {
ACCESS_LOG_PATH,
API_AUTH_PASSWORD,
API_AUTH_USERNAME,
ACR_VALUE_FOR_IAL1_AAL1,
ACR_VALUE_FOR_IAL1_AAL2,
ACR_VALUE_FOR_IAL2_AAL1,
ACR_VALUE_FOR_IAL2_AAL2,
BREVO_API_KEY,
CRISP_BASE_URL,
CRISP_IDENTIFIER,
Expand Down
12 changes: 12 additions & 0 deletions src/config/env.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ export const secretEnvSchema = z.object({

export const paramsEnvSchema = z.object({
ACCESS_LOG_PATH: z.string().optional(),
ACR_VALUE_FOR_IAL1_AAL1: z
.string()
.default("urn:dinum:ac:classes:self-asserted"),
ACR_VALUE_FOR_IAL1_AAL2: z
.string()
.default("urn:dinum:ac:classes:self-asserted-2fa"),
ACR_VALUE_FOR_IAL2_AAL1: z
.string()
.default("urn:dinum:ac:classes:consistency-checked"),
ACR_VALUE_FOR_IAL2_AAL2: z
.string()
.default("urn:dinum:ac:classes:consistency-checked-2fa"),
DEPLOY_ENV: z.enum(["preview", "production", "sandbox"]).default("preview"),
DIRTY_DS_REDIRECTION_URL: z
.string()
Expand Down
14 changes: 13 additions & 1 deletion src/config/oidc-provider-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import epochTime from "../services/epoch-time";
import { findAccount } from "../services/oidc-account-adapter";
import policy from "../services/oidc-policy";
import { renderWithEjsLayout } from "../services/renderer";
import {
ACR_VALUE_FOR_IAL1_AAL1,
ACR_VALUE_FOR_IAL1_AAL2,
ACR_VALUE_FOR_IAL2_AAL1,
ACR_VALUE_FOR_IAL2_AAL2,
} from "./env";

//

Expand All @@ -13,7 +19,13 @@ export const oidcProviderConfiguration = ({
shortTokenTtlInSeconds = 10 * 60,
tokenTtlInSeconds = 60 * 60,
}): Configuration => ({
acrValues: ["eidas1", "https://refeds.org/profile/mfa"],
acrValues: [
"eidas1",
ACR_VALUE_FOR_IAL1_AAL1,
ACR_VALUE_FOR_IAL1_AAL2,
ACR_VALUE_FOR_IAL2_AAL1,
ACR_VALUE_FOR_IAL2_AAL2,
],
claims: {
amr: null,
// claims definitions can be found here: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
Expand Down
Loading

0 comments on commit 2057625

Please sign in to comment.