diff --git a/README.md b/README.md index df13f232..3ea7dcdd 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Afin de configurer votre module ou votre client OpenId Connect, vous trouverez c Voici à quoi ressemble le bouton MonComptePro : -![](/assets/button-moncomptepro.svg) +![](/public/button-moncomptepro.svg) Pour intègrer ce bouton sur votre service, 3 options s’offrent à vous. diff --git a/package-lock.json b/package-lock.json index 95b50555..3c892e7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "@types/express-session": "^1.18.0", "@types/morgan": "^1.9.9", "@zootools/email-spell-checker": "^1.12.0", + "await-to-js": "^3.0.0", "axios": "^1.6.8", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", @@ -54,7 +55,7 @@ "nocache": "^4.0.0", "node-pg-migrate": "^7.6.1", "npm-run-all2": "^6.1.2", - "oidc-provider": "^8.4.6", + "oidc-provider": "^8.5.1", "pg": "^8.11.5", "qrcode": "^1.5.3", "rate-limiter-flexible": "^2.4.2", @@ -75,7 +76,7 @@ "@types/lodash-es": "^4.17.12", "@types/mocha": "^10.0.7", "@types/node": "^22.1.0", - "@types/oidc-provider": "^8.4.4", + "@types/oidc-provider": "^8.5.2", "@types/qrcode": "^1.5.5", "@types/sinonjs__fake-timers": "^8.1.5", "axe-core": "^4.8.4", @@ -1758,10 +1759,11 @@ } }, "node_modules/@types/oidc-provider": { - "version": "8.4.4", - "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-8.4.4.tgz", - "integrity": "sha512-+SlmKc4qlCJLjpw6Du/8cXw18JsPEYyQwoy+xheLkiuNsCz1mPEYI/lRXLQHvfJD9TH6+2/WDTLZQ2UUJ5G4bw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-8.5.2.tgz", + "integrity": "sha512-NiD3VG49+cRCAAe8+uZLM4onOcX8y9+cwaml8JG1qlgc98rWoCRgsnOB4Ypx+ysays5jiwzfUgT0nWyXPB/9uQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/koa": "*", "@types/node": "*" @@ -2103,6 +2105,15 @@ "node": ">=10.12.0" } }, + "node_modules/await-to-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", + "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -5394,9 +5405,10 @@ } }, "node_modules/jose": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.4.tgz", - "integrity": "sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.8.0.tgz", + "integrity": "sha512-E7CqYpL/t7MMnfGnK/eg416OsFCVUrU/Y3Vwe7QjKhu/BkS1Ms455+2xsqZQVN57/U2MHMBvEb5SrmAZWAIntA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } @@ -6511,16 +6523,17 @@ } }, "node_modules/oidc-provider": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-8.4.6.tgz", - "integrity": "sha512-liuHBXRaIjer6nPGWagrl5UjPhIZqahqLVPoYlc2WXsRR7XddwNCBUl1ks5r3Q3uCUfMdQTv1VsjmlaObdff8w==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-8.5.1.tgz", + "integrity": "sha512-Bm3EyxN68/KS76IlciJ3+4pnVtfdRWL+NghWpIF0XQbiRT1gzc6Qf/cyFmpL9yieko/jXYZ/uLHUv77jD00qww==", + "license": "MIT", "dependencies": { "@koa/cors": "^5.0.0", "@koa/router": "^12.0.1", - "debug": "^4.3.4", + "debug": "^4.3.5", "eta": "^3.4.0", "got": "^13.0.0", - "jose": "^5.2.4", + "jose": "^5.6.2", "jsesc": "^3.0.2", "koa": "^2.15.3", "nanoid": "^5.0.7", @@ -6534,9 +6547,10 @@ } }, "node_modules/oidc-provider/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -6552,7 +6566,8 @@ "node_modules/oidc-provider/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" }, "node_modules/oidc-provider/node_modules/nanoid": { "version": "5.0.7", diff --git a/package.json b/package.json index 17afdd6b..7d94b304 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@types/express-session": "^1.18.0", "@types/morgan": "^1.9.9", "@zootools/email-spell-checker": "^1.12.0", + "await-to-js": "^3.0.0", "axios": "^1.6.8", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", @@ -80,7 +81,7 @@ "nocache": "^4.0.0", "node-pg-migrate": "^7.6.1", "npm-run-all2": "^6.1.2", - "oidc-provider": "^8.4.6", + "oidc-provider": "^8.5.1", "pg": "^8.11.5", "qrcode": "^1.5.3", "rate-limiter-flexible": "^2.4.2", @@ -101,7 +102,7 @@ "@types/lodash-es": "^4.17.12", "@types/mocha": "^10.0.7", "@types/node": "^22.1.0", - "@types/oidc-provider": "^8.4.4", + "@types/oidc-provider": "^8.5.2", "@types/qrcode": "^1.5.5", "@types/sinonjs__fake-timers": "^8.1.5", "axe-core": "^4.8.4", diff --git a/scripts/create-anonymized-copy-of-database.sh b/scripts/create-anonymized-copy-of-database.sh index 21c34d2e..0ccfb730 100755 --- a/scripts/create-anonymized-copy-of-database.sh +++ b/scripts/create-anonymized-copy-of-database.sh @@ -17,6 +17,7 @@ echo "$(logPrefix) Cleaning anonymized database in correct order..." psql $DEST_DB_URL --command="DROP TABLE IF EXISTS users_organizations" psql $DEST_DB_URL --command="DROP TABLE IF EXISTS users_oidc_clients" psql $DEST_DB_URL --command="DROP TABLE IF EXISTS moderations" +psql $DEST_DB_URL --command="DROP TABLE IF EXISTS email_domains" psql $DEST_DB_URL --command="DROP TABLE IF EXISTS organizations" psql $DEST_DB_URL --command="DROP TABLE IF EXISTS users" psql $DEST_DB_URL --command="DROP TABLE IF EXISTS oidc_clients" @@ -44,8 +45,12 @@ SELECT regexp_replace(magic_link_token, '.', '*', 'g') as magic_link_token, magic_link_sent_at, email_verified_at, + regexp_replace(current_challenge, '.', '*', 'g') as current_challenge, needs_inclusionconnect_welcome_page, - needs_inclusionconnect_onboarding_help + needs_inclusionconnect_onboarding_help, + regexp_replace(encrypted_totp_key, '.', '*', 'g') as encrypted_totp_key, + totp_key_verified_at, + force_2fa FROM users" psql $SRC_DB_URL --command="ALTER TABLE tmp_users ADD PRIMARY KEY (id)" pg_dump --table=tmp_users $SRC_DB_URL | psql $DEST_DB_URL @@ -55,6 +60,9 @@ psql $SRC_DB_URL --command="DROP TABLE IF EXISTS tmp_users" echo "$(logPrefix) Creating anonymized copy of table organizations..." pg_dump --table=organizations $SRC_DB_URL | psql $DEST_DB_URL +echo "$(logPrefix) Creating anonymized copy of table email_domains..." +pg_dump --table=email_domains $SRC_DB_URL | psql $DEST_DB_URL + echo "$(logPrefix) Creating anonymized copy of table moderations..." pg_dump --table=moderations $SRC_DB_URL | psql $DEST_DB_URL @@ -72,7 +80,11 @@ SELECT post_logout_redirect_uris, scope, client_uri, - client_description + client_description, + userinfo_signed_response_alg, + id_token_signed_response_alg, + authorization_signed_response_alg, + introspection_signed_response_alg FROM oidc_clients" psql $SRC_DB_URL --command="ALTER TABLE tmp_oidc_clients ADD PRIMARY KEY (id)" pg_dump --table=tmp_oidc_clients $SRC_DB_URL | psql $DEST_DB_URL diff --git a/scripts/fixtures.sql b/scripts/fixtures.sql index 9bae2b5f..6974e00e 100644 --- a/scripts/fixtures.sql +++ b/scripts/fixtures.sql @@ -437,7 +437,9 @@ VALUES 'https://staging2.api-entreprise.v2.datapass.api.gouv.fr', 'https://staging.v2.datapass.api.gouv.fr', 'https://staging1.v2.datapass.api.gouv.fr', - 'https://staging2.v2.datapass.api.gouv.fr' + 'https://staging2.v2.datapass.api.gouv.fr', + 'https://sandbox.hubee.v2.datapass.api.gouv.fr', + 'https://staging.hubee.v2.datapass.api.gouv.fr' ], 'openid email profile phone organization organizations', 'https://datapass-development.api.gouv.fr', diff --git a/src/controllers/user/official-contact-email-verification.ts b/src/controllers/user/official-contact-email-verification.ts index 3cf9ba54..7c2c719b 100644 --- a/src/controllers/user/official-contact-email-verification.ts +++ b/src/controllers/user/official-contact-email-verification.ts @@ -1,5 +1,6 @@ +import { to } from "await-to-js"; import { NextFunction, Request, Response } from "express"; -import { z, ZodError } from "zod"; +import { z } from "zod"; import { ApiAnnuaireError, InvalidTokenError, @@ -105,23 +106,24 @@ export const postOfficialContactEmailVerificationMiddleware = async ( params: req.params, }); - await verifyOfficialContactEmailToken({ - user_id: getUserFromAuthenticatedSession(req).id, - organization_id, - token: official_contact_email_verification_token, - }); + const [error] = await to( + verifyOfficialContactEmailToken({ + user_id: getUserFromAuthenticatedSession(req).id, + organization_id, + token: official_contact_email_verification_token, + }), + ); - return next(); - } catch (error) { - if ( - req.params?.organization_id && - (error instanceof InvalidTokenError || error instanceof ZodError) - ) { + if (error instanceof InvalidTokenError) { return res.redirect( `/users/official-contact-email-verification/${req.params.organization_id}?notification=invalid_verify_email_code`, ); + } else if (error) { + return next(error); } + return next(); + } catch (error) { next(error); } }; diff --git a/src/managers/session/authenticated.ts b/src/managers/session/authenticated.ts index 4a0778ea..8e9ec20d 100644 --- a/src/managers/session/authenticated.ts +++ b/src/managers/session/authenticated.ts @@ -1,3 +1,4 @@ +import * as Sentry from "@sentry/node"; import { Request, Response } from "express"; import { Session, SessionData } from "express-session"; import { isEmpty } from "lodash-es"; @@ -122,6 +123,12 @@ export const getUserFromAuthenticatedSession = (req: Request) => { throw new UserNotLoggedInError(); } + Sentry.setUser({ + email: req.session.user.email, + id: req.session.user.id, + ip_address: req.ip, + username: `${req.session.user.given_name} ${req.session.user.family_name}`, + }); return req.session.user; }; @@ -132,6 +139,13 @@ export const updateUserInAuthenticatedSession = (req: Request, user: User) => { ) { throw new UserNotLoggedInError(); } + + Sentry.setUser({ + email: req.session.user.email, + id: req.session.user.id, + ip_address: req.ip, + username: `${req.session.user.given_name} ${req.session.user.family_name}`, + }); req.session.user = user; };