11import Keycloak from 'keycloak-js' ;
2+ import { buildRedirectSyncMeUri } from '../router' ;
23import config , { ConfigDto } from './config' ;
34
45class Auth {
@@ -10,14 +11,19 @@ class Auth {
1011 realm : cfg . keycloakRealm ,
1112 clientId : cfg . keycloakClientIdHub
1213 } ) ;
14+
15+ const auth = new Auth ( keycloak ) ;
16+
1317 keycloak . onTokenExpired = ( ) => {
14- keycloak . updateToken ( 30 ) ;
15- } ; // TODO: show notification with .catch(() => notify-user-somehow);
18+ auth . refreshToken ( 30 ) ;
19+ } ;
20+
1621 await keycloak . init ( {
1722 checkLoginIframe : false ,
1823 pkceMethod : 'S256' ,
1924 } ) ;
20- return new Auth ( keycloak ) ;
25+
26+ return auth ;
2127 }
2228
2329 private constructor ( keycloak : Keycloak ) {
@@ -29,25 +35,42 @@ class Auth {
2935 }
3036
3137 public async login ( redirectUri : string ) : Promise < void > {
32- await this . keycloak . login ( {
33- redirectUri : ( redirectUri )
34- } ) ;
38+ await this . keycloak . login ( { redirectUri } ) ;
3539 }
3640
3741 public async logout ( redirectUri ?: string ) : Promise < void > {
38- return this . keycloak . logout ( {
39- redirectUri : redirectUri
40- } ) ;
42+ await this . keycloak . logout ( { redirectUri } ) ;
4143 }
4244
4345 public async bearerToken ( ) : Promise < string | undefined > {
44- await this . keycloak . updateToken ( 10 ) ;
46+ await this . refreshToken ( 10 ) ;
4547 return this . keycloak . token ;
4648 }
4749
4850 public hasRole ( role : string ) : boolean {
4951 return this . keycloak . tokenParsed ?. realm_access ?. roles . includes ( role ) ?? false ;
5052 }
53+
54+ private async refreshToken ( minValidity ?: number , delay : number = 500 ) : Promise < void > {
55+ if ( delay > 16000 ) { // max wait time of 16 seconds before giving up
56+ console . error ( 'Auth Token refresh failed after maximum retries. Clearing tokens & logging out.' ) ;
57+ this . keycloak . clearToken ( ) ;
58+ return this . logout ( ) ;
59+ }
60+ try {
61+ await this . keycloak . updateToken ( minValidity ) ;
62+ return ; // success
63+ } catch ( err ) {
64+ if ( ! this . isAuthenticated ( ) ) {
65+ const redirectUrl = buildRedirectSyncMeUri ( ) ;
66+ return this . login ( redirectUrl ) ;
67+ } else {
68+ console . warn ( `Auth Token refresh failed, retrying in ${ delay } ms` , err ) ;
69+ await new Promise ( res => setTimeout ( res , delay ) ) ;
70+ return this . refreshToken ( minValidity , delay * 2 ) ; // exponential backoff
71+ }
72+ }
73+ }
5174}
5275
5376// this is a lazy singleton:
0 commit comments