diff --git a/.env.sample b/.env.sample index 81c2e8e7..2bb8b60d 100644 --- a/.env.sample +++ b/.env.sample @@ -67,7 +67,6 @@ AFTER_MEETING_SURVEY_URL=https://startupdetat.typeform.com/to/CQhQfpVU ENCRYPT_SECRET=un_secret_avec_exactement_64_bit # Use OIDC auth instead of audioconf's magiclink auth -#FEATURE_OIDC=true #OIDC_PROVIDER_URL= #OIDC_CLIENT_ID= #OIDC_CLIENT_SECRET= diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 9e58a200..5e1e0d06 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -47,5 +47,6 @@ jobs: SECRET: fakesessionsecret ENCRYPT_SECRET: un_secret_avec_exactement_64_bit FEATURE_WEB_ACCESS: true + OIDC_ACR_VALUES: eidas1 DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres AFTER_MEETING_SURVEY_URL: https://startupdetat.typeform.com/to/R5uC1b0k diff --git a/config.js b/config.js index d68a3ad8..76c9e8c6 100644 --- a/config.js +++ b/config.js @@ -102,7 +102,6 @@ config.FEATURE_JOB_COMPUTE_STATS = process.env.FEATURE_JOB_COMPUTE_STATS === "tr config.FEATURE_JOB_ANONYMIZE_EMAILS = process.env.FEATURE_JOB_ANONYMIZE_EMAILS === "true" || false config.FEATURE_JOB_CALLS_STATS = process.env.FEATURE_JOB_CALLS_STATS === "true" || false config.FEATURE_WEB_ACCESS = process.env.FEATURE_WEB_ACCESS === "true" || false -config.FEATURE_OIDC = process.env.FEATURE_OIDC === "true" || false config.ANNOUNCEMENTS = process.env.ANNOUNCEMENTS ? process.env.ANNOUNCEMENTS.split("|") : [] @@ -112,6 +111,9 @@ config.STATS_EXTERNAL_DASHBOARD_URL = process.env.STATS_EXTERNAL_DASHBOARD_URL config.OIDC_PROVIDER_URL = process.env.OIDC_PROVIDER_URL config.OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID config.OIDC_CLIENT_SECRET = process.env.OIDC_CLIENT_SECRET +config.OIDC_ACR_VALUES = process.env.OIDC_ACR_VALUES +config.OIDC_ID_TOKEN_SIGNED_ALG = process.env.OIDC_ID_TOKEN_SIGNED_ALG +config.OIDC_USER_INFO_SIGNED_ALG = process.env.OIDC_USER_INFO_SIGNED_ALG config.RIZOMO_URI = process.env.RIZOMO_URI diff --git a/controllers/createConfController.js b/controllers/createConfController.js index 9bc328cf..5de8eeca 100644 --- a/controllers/createConfController.js +++ b/controllers/createConfController.js @@ -5,7 +5,6 @@ const config = require("../config") const db = require("../lib/db") const emailer = require("../lib/emailer") const format = require("../lib/format") -const magicLinkAuth = require("../lib/magicLinkAuth") const oidcAuth = require("../lib/oidcAuth") const urls = require("../urls") const { isAcceptedEmail } = require("../lib/emailChecker") @@ -45,9 +44,7 @@ const createConfWithDay = async (email, conferenceDay, userTimezoneOffset) => { } module.exports.createConf = async (req, res) => { - const confData = await (config.FEATURE_OIDC ? - oidcAuth.finishAuth(req) : - magicLinkAuth.finishAuth(req)) + const confData = await oidcAuth.finishAuth(req) const { email, durationInMinutes, conferenceDay, userTimezoneOffset } = confData @@ -146,4 +143,4 @@ module.exports.cancelConf = async (req, res) => { function shouldSendWebAccessMail(email) { return isAcceptedEmail(email, config.EMAIL_WEB_ACCESS_WHITELIST) && config.FEATURE_WEB_ACCESS -} \ No newline at end of file +} diff --git a/controllers/startAuthController.js b/controllers/userController.js similarity index 60% rename from controllers/startAuthController.js rename to controllers/userController.js index 0446421e..5a753420 100644 --- a/controllers/startAuthController.js +++ b/controllers/userController.js @@ -1,22 +1,15 @@ -const config = require("../config.js") -const magicLinkAuth = require("../lib/magicLinkAuth") const oidcAuth = require("../lib/oidcAuth") - +const urls = require("../urls") module.exports.startAuth = async (req, res) => { const userTimezoneOffset = req.body.userTimezoneOffset - const email = req.body.email const conferenceDurationInMinutes = req.body.durationInMinutes const conferenceDayString = req.body.day if (typeof conferenceDayString === 'undefined' && typeof conferenceDurationInMinutes === 'undefined') { throw new Error('Both conferenceDayString and conferenceDurationInMinutes are undefined. This should not happen.') } - const authRequest = await ( - config.FEATURE_OIDC ? - oidcAuth.startAuth(email, conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) : - magicLinkAuth.startAuth(email, conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) - ) + const authRequest = await oidcAuth.startAuth(conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) if (authRequest.error) { console.log("Error in authentication", authRequest.error) @@ -26,3 +19,15 @@ module.exports.startAuth = async (req, res) => { res.redirect(authRequest.redirectUrl) } + +module.exports.logout = async (req, res) => { + const user = req.session.user + if(!user){ + return res.redirect(urls.landing) + } + const {id_token, state} = user + req.session.destroy() + + const logoutUrl = await oidcAuth.getLogoutUrl({id_token_hint: id_token, state}) + return res.redirect(logoutUrl) +} diff --git a/index.js b/index.js index 2c383627..df510842 100644 --- a/index.js +++ b/index.js @@ -13,7 +13,7 @@ const dashboardController = require("./controllers/dashboardController") const format = require("./lib/format") const createConfController = require("./controllers/createConfController") const landingController = require("./controllers/landingController") -const startAuthController = require("./controllers/startAuthController") +const userController = require("./controllers/userController") const statusController = require("./controllers/statusController") const stats = require("./lib/stats") const urls = require("./urls") @@ -70,12 +70,14 @@ app.use(function(req, res, next){ res.locals.urls = urls res.locals.version = version res.locals.siteUrl = config.HOSTNAME_WITH_PROTOCOL + res.locals.user = req.session.user next() }) app.get(urls.landing, landingController.getLanding) -app.post(urls.startAuth, startAuthController.startAuth) +app.post(urls.startAuth, userController.startAuth) +app.post(urls.logout, userController.logout) app.get(urls.validationEmailSent, (req, res) => { res.render("validationEmailSent", { @@ -139,6 +141,8 @@ app.get(urls.faq, (req, res) => { }) }) +app.get(urls.logout, userController.logout) + app.get(urls.status, statusController.getStatus) app.use(Sentry.Handlers.errorHandler()) diff --git a/lib/magicLinkAuth.js b/lib/magicLinkAuth.js deleted file mode 100644 index 1b6b8faa..00000000 --- a/lib/magicLinkAuth.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Usage : - * const authRequest = startAuth(email, conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) - * - * If fail - * authRequest == { error: "error message"} - * - * If success - * authRequest == { redirectUrl : "" } - */ -const url = require("url") - -const config = require("../config") -const crypto = require("crypto") -const emailer = require("./emailer") -const { isAcceptedEmail, isValidEmail } = require("./emailChecker") -const urls = require("../urls") -const format = require("../lib/format") -const db = require("../lib/db") - -const generateToken = () => { - return crypto.randomBytes(256).toString("base64") -} - - -module.exports.startAuth = async (email, conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) => { - if (!isValidEmail(email)) { - return { error: "Adresse mail invalide. Avez vous bien tapé votre adresse mail ? Vous pouvez réessayer." } - } - - if (!isAcceptedEmail(email, config.EMAIL_WHITELIST)) { - return { error: "Cette adresse mail ne correspond pas à une agence de l'État. Si vous appartenez à un service de l'État mais votre adresse mail n'est pas reconnue par AudioConf, contactez-nous pour que nous la rajoutions!" } - } - - const token = generateToken() - const tokenExpirationDate = new Date() - tokenExpirationDate.setMinutes(tokenExpirationDate.getMinutes() + config.TOKEN_DURATION_IN_MINUTES) - - const validationUrl = `${config.HOSTNAME_WITH_PROTOCOL}${urls.createConf}?token=${encodeURIComponent(token)}` - - try { - await emailer.sendEmailValidationEmail(email, tokenExpirationDate, validationUrl) - } catch(err) { - console.log("Erreur sur la création de token", err) - return { error : "Une erreur interne s'est produite, nous n'avons pas pu créer votre conférence." } - } - - const redirectUrl = url.format({ - pathname: urls.validationEmailSent, - query: { - email: email - }, - }) - - try { - await db.insertToken(email, token, tokenExpirationDate, conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) - console.log(`Login token créé pour ${format.hashForLogs(email)}, il expire à ${tokenExpirationDate}`) - } catch(err) { - console.log("Error when inserting authrequest token in DB", err) - return { error: "Une erreur interne s'est produite, nous n'avons pas pu créer votre conférence." } - } - - return { redirectUrl } -} - -module.exports.finishAuth = async (req) => { - const token = req.query.token - - const confDatas = await db.getToken(token) - - const isTokenValid = confDatas.length === 1 - if (!isTokenValid) { - // todo use propoer error codes ? - return { error: "Ce lien de confirmation ne marche plus, il a expiré. Entrez votre adresse mail ci-dessous pour recommencer." } - } - - const confData = confDatas[0] - return { - email: confData.email, - durationInMinutes: confData.durationInMinutes, - conferenceDay: confData.conferenceDay, - userTimezoneOffset: confData.userTimezoneOffset, - } -} diff --git a/lib/oidcAuth.js b/lib/oidcAuth.js index 62498a63..60353598 100644 --- a/lib/oidcAuth.js +++ b/lib/oidcAuth.js @@ -12,7 +12,6 @@ const { generators, Issuer } = require("openid-client") const config = require("../config.js") const urls = require("../urls") const db = require("../lib/db") -const format = require("../lib/format") const urlCallback = urls.createConf @@ -24,14 +23,15 @@ module.exports.getClient = async () => { client_secret: config.OIDC_CLIENT_SECRET, redirect_uris: [config.HOSTNAME_WITH_PROTOCOL + urlCallback], response_types: ["code"], - // id_token_signed_response_alg (default "RS256") - // token_endpoint_auth_method (default "client_secret_basic") + id_token_signed_response_alg: config.OIDC_ID_TOKEN_SIGNED_ALG, + userinfo_signed_response_alg: config.OIDC_USER_INFO_SIGNED_ALG, + // token_endpoint_auth_method (default "client_secret_basic") }) return client } -module.exports.startAuth = async (email, conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) => { +module.exports.startAuth = async (conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) => { const client = await this.getClient() /* todo : store the code_verifier in DB. We don't use it for now. @@ -49,20 +49,21 @@ module.exports.startAuth = async (email, conferenceDurationInMinutes, conference const nonce = generators.random(128) const redirectUrl = client.authorizationUrl({ - scope: "openid", + scope: "openid uid email", state, + acr_values: config.OIDC_ACR_VALUES, /* todo add this back code_challenge, code_challenge_method: 'S256', */ nonce, - login_hint: email + // login_hint: email }) // todo write test : null nonce fails try { await db.insertOidcRequest(state, nonce, conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset) - console.log(`OIDC request créé pour ${format.hashForLogs(email)}`) + console.log(`OIDC request créé pour state ${state}`) } catch(err) { console.log("Error when inserting authrequest token in DB", err) return { error: "Une erreur interne s'est produite, nous n'avons pas pu créer votre conférence." } @@ -94,18 +95,34 @@ module.exports.finishAuth = async (req) => { return { error: "L'identification a échoué. Entrez votre adresse mail ci-dessous pour recommencer." } } - const tokenSet = await client.callback( - config.HOSTNAME_WITH_PROTOCOL + urlCallback, - params, - { - state: request.state, - nonce: request.nonce - // todo code_verifier: req.session.code_verifier - } - ) - const claims = tokenSet.claims() - const email = claims.preferred_username + let tokenSet + try { + tokenSet = await client.callback( + config.HOSTNAME_WITH_PROTOCOL + urlCallback, + params, + { + state: request.state, + nonce: request.nonce + // todo code_verifier: req.session.code_verifier + } + ) + } catch(error){ + console.error("error when requesting token from OIDC", error) + return { error: "L'identification a échoué. Entrez votre adresse mail ci-dessous pour recommencer." } + } + + let userinfo + try { + userinfo = await client.userinfo(tokenSet) + } catch(error){ + console.error("error when requesting userinfo from OIDC", error) + return { error: "L'identification a échoué. Entrez votre adresse mail ci-dessous pour recommencer." } + } + const email = userinfo.email + const user = {id_token: tokenSet.id_token, state: request.state} + req.session.user = user + return { email, durationInMinutes: request.durationInMinutes, @@ -114,3 +131,8 @@ module.exports.finishAuth = async (req) => { } } +module.exports.getLogoutUrl = async({state, id_token_hint}) => { + const client = await this.getClient() + + return client.endSessionUrl({id_token_hint,post_logout_redirect_uri: `${config.HOSTNAME_WITH_PROTOCOL}${urls.landing}`,state}) +} diff --git a/package-lock.json b/package-lock.json index 12cd1bdd..f718c3e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "conferences", - "version": "1.6.1", + "version": "1.6.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "conferences", - "version": "1.6.1", + "version": "1.6.2", "license": "MIT", "dependencies": { - "@gouvfr/dsfr": "^1.0.0-rc1.0", + "@gouvfr/dsfr": "^1.11.2", "@sentry/node": "^6.2.2", "chart.js": "^2.9.4", "connect-flash": "^0.1.1", @@ -242,11 +242,11 @@ } }, "node_modules/@gouvfr/dsfr": { - "version": "1.0.0-rc1.0", - "resolved": "https://registry.npmjs.org/@gouvfr/dsfr/-/dsfr-1.0.0-rc1.0.tgz", - "integrity": "sha512-mXfun0FzqeSgfyti6SsdPQHSpNsQBOJNkzZtvoN8KfaUGP3asupaMXe2CbRgrkV3ogqM0n/vToWjYjLzBwPxuQ==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@gouvfr/dsfr/-/dsfr-1.11.2.tgz", + "integrity": "sha512-S7idT4rCCw6M1pqxJS8y4nUSq/Rus+Kucoifmv0CrZD/eoBA34HWqWYzR9GIxvtAX/TQ1XC7Hz+54THtVzMeqQ==", "engines": { - "node": ">=12.13.0" + "node": ">=18.16.1" } }, "node_modules/@jridgewell/gen-mapping": { @@ -6266,9 +6266,9 @@ } }, "@gouvfr/dsfr": { - "version": "1.0.0-rc1.0", - "resolved": "https://registry.npmjs.org/@gouvfr/dsfr/-/dsfr-1.0.0-rc1.0.tgz", - "integrity": "sha512-mXfun0FzqeSgfyti6SsdPQHSpNsQBOJNkzZtvoN8KfaUGP3asupaMXe2CbRgrkV3ogqM0n/vToWjYjLzBwPxuQ==" + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@gouvfr/dsfr/-/dsfr-1.11.2.tgz", + "integrity": "sha512-S7idT4rCCw6M1pqxJS8y4nUSq/Rus+Kucoifmv0CrZD/eoBA34HWqWYzR9GIxvtAX/TQ1XC7Hz+54THtVzMeqQ==" }, "@jridgewell/gen-mapping": { "version": "0.3.3", diff --git a/package.json b/package.json index 85ef893c..f0e21983 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "homepage": "https://github.com/betagouv/conferences#readme", "dependencies": { - "@gouvfr/dsfr": "^1.0.0-rc1.0", + "@gouvfr/dsfr": "^1.11.2", "@sentry/node": "^6.2.2", "chart.js": "^2.9.4", "connect-flash": "^0.1.1", diff --git a/static/css/custom.css b/static/css/custom.css index 8f8ed467..65c790ac 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -10,14 +10,17 @@ margin-bottom: 56px !important; /* fr-mb-7w */ } -/* Todo : use callouts from the design system instead of home-made paragraphs ? */ +.bandeau-principal { + background-color: var(--background-contrast-grey); +} + .error-text { - color: var(--error); + color: var(--text-default-error); } .cell--info { - background-color: var(--info); + background-color: var(--background-alt-blue-france); } .notification--error { @@ -29,17 +32,11 @@ } .notification--info { - --scheme-border: var(--info); + --scheme-border: var(--background-alt-blue-france); } .notification--success { - --scheme-border: var(--success); -} - - -.fr-bg--alt { - background-color: var(--bf500); - color: var(--g100); + --scheme-border: var(--background-flat-success); } .text-center { diff --git a/test/createConfControllerTest.js b/test/createConfControllerTest.js index 8308354b..31dfa21e 100644 --- a/test/createConfControllerTest.js +++ b/test/createConfControllerTest.js @@ -6,7 +6,6 @@ const app = require("../index") const conferences = require("../lib/conferences") const db = require("../lib/db") const emailer = require("../lib/emailer") -const magicLinkAuth = require("../lib/magicLinkAuth") const oidcAuth = require("../lib/oidcAuth") const urls = require("../urls") const { encrypt } = require("../lib/crypto") @@ -20,11 +19,9 @@ describe("createConfController", function() { let insertConfStub let sendWebAccessEmailStub - let oidcFlagBackupValue let webAccessFlagBackupValue beforeEach(function(done) { - oidcFlagBackupValue = config.FEATURE_OIDC webAccessFlagBackupValue = config.FEATURE_WEB_ACCESS config.FEATURE_WEB_ACCESS = true @@ -37,7 +34,6 @@ describe("createConfController", function() { }) afterEach(function(done) { - config.FEATURE_OIDC = oidcFlagBackupValue config.FEATURE_WEB_ACCESS = webAccessFlagBackupValue createConfStub.restore() @@ -47,42 +43,10 @@ describe("createConfController", function() { done() }) - describe("using magicLinkAuth", () => { - let magicLinkFinishAuthStub - - beforeEach(function() { - config.FEATURE_OIDC = false - magicLinkFinishAuthStub = sinon.stub(magicLinkAuth, "finishAuth") - }) - - afterEach(function() { - magicLinkFinishAuthStub.restore() - }) - - it("should create conf and send email", function(done) { - shouldCreateConfAndSendEmail(done, magicLinkFinishAuthStub) - }) - - it("should redirect when finishAuth has failed", function(done) { - shouldRedirectWhenFinishAuthHasFailed(done, magicLinkFinishAuthStub) - }) - - it("should redirect when conf was not created", function(done) { - shouldRedirectWhenConfWasNotCreated(done, magicLinkFinishAuthStub) - }) - - it("should redirect when email was not sent", function(done) { - shouldRedirectWhenEmailWasNotSent(done, magicLinkFinishAuthStub) - }) - - }) - - describe("using OIDC auth", () => { let oidcFinishAuthStub beforeEach(function() { - config.FEATURE_OIDC = true oidcFinishAuthStub = sinon.stub(oidcAuth, "finishAuth") }) diff --git a/test/magicLinkAuthTest.js b/test/magicLinkAuthTest.js deleted file mode 100644 index e20228cc..00000000 --- a/test/magicLinkAuthTest.js +++ /dev/null @@ -1,139 +0,0 @@ -const { expect } = require("chai") - -const config = require("../config") -const db = require("../lib/db") -const emailer = require("../lib/emailer") -const sinon = require("sinon") -const url = require("url") -const utils = require("./utils") - -const magicLinkAuth = require("../lib/magicLinkAuth") - -describe("magicLinkAuth", function() { - let sendEmailStub - let dbStub - let EMAIL_WHITELIST_BCK - - beforeEach(function(done) { - EMAIL_WHITELIST_BCK = config.EMAIL_WHITELIST - config.EMAIL_WHITELIST = [ /.*@(.*\.|)beta\.gouv\.fr/, /.*@(.*\.|)numerique\.gouv\.fr/ ] - done() - }) - - afterEach(function(done) { - config.EMAIL_WHITELIST = EMAIL_WHITELIST_BCK - done() - }) - - describe("unit tests", () => { - beforeEach(function(done) { - sendEmailStub = sinon.stub(emailer, "sendEmailValidationEmail") - dbStub = sinon.stub(db, "insertToken") - done() - }) - - afterEach(function(done) { - sendEmailStub.restore() - dbStub.restore() - done() - }) - - it("should refuse invalid email", async function() { - const email = "bad.email" - const request = await magicLinkAuth.startAuth(email) - - expect(request).to.have.own.property("error") - sinon.assert.notCalled(sendEmailStub) - sinon.assert.notCalled(dbStub) - }) - - it("should refuse email that is not in EMAIL_WHITELIST", async function() { - const email = "bad.email@not.betagouv.fr" - const request = await magicLinkAuth.startAuth(email) - - expect(request).to.have.own.property("error") - expect(request).not.to.have.own.property("redirectUrl") - sinon.assert.notCalled(sendEmailStub) - sinon.assert.notCalled(dbStub) - }) - - it("should send email and record the auth token in db", async function() { - const email = "good.email@beta.gouv.fr" - const conferenceDayString = "2022-05-25" - const userTimezoneOffset = 120 - sendEmailStub.returns(Promise.resolve()) - dbStub.returns(Promise.resolve()) - - const request = await magicLinkAuth.startAuth( - email, - undefined, //conferenceDurationInMinutes, - conferenceDayString, - userTimezoneOffset, - ) - - expect(request).to.have.own.property("redirectUrl") - expect(request).not.to.have.own.property("error") - expect(request.redirectUrl).to.contain(email.replace("@", "%40")) - sinon.assert.calledOnce(sendEmailStub) - sinon.assert.calledWith(sendEmailStub.getCall(0), - email - ) - sinon.assert.calledOnce(dbStub) - sinon.assert.calledWith(dbStub.getCall(0), - email, - sinon.match.string, // token, - sinon.match.date, // tokenExpirationDate, - undefined, // conferenceDurationInMinutes, - conferenceDayString, - userTimezoneOffset - ) - - // check token is present in email - const magicLink = sendEmailStub.getCall(0).args[2] - const token = dbStub.getCall(0).args[1] - expect(magicLink).to.contain(encodeURIComponent(token)) - }) - }) - - describe("end to end test", () => { - beforeEach(async function() { - sendEmailStub = sinon.stub(emailer, "sendEmailValidationEmail") - // don't stub DB, use real one - await utils.reinitializeDB() - }) - - afterEach(function(done) { - sendEmailStub.restore() - done() - }) - - after(async () => { - await utils.reinitializeDB() - }) - - it("should run the whole auth flow", async () => { - const email = "good.email@beta.gouv.fr" - const conferenceDayString = "2022-05-25" - const userTimezoneOffset = 120 - sendEmailStub.returns(Promise.resolve()) - - await magicLinkAuth.startAuth( - email, - undefined, //conferenceDurationInMinutes, - conferenceDayString, - userTimezoneOffset, - ) - - // The magicLink that was emailed : - const magicLink = sendEmailStub.getCall(0).args[2] - - // Simulate : The user clicks the magic link - const fakeRequest = url.parse(magicLink, true) - const confData = await magicLinkAuth.finishAuth(fakeRequest) - - expect(confData.email).to.equal(email) - expect(confData.conferenceDay).to.equal(conferenceDayString) - expect(confData.userTimezoneOffset).to.equal(userTimezoneOffset) - }) - }) -}) diff --git a/test/oidcAuthTest.js b/test/oidcAuthTest.js index 6e8433b2..6cdafc91 100644 --- a/test/oidcAuthTest.js +++ b/test/oidcAuthTest.js @@ -24,12 +24,10 @@ describe("oidcAuth", function() { })) insertOidcRequestStub.returns(Promise.resolve()) - const email = "good.email@beta.gouv.fr" const conferenceDayString = "2022-05-25" const userTimezoneOffset = 60 await oidcAuth.startAuth( - email, undefined, //conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset, @@ -54,12 +52,10 @@ describe("oidcAuth", function() { })) insertOidcRequestStub.returns(Promise.resolve()) - const email = "good.email@beta.gouv.fr" const conferenceDayString = "2022-05-25" const userTimezoneOffset = 60 const request = await oidcAuth.startAuth( - email, undefined, //conferenceDurationInMinutes, conferenceDayString, userTimezoneOffset, @@ -68,8 +64,8 @@ describe("oidcAuth", function() { expect(request.redirectUrl).to.equal(redirectUrl) sinon.assert.calledWith(authorizationUrlStub.getCall(0), { - login_hint: email, - scope: "openid", + scope: "openid uid email", + acr_values: "eidas1", state: sinon.match.string, nonce: sinon.match.string, } diff --git a/test/startAuthControllerTest.js b/test/startAuthControllerTest.js index 2d3c9e01..1b341049 100644 --- a/test/startAuthControllerTest.js +++ b/test/startAuthControllerTest.js @@ -1,81 +1,16 @@ const app = require("../index") const chai = require("chai") -const magicLinkAuth = require("../lib/magicLinkAuth") const oidcAuth = require("../lib/oidcAuth") const sinon = require("sinon") const urls = require("../urls") -const config = require("../config") -describe("startAuthController", function() { - let featureFlagValue - - beforeEach(function(done) { - featureFlagValue = config.FEATURE_OIDC - done() - }) - - afterEach(function(done) { - config.FEATURE_OIDC = featureFlagValue - done() - }) - - describe("using magicLinkAuth", function() { - let magicLinkAuthStub - - beforeEach(function() { - magicLinkAuthStub = sinon.stub(magicLinkAuth, "startAuth") - config.FEATURE_OIDC = false - }) - - afterEach(function() { - magicLinkAuthStub.restore() - }) - - - it("should redirect to landing page if startAuth failed", function(done) { - magicLinkAuthStub.returns(Promise.resolve({ error: "something went wrong"})) - - chai.request(app) - .post(urls.startAuth) - .redirects(0) // block redirects, we don't want to test them - .type("form") - .send({ - email: "email", - day: "2020-12-09", - }) - .end((err, res) => { - res.should.redirectTo(urls.landing) - done() - }) - }) - - it("should redirect to redirectUrl if startAuth succeeded", function(done) { - const redirectUrl = "/my-redirect-url" - - magicLinkAuthStub.returns(Promise.resolve({ redirectUrl })) - - chai.request(app) - .post(urls.startAuth) - .redirects(0) // block redirects, we don't want to test them - .type("form") - .send({ - email: "me@email.com", - day: "2020-12-09", - }) - .end((err, res) => { - res.should.redirectTo(redirectUrl) - done() - }) - }) - - }) +describe("userController", function() { describe("using oidcAuth", function() { let oidcClientStub beforeEach(function() { oidcClientStub = sinon.stub(oidcAuth, "startAuth") - config.FEATURE_OIDC = true }) afterEach(function() { @@ -83,8 +18,6 @@ describe("startAuthController", function() { }) it("should redirect to landing page if startAuth failed", function(done) { - config.FEATURE_OIDC = true - oidcClientStub.returns(Promise.resolve({ error: "something went wrong"})) chai.request(app) @@ -102,7 +35,6 @@ describe("startAuthController", function() { }) it("should redirect to redirectUrl if startAuth succeeded", function(done) { - config.FEATURE_OIDC = true const redirectUrl = "/my-redirect-url" oidcClientStub.returns(Promise.resolve({ redirectUrl })) diff --git a/urls.js b/urls.js index 070b091d..7906aaa5 100644 --- a/urls.js +++ b/urls.js @@ -14,4 +14,5 @@ module.exports = { participantAction: '/dashboard/:participantId/:action', dashboard: '/dashboard', status: '/api/status', + logout: '/api/logout' } diff --git a/views/dashboard.ejs b/views/dashboard.ejs index dd6ce410..bf9cae9a 100644 --- a/views/dashboard.ejs +++ b/views/dashboard.ejs @@ -6,7 +6,7 @@ Tableau de bord de votre conférence

-
+

diff --git a/views/landing.ejs b/views/landing.ejs index 72e29630..19ccc545 100644 --- a/views/landing.ejs +++ b/views/landing.ejs @@ -1,6 +1,15 @@ <%- include('partials/header') -%>
+
+
+
+

+ Audioconf change de mode d'authentification au 27/05/2024 pour Agent Connect ! +

+
+
+
@@ -32,10 +41,6 @@ <% } %>
-
- - -
<% if(FEATURE_RESERVATIONS) { %>
@@ -61,9 +66,16 @@ -
- -
+ +
+ +

+ Qu’est-ce que AgentConnect ? +

+

@@ -79,7 +91,7 @@

-
+

Collaborez à distance facilement

diff --git a/views/partials/dark-mode-modale.ejs b/views/partials/dark-mode-modale.ejs index c8b1afe9..9c6e41f9 100644 --- a/views/partials/dark-mode-modale.ejs +++ b/views/partials/dark-mode-modale.ejs @@ -1,36 +1,61 @@ -
-
-
-
-
- -
-
-

- Paramètres d’affichage -

-
-
- Choisissez un thème pour personnaliser l’apparence - du site. -
-
- - +
+
+
+
+
+ +
+
+

Paramètres d’affichage

+
+
+ Choisissez un thème pour personnaliser l’apparence du site. +
+
+ + +
+ +
+
+
+
+
+ + +
+ +
+
-
- - +
+
+ + +
+ +
+
-
-
+
+
-
- + diff --git a/views/partials/dark-mode-switch.ejs b/views/partials/dark-mode-switch.ejs index 39ef4909..5f6e0bad 100644 --- a/views/partials/dark-mode-switch.ejs +++ b/views/partials/dark-mode-switch.ejs @@ -1 +1,7 @@ - \ No newline at end of file + diff --git a/views/partials/footer.ejs b/views/partials/footer.ejs index 0ec241f6..08f7e5c2 100644 --- a/views/partials/footer.ejs +++ b/views/partials/footer.ejs @@ -1,5 +1,5 @@ - - + + <% if (locals.rizomoURI !== undefined) { %> <% } %> diff --git a/views/partials/header.ejs b/views/partials/header.ejs index c471c9f8..e60d7fd7 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -1,5 +1,5 @@ - + @@ -12,8 +12,9 @@ <% } %> - + + @@ -52,12 +53,17 @@
@@ -71,4 +77,4 @@
- \ No newline at end of file + diff --git a/views/partials/notificationError.ejs b/views/partials/notificationError.ejs index ff5cae8a..9d23385a 100644 --- a/views/partials/notificationError.ejs +++ b/views/partials/notificationError.ejs @@ -1,4 +1,4 @@ -
+
<% if (error.message) { %>

<%= error.message %>

<% if (error.withContactLink) { %> diff --git a/views/partials/notificationInfo.ejs b/views/partials/notificationInfo.ejs index a21c025f..73358297 100644 --- a/views/partials/notificationInfo.ejs +++ b/views/partials/notificationInfo.ejs @@ -1,4 +1,4 @@ -
+

<%= info %>

diff --git a/views/partials/notificationSuccess.ejs b/views/partials/notificationSuccess.ejs index ce80fd2a..9e1faf9e 100644 --- a/views/partials/notificationSuccess.ejs +++ b/views/partials/notificationSuccess.ejs @@ -1,3 +1,3 @@ -
+

<%= success %>

diff --git a/views/partials/unsafeAnnouncementInfo.ejs b/views/partials/unsafeAnnouncementInfo.ejs index 8b4ed89e..ec3f715f 100644 --- a/views/partials/unsafeAnnouncementInfo.ejs +++ b/views/partials/unsafeAnnouncementInfo.ejs @@ -2,6 +2,6 @@ Use this template to display info messages containing HTML. WARNING : this template does not escape HTML, so be sure that no user-provided data is displayed in it, to avoid injection attacks. (google "server side template injection" to learn about it) --> -
+

<%- info %>