11import { Component , OnInit , AfterViewInit , Renderer2 } from '@angular/core'
22import { getThemeConfig } from '@geonetwork-ui/util/app-config'
33import { ThemeService } from '@geonetwork-ui/util/shared'
4+ import Keycloak from 'keycloak-js'
45
56@Component ( {
67 selector : 'datahub-root' ,
@@ -9,7 +10,7 @@ import { ThemeService } from '@geonetwork-ui/util/shared'
910 standalone : false ,
1011} )
1112export class AppComponent implements OnInit , AfterViewInit {
12- constructor ( public renderer : Renderer2 ) { }
13+ constructor ( public renderer : Renderer2 ) { }
1314
1415 ngOnInit ( ) : void {
1516 const favicon = getThemeConfig ( ) . FAVICON
@@ -27,5 +28,116 @@ export class AppComponent implements OnInit, AfterViewInit {
2728 this . renderer . removeClass ( title , 'fr-badge--green-emeraude' )
2829 this . renderer . insertBefore ( title , spanBadge , title . firstChild )
2930 this . renderer . addClass ( title , 'fr-badge--blue-cumulus' )
31+ this . keycloakCheckAuth ( )
32+ }
33+
34+ keycloakCheckAuth ( ) : void {
35+ const authContainer = document . getElementById ( 'header-auth' )
36+ if ( ! authContainer ) return
37+
38+ const renderLoggedOut = ( ) => {
39+ document . querySelectorAll ( '.login-btn' ) . forEach ( ( btn ) => {
40+ btn . addEventListener ( 'click' , ( ) => {
41+ keycloak . login ( { redirectUri : window . location . href } )
42+ } )
43+ } )
44+ }
45+
46+ const renderLoggedIn = async ( ) => {
47+ const claims = keycloak . idTokenParsed || keycloak . tokenParsed || { }
48+
49+ const displayName =
50+ ( typeof claims . name === 'string' && claims . name ) ||
51+ [ claims . given_name , claims . family_name ] . filter ( Boolean ) . join ( ' ' ) ||
52+ ( typeof claims . preferred_username === 'string' &&
53+ claims . preferred_username ) ||
54+ ( typeof claims . email === 'string' && claims . email ) ||
55+ 'Compte'
56+
57+ const generateUserMenuHTML = ( collapseId ) => {
58+ const currentUrl = encodeURIComponent ( window . location . href )
59+ return `
60+ <li>
61+ <div class="fr-translate fr-nav">
62+ <div class="fr-nav__item">
63+ <button aria-controls="${ collapseId } " aria-expanded="false" title="Mon espace" class="fr-nav__btn fr-btn fr-px-2w">
64+ <span class="fr-icon-account-circle-fill fr-icon--sm fr-mr-1w" aria-hidden="true"></span>Mon espace</button>
65+ <div class="fr-collapse fr-translate__menu fr-menu" id="${ collapseId } ">
66+ <ul class="fr-menu__list">
67+ <li style="pointer-events: none;">
68+ <div class="fr-text--sm">
69+ <p class="custom-center-btn fr-text--bold fr-mx-2w fr-text--sm fr-mt-3v fr-mb-2v">${ displayName } </p>
70+ <p class="fr-text--xs fr-mb-3v fr-mx-2w fr-text-mention--grey" style="text-align: left;">${ claims . email } </p>
71+ </div>
72+ </li>
73+ <li>
74+ <a class="fr-nav__link fr-mr-3w" href="https://cartes.gouv.fr/tableau-de-bord">
75+ <span class="fr-icon-dashboard-3-line fr-icon--sm"> Tableau de bord</span></a>
76+ </li>
77+ <li>
78+ <a class="fr-nav__link fr-mr-3w" href="https://cartes.gouv.fr/mon-compte">
79+ <span class="fr-icon-user-line fr-icon--sm"> Mon compte</span></a>
80+ </li>
81+ <li>
82+ <div>
83+ <a href="https://sso.geopf.fr/realms/geoplateforme/protocol/openid-connect/logout?post_logout_redirect_uri=${ currentUrl } &client_id=cartes-gouv-public"
84+ class="fr-icon-logout-box-r-line fr-icon--sm custom-center-btn fr-btn fr-btn--tertiary fr-btn--sm fr-mt-3v fr-mx-2w">
85+ Se déconnecter
86+ </a>
87+ </div>
88+ </li>
89+ </ul>
90+ </div>
91+ </div>
92+ </div>
93+ </li>
94+ `
95+ }
96+
97+ authContainer . innerHTML = generateUserMenuHTML ( 'espace-collapse' )
98+
99+ const authContainerMobile = document . getElementById ( 'header-auth-mobile' )
100+ if ( authContainerMobile ) {
101+ authContainerMobile . innerHTML = generateUserMenuHTML (
102+ 'espace-collapse-mobile'
103+ )
104+ }
105+ }
106+
107+ const keycloak = new Keycloak ( {
108+ url : 'https://sso.geopf.fr' ,
109+ realm : 'geoplateforme' ,
110+ clientId : 'cartes-gouv-public' ,
111+ } )
112+
113+ // "Authorization Code" flow avec PKCE (type de client Keycloak : Public).
114+ keycloak
115+ . init ( {
116+ onLoad : 'check-sso' ,
117+ flow : 'standard' ,
118+ pkceMethod : 'S256' ,
119+ checkLoginIframe : false ,
120+ silentCheckSsoRedirectUri : `${ window . location . origin } /silent-check-sso.html` ,
121+ } )
122+ . then ( async ( authenticated ) => {
123+ if ( ! authenticated ) {
124+ renderLoggedOut ( )
125+ return
126+ }
127+
128+ await renderLoggedIn ( )
129+
130+ // Si on veut garder le token à jour pour d'éventuelles futures appels API :
131+ // window.setInterval(() => {
132+ // keycloak.updateToken(60).catch(() => {
133+ // // Si le rafraîchissement échoue, on affiche simplement l'interface déconnectée.
134+ // renderLoggedOut();
135+ // });
136+ // }, 30_000);
137+ } )
138+ . catch ( ( error ) => {
139+ console . error ( 'Failed to initialize Keycloak' , error )
140+ renderLoggedOut ( )
141+ } )
30142 }
31143}
0 commit comments