Skip to content

Commit

Permalink
Ouvre route GET /auth/fcplus/creationSession
Browse files Browse the repository at this point in the history
Étapes suivantes :
- gérer le cas `idp_hint=eidas-bridge` avec variable environnement
- ajouter un lien « se connecter » sur la page d'accueil
- ajouter les nouveaux points d'entrée dans le serveur mock FC+
  - `authorization_endpoint`

Puis plus tard…
- regarder s'il ne faut pas ajouter des `return` dans les tests de `test/api/destructionSessionFCPlus.spec.js`
- factoriser adaptateurChiffrement.clefHachage(`${Math.random()}`)

Co-authored-by: Emmanuel Gaillot <[email protected]>
  • Loading branch information
Fabinout and egaillot committed Apr 30, 2024
1 parent 39608d6 commit 2925db0
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 0 deletions.
6 changes: 6 additions & 0 deletions mockFCPlus.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,19 @@ app.use(express.urlencoded({ extended: true }));

app.get('/', (_requete, reponse) => {
reponse.json({
authorization_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/debut_session`,
end_session_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/fin_session`,
jwks_uri: `${process.env.URL_BASE_MOCK_FCPLUS}/jwks`,
token_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/jeton`,
userinfo_endpoint: `${process.env.URL_BASE_MOCK_FCPLUS}/userinfo`,
});
});

app.get('/debut_session', (requete, reponse) => {
const etat = requete.params.state;
reponse.redirect(`${process.env.URL_REDIRECTION_CONNEXION}?state=${etat}&code=abcdef`);
});

app.get('/fin_session', (_requete, reponse) => {
reponse.redirect(process.env.URL_REDIRECTION_DECONNEXION);
});
Expand Down
6 changes: 6 additions & 0 deletions src/adaptateurs/adaptateurEnvironnement.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const avecEnvoiCookieSurHTTP = () => process.env.AVEC_ENVOI_COOKIE_SUR_HTTP ===

const clePriveeJWK = () => JSON.parse(atob(process.env.CLE_PRIVEE_JWK_EN_BASE64));

const identifiantClient = () => process.env.IDENTIFIANT_CLIENT_FCPLUS;

const identifiantEIDAS = () => process.env.IDENTIFIANT_EIDAS;

const parametresRequeteJeton = () => ({
Expand All @@ -16,15 +18,19 @@ const secretJetonSession = () => new TextEncoder().encode(process.env.SECRET_JET

const urlConfigurationOpenIdFCPlus = () => process.env.URL_CONFIGURATION_OPEN_ID_FCPLUS;

const urlRedirectionConnexion = () => process.env.URL_REDIRECTION_CONNEXION;

const urlRedirectionDeconnexion = () => process.env.URL_REDIRECTION_DECONNEXION;

module.exports = {
avecEnvoiCookieSurHTTP,
avecRequetePieceJustificative,
clePriveeJWK,
identifiantClient,
identifiantEIDAS,
parametresRequeteJeton,
secretJetonSession,
urlConfigurationOpenIdFCPlus,
urlRedirectionConnexion,
urlRedirectionDeconnexion,
};
4 changes: 4 additions & 0 deletions src/adaptateurs/adaptateurFranceConnectPlus.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,16 @@ const recupereInfosUtilisateurChiffrees = (jetonAcces) => configurationOpenIdFra
const recupereURLClefsPubliques = () => configurationOpenIdFranceConnectPlus
.then(({ jwks_uri: url }) => url);

const urlCreationSession = () => configurationOpenIdFranceConnectPlus
.then(({ authorization_endpoint: url }) => url);

const urlDestructionSession = () => configurationOpenIdFranceConnectPlus
.then(({ end_session_endpoint: url }) => url);

module.exports = {
recupereDonneesJetonAcces,
recupereInfosUtilisateurChiffrees,
recupereURLClefsPubliques,
urlCreationSession,
urlDestructionSession,
};
15 changes: 15 additions & 0 deletions src/api/creationSessionFCPlus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const creationSessionFCPlus = (config, requete, reponse) => {
const { adaptateurChiffrement, adaptateurEnvironnement, adaptateurFranceConnectPlus } = config;

const identifiantClient = adaptateurEnvironnement.identifiantClient();
const urlRedirectionConnexion = adaptateurEnvironnement.urlRedirectionConnexion();
const etat = adaptateurChiffrement.cleHachage(`${Math.random()}`);
const nonce = adaptateurChiffrement.cleHachage(`${Math.random()}`);

return adaptateurFranceConnectPlus.urlCreationSession()
.then((url) => reponse.redirect(
`${url}?scope=profile%20openid&acr_values=eidas2&claims={%22id_token%22:{%22amr%22:{%22essential%22:true}}}&prompt=login%20consent&response_type=code&client_id=${identifiantClient}&redirect_uri=${urlRedirectionConnexion}&state=${etat}&nonce=${nonce}`,
));
};

module.exports = creationSessionFCPlus;
13 changes: 13 additions & 0 deletions src/routes/routesAuth.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const express = require('express');

const connexionFCPlus = require('../api/connexionFCPlus');
const deconnexionFCPlus = require('../api/deconnexionFCPlus');
const creationSessionFCPlus = require('../api/creationSessionFCPlus');
const destructionSessionFCPlus = require('../api/destructionSessionFCPlus');

const routesAuth = (config) => {
Expand Down Expand Up @@ -66,6 +67,18 @@ const routesAuth = (config) => {
)
));

routes.get('/fcplus/creationSession', (requete, reponse) => (
creationSessionFCPlus(
{
adaptateurChiffrement,
adaptateurEnvironnement,
adaptateurFranceConnectPlus,
},
requete,
reponse,
)
));

return routes;
};

Expand Down
1 change: 1 addition & 0 deletions src/vues/pageAccueil.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
{{/infosUtilisateur}}
{{^infosUtilisateur}}
<p>Pas d'utilisateur courant</p>
<a href="/auth/fcplus/creationSession">Connexion</a>
{{/infosUtilisateur}}
110 changes: 110 additions & 0 deletions test/api/creationSessionFCPlus.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const creationSessionFCPlus = require('../../src/api/creationSessionFCPlus');

describe('Le requêteur de création de session FC+', () => {
const adaptateurChiffrement = {};
const adaptateurEnvironnement = {};
const adaptateurFranceConnectPlus = {};
const config = { adaptateurChiffrement, adaptateurEnvironnement, adaptateurFranceConnectPlus };
const reponse = {};

const requete = {};

beforeEach(() => {
adaptateurChiffrement.cleHachage = () => '';
adaptateurEnvironnement.identifiantClient = () => '';
adaptateurEnvironnement.urlRedirectionConnexion = () => '';
adaptateurFranceConnectPlus.urlCreationSession = () => Promise.resolve('');
});

it('redirige vers serveur France Connect Plus', () => {
expect.assertions(1);
adaptateurFranceConnectPlus.urlCreationSession = () => Promise.resolve('http://example.com');

reponse.redirect = (url) => {
try {
expect(url).toMatch(/^http:\/\/example\.com\?/);
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
};

return creationSessionFCPlus(config, requete, reponse);
});

it('ajoute des paramètres à la requête', () => {
expect.assertions(5);

reponse.redirect = (url) => {
try {
expect(url).toContain('scope=profile%20openid');
expect(url).toContain('acr_values=eidas2');
expect(url).toContain('claims={%22id_token%22:{%22amr%22:{%22essential%22:true}}}');
expect(url).toContain('prompt=login%20consent');
expect(url).toContain('response_type=code');

return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
};

return creationSessionFCPlus(config, requete, reponse);
});

it("ajoute l'identifiant client FC+ en paramètre", () => {
expect.assertions(1);

adaptateurEnvironnement.identifiantClient = () => '12345';

reponse.redirect = (url) => {
try {
expect(url).toContain('client_id=12345');
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
};

return creationSessionFCPlus(config, requete, reponse);
});

it("ajoute l'URL de redirection post-login en paramètre", () => {
expect.assertions(1);

adaptateurEnvironnement.urlRedirectionConnexion = () => 'http://example.com';

reponse.redirect = (url) => {
try {
expect(url).toContain('redirect_uri=http://example.com');
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
};

return creationSessionFCPlus(config, requete, reponse);
});

it('ajoute un état et un nonce en paramètres de la requête', () => {
expect.assertions(2);
let nbClesGenerees = 0;

adaptateurChiffrement.cleHachage = () => {
nbClesGenerees += 1;
return `12345-${nbClesGenerees}`;
};

reponse.redirect = (url) => {
try {
expect(url).toContain('state=12345-1');
expect(url).toContain('nonce=12345-2');
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
};

return creationSessionFCPlus(config, requete, reponse);
});
});
12 changes: 12 additions & 0 deletions test/routes/routesAuth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,16 @@ describe('Le serveur des routes `/auth`', () => {
.catch(leveErreur);
});
});

describe('sur GET /auth/fcplus/creationSession', () => {
it("redirige vers l'URL (FC+) de création de session", () => {
serveur.adaptateurFranceConnectPlus().urlCreationSession = () => Promise.resolve(
`http://localhost:${port}/redirectionConnexion`, // page inexistante, résultera en une erreur HTTP 404
);

return axios.get(`http://localhost:${port}/auth/fcplus/creationSession`)
.catch((e) => expect(e.response.request.path).toContain('/redirectionConnexion'))
.catch(leveErreur);
});
});
});
2 changes: 2 additions & 0 deletions test/routes/serveurTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ const serveurTest = () => {
adaptateurEnvironnement = {
avecEnvoiCookieSurHTTP: () => true,
avecRequetePieceJustificative: () => true,
identifiantClient: () => '',
identifiantEIDAS: () => 'FR/BE/123456789',
secretJetonSession: () => 'secret',
urlRedirectionConnexion: () => '',
urlRedirectionDeconnexion: () => '',
};

Expand Down

0 comments on commit 2925db0

Please sign in to comment.