import { encode } from 'querystring' ; import { Body , Controller , Get , Post , Query , Render , Req , Res , UsePipes , ValidationPipe , } from '@nestjs/common' ; import { AppConfig } from '@fc/app' ; import { ConfigService } from '@fc/config' ; import { CryptographyService } from '@fc/cryptography' ; import { IdentityProviderAdapterEnvService } from '@fc/identity-provider-adapter-env' ; import { LoggerLevelNames , LoggerService } from '@fc/logger' ; import { IdentityProviderMetadata , IOidcIdentity , OidcSession } from '@fc/oidc' ; import { OidcClientConfig , OidcClientRoutes , OidcClientService , OidcClientSession , } from '@fc/oidc-client' ; import { ISessionService , Session , SessionNotFoundException , } from '@fc/session' ; import { AccessTokenParamsDTO } from '../dto' ; import { MockServiceProviderRoutes } from '../enums' ; import { MockServiceProviderTokenRevocationException , MockServiceProviderUserinfoException , } from '../exceptions' ; @ Controller ( ) export class MockServiceProviderController { constructor ( private readonly config , private readonly oidcClient , private readonly logger , private readonly crypto , private readonly identityProvider ) { this . logger . setContext ( this . constructor . name ) ; } @ Get ( ) @ Render ( 'index' ) async index ( todo author Hugues
     *  date ticket @ Session ( 'OidcClient' ) sessionOidc ) { const { defaultAcrValue } = this . config . get ( 'App' ) ; const [ provider ] = await this . identityProvider . getList ( ) ; const { authorizationUrl , params } = await this . getInteractionParameters ( provider ) ; await sessionOidc . set ( { idpId : provider . uid , idpState : params . state , idpNonce : params . nonce , } ) ; const response = { titleFront : 'Mock Service Provider' , params , authorizationUrl : authorizationUrl , defaultAcrValue , } ; this . logger . trace ( { route : '/' , method : 'GET' , response , } ) ; return response ; } @ Get ( MockServiceProviderRoutes . VERIFY ) @ Render ( 'login-callback' ) async getVerify ( todo author Hugues
     *  date ticket @ Session ( 'OidcClient' ) sessionOidc ) { const session = await sessionOidc . get ( ) ; const response = { ... session , accessToken : session . idpAccessToken , titleFront : 'Mock Service Provider - Login Callback' , } ; this . logger . trace ( { route : MockServiceProviderRoutes . VERIFY , method : 'GET' , name : 'MockServiceProviderRoutes.VERIFY' , response , } ) ; return response ; } @ Get ( MockServiceProviderRoutes . LOGOUT ) async logout ( @ Res ( ) res , todo author Hugues
     *  date ticket @ Session ( 'OidcClient' ) sessionOidc , @ Query ( 'post_logout_redirect_uri' ) postLogoutRedirectUri ? ) { const { idpIdToken , idpState , idpId } = await sessionOidc . get ( ) ; const endSessionUrl = await this . oidcClient . getEndSessionUrlFromProvider ( idpId , idpState , idpIdToken , postLogoutRedirectUri ) ; this . logger . trace ( { route : MockServiceProviderRoutes . LOGOUT , method : 'GET' , name : 'MockServiceProviderRoutes.LOGOUT' , redirect : endSessionUrl , } ) ; res . redirect ( endSessionUrl ) ; } @ Get ( MockServiceProviderRoutes . LOGOUT_CALLBACK ) async logoutCallback ( @ Res ( ) res ) { TODO see https this . logger . trace ( { route : MockServiceProviderRoutes . LOGOUT_CALLBACK , method : 'GET' , name : 'MockServiceProviderRoutes.LOGOUT_CALLBACK' , redirect : '/' , } ) ; return res . redirect ( '/' ) ; } @ Post ( MockServiceProviderRoutes . REVOCATION ) @ UsePipes ( new ValidationPipe ( { whitelist : true } ) ) @ Render ( 'success-revoke-token' ) async revocationToken ( @ Res ( ) res , @ Body ( ) body ) { let response ; try { TODO see https const providerUid = 'envIssuer' ; const { accessToken } = body ; await this . oidcClient . utils . revokeToken ( accessToken , providerUid ) ; response = { titleFront : 'Mock Service Provider - Token révoqué' , accessToken , } ; } catch ( e ) { this . logger . trace ( { e } , LoggerLevelNames . WARN ) ; params params if ( e . error && e . error_description ) { const redirect = `/error?error=${ e . error }&error_description=${ e . error_description }` ; this . logger . trace ( { redirect } , LoggerLevelNames . WARN ) ; return res . redirect ( redirect ) ; } throw new MockServiceProviderTokenRevocationException ( ) ; } this . logger . trace ( { route : MockServiceProviderRoutes . REVOCATION , method : 'POST' , name : 'MockServiceProviderRoutes.REVOCATION' , response , } ) ; return response ; } TODO see https @ Get ( OidcClientRoutes . OIDC_CALLBACK ) async getOidcCallback ( @ Req ( ) req , @ Res ( ) res , @ Query ( ) query , todo author Hugues
     *  date ticket @ Session ( 'OidcClient' ) sessionOidc ) { todo Todo author Stéphane/Matthieu
         *  date ticket if ( query . error ) { const errorUri = `/error?${ encode ( query ) }` ; this . logger . trace ( { query , errorUri } , LoggerLevelNames . WARN ) ; return res . redirect ( errorUri ) ; } const session = await sessionOidc . get ( ) ; if ( ! session ) { throw new SessionNotFoundException ( 'OidcClient' ) ; } const { idpId , idpNonce , idpState , interactionId } = session ; const tokenParams = { state : idpState , nonce : idpNonce , } ; const { accessToken , idToken , acr , amr } = await this . oidcClient . getTokenFromProvider ( idpId , tokenParams , req ) ; const userInfoParams = { accessToken , idpId , } ; const identity = await this . oidcClient . getUserInfosFromProvider ( userInfoParams , req ) ; todo author Arnaud
         *  date ticket const identityExchange = { idpIdentity : identity , idpAcr : acr , amr , idpAccessToken : accessToken , idpIdToken : idToken , } ; await sessionOidc . set ( { ... identityExchange } ) ; const { urlPrefix } = this . config . get < AppConfig > ( 'App' ) ; const url = `${ urlPrefix }/interaction/${ interactionId }/verify` ; this . logger . trace ( { route : OidcClientRoutes . OIDC_CALLBACK , method : 'GET' , name : 'OidcClientRoutes.OIDC_CALLBACK' , identityExchange , redirect : url , } ) ; res . redirect ( url ) ; } @ Post ( MockServiceProviderRoutes . USERINFO ) @ UsePipes ( new ValidationPipe ( { whitelist : true } ) ) @ Render ( 'login-callback' ) async retrieveUserinfo ( @ Res ( ) res , @ Body ( ) body ) { let response ; try { TODO see https const providerUid = 'envIssuer' ; const { accessToken } = body ; const idpIdentity = await this . oidcClient . utils . getUserInfo ( accessToken , providerUid ) ; response = { titleFront : 'Mock Service Provider - Userinfo' , accessToken , idpIdentity , } ; } catch ( e ) { this . logger . trace ( { e } , LoggerLevelNames . WARN ) ; params params if ( e . error && e . error_description ) { const redirect = `/error?error=${ e . error }&error_description=${ e . error_description }` ; this . logger . trace ( { redirect } , LoggerLevelNames . WARN ) ; return res . redirect ( redirect ) ; } throw new MockServiceProviderUserinfoException ( ) ; } this . logger . trace ( { route : MockServiceProviderRoutes . USERINFO , method : 'POST' , name : 'MockServiceProviderRoutes.USERINFO' , response , } ) ; return response ; } @ Get ( MockServiceProviderRoutes . ERROR ) @ Render ( 'error' ) async error ( @ Query ( ) query ) { const response = { titleFront : "Mock service provider - Erreur lors de l'authentification" , ... query , } ; this . logger . trace ( { route : MockServiceProviderRoutes . ERROR , method : 'GET' , name : 'MockServiceProviderRoutes.ERROR' , response , } ) ; return response ; } private async getInteractionParameters ( provider ) { const oidcClientConfig = this . config . get < OidcClientConfig > ( 'OidcClient' ) ; const { scope , acr , claims } = oidcClientConfig ; const { state , nonce } = await this . oidcClient . utils . buildAuthorizeParameters ( ) ; const authorizationUrl = await this . oidcClient . utils . getAuthorizeUrl ( { state , scope , idpId : provider . uid , acr_values : acr , nonce , claims , } ) ; const url = new URL ( authorizationUrl ) ; return { params : { redirect_uri : provider . client . redirect_uris [ 0 ] , client_id : provider . client . client_id , uid : provider . uid , state , scope , acr , claims , nonce , authorization_endpoint : `${ url . origin }${ url . pathname }` , } , authorizationUrl , } ; } }