11import Keycloak , {
2- KeycloakInitOptions ,
3- KeycloakTokenParsed ,
4- KeycloakConfig ,
5- } from "keycloak-js" ;
6- import StorageService from "../storage/storageService" ;
7-
8- class KeycloakService {
9- /**
10- * Used to create Keycloak object
11- */
12- private _keycloakConfig : KeycloakConfig | undefined ;
13- private kc : Keycloak | undefined ;
14- private static instance : KeycloakService ;
15- private token : string | undefined ;
16- private _tokenParsed : KeycloakTokenParsed | undefined ;
17- private timerId : any = 0 ;
18- private userData : any
19-
20- private constructor ( url : string , realm : string , clientId : string , tenantId ?: string ) {
21- this . _keycloakConfig = {
22- url : url ,
23- realm : realm ,
24- clientId : tenantId ? `${ tenantId } -${ clientId } ` : clientId ,
25- } ;
26- this . kc = new Keycloak ( this . _keycloakConfig ) ;
27- }
28-
29- /**
30- * used to call the `init` method from keycloak
31- */
32- private keycloakInitConfig : KeycloakInitOptions = {
33- onLoad : "check-sso" ,
34- silentCheckSsoRedirectUri : `${ window . location . origin } /silent-check-sso.html` ,
35- pkceMethod : "S256" ,
36- checkLoginIframe : false ,
2+ KeycloakInitOptions ,
3+ KeycloakTokenParsed ,
4+ KeycloakConfig ,
5+ } from "keycloak-js" ;
6+ import StorageService from "../storage/storageService" ;
7+
8+ class KeycloakService {
9+ /**
10+ * Used to create Keycloak object
11+ */
12+ private _keycloakConfig : KeycloakConfig | undefined ;
13+ private kc : Keycloak | undefined ;
14+ private static instance : KeycloakService ;
15+ private token : string | undefined ;
16+ private _tokenParsed : KeycloakTokenParsed | undefined ;
17+ private timerId : ReturnType < typeof setTimeout > | null = null ;
18+ private userData : any ;
19+
20+ private constructor (
21+ url : string ,
22+ realm : string ,
23+ clientId : string ,
24+ tenantId ?: string
25+ ) {
26+ this . _keycloakConfig = {
27+ url : url ,
28+ realm : realm ,
29+ clientId : tenantId ? `${ tenantId } -${ clientId } ` : clientId ,
3730 } ;
38-
39- private login ( ) : void {
40- this . kc ?. login ( ) ;
41- }
42-
43- private logout ( ) : void {
44- this . kc ?. logout ( ) ;
45- StorageService . clear ( )
31+ this . kc = new Keycloak ( this . _keycloakConfig ) ;
32+ }
33+
34+ /**
35+ * used to call the `init` method from keycloak
36+ */
37+ private keycloakInitConfig : KeycloakInitOptions = {
38+ onLoad : "check-sso" ,
39+ silentCheckSsoRedirectUri : `${ window . location . origin } /silent-check-sso.html` ,
40+ pkceMethod : "S256" ,
41+ checkLoginIframe : false ,
42+ } ;
43+
44+ private login ( ) : void {
45+ this . kc ?. login ( ) ;
46+ }
47+
48+ private logout ( ) : void {
49+ this . kc ?. logout ( ) ;
50+ StorageService . clear ( ) ;
51+ }
52+
53+ /**
54+ *
55+ * @returns Keycloak token validity in milliseconds
56+ */
57+ private getTokenExpireTime ( ) : number {
58+ const exp = this . _tokenParsed ?. exp ;
59+ const iat = this . _tokenParsed ?. iat ;
60+ if ( exp && iat ) {
61+ const toeknExpiretime =
62+ new Date ( exp * 1000 ) . valueOf ( ) - new Date ( iat * 1000 ) . valueOf ( ) ;
63+ return toeknExpiretime ;
64+ } else {
65+ return 60000 ;
4666 }
47-
48- /**
49- *
50- * @returns Keycloak token validity in milliseconds
51- */
52- private getTokenExpireTime ( ) : number {
53- const exp = this . _tokenParsed ?. exp ;
54- const iat = this . _tokenParsed ?. iat ;
55- if ( exp && iat ) {
56- const toeknExpiretime =
57- new Date ( exp * 1000 ) . valueOf ( ) - new Date ( iat * 1000 ) . valueOf ( ) ;
58- return toeknExpiretime ;
67+ }
68+
69+ public static async updateToken ( ) : Promise < string | null > {
70+ const instance = this . instance ;
71+ if ( ! instance ?. kc ) return null ;
72+
73+ try {
74+ const refreshed = await instance . kc . updateToken ( - 1 ) ;
75+
76+ if ( refreshed ) {
77+ instance . token = instance . kc . token ! ;
78+ StorageService . save ( StorageService . User . AUTH_TOKEN , instance . token ) ;
79+ return instance . token ;
5980 } else {
60- return 60000 ;
81+ return null ;
6182 }
83+ } catch ( err ) {
84+ throw "error updating token" ;
6285 }
63-
64- /**
65- * Refresh the keycloak token before expiring
66- */
67- private refreshToken ( ) : void {
68- this . timerId = setInterval ( ( ) => {
69- this . kc
70- ?. updateToken ( - 1 )
71- . then ( ( refreshed ) => {
72- if ( refreshed ) {
73- console . log ( "Token refreshed!" ) ;
74- clearInterval ( this . timerId ) ;
75- this . token = this . kc . token
76- StorageService . save ( StorageService . User . AUTH_TOKEN , this . token ! ) ;
77- this . refreshToken ( ) ;
86+ }
87+
88+ /**
89+ * Refresh the keycloak token before expiring
90+ */
91+ private refreshToken ( ) : void {
92+ if ( ! this . kc ) return ;
93+
94+ const refreshLoop = async ( ) => {
95+ try {
96+ const refreshed = await KeycloakService . updateToken ( ) ;
97+
98+ if ( ! refreshed ) {
99+ console . log ( "Token is still valid." ) ;
100+ }
101+
102+ this . token = this . kc . token ;
103+
104+ // Schedule the next refresh
105+ this . timerId = setTimeout ( refreshLoop , this . getTokenExpireTime ( ) ) ;
106+
107+ } catch ( err ) {
108+ console . error ( "Keycloak token update failed!" , err ) ;
109+ clearTimeout ( this . timerId ) ;
110+ this . logout ( ) ; // ensure logout is defined on the instance
111+ }
112+ } ;
113+
114+ refreshLoop ( ) ; // start the refresh loop
115+ }
116+ /**
117+ *
118+ * @param url - Valid keycloak url
119+ * @param realm - Valid keycloak realm
120+ * @param clientId - Valid keycloak clientId
121+ * @param tenantId - Optional - Valid tenant Id for multitenant environments
122+ * @returns KeycloakService instance
123+ */
124+ public static getInstance (
125+ url : string ,
126+ realm : string ,
127+ clientId : string ,
128+ tenantId ?: string
129+ ) : KeycloakService {
130+ return this . instance
131+ ? this . instance
132+ : ( this . instance = new KeycloakService ( url , realm , clientId , tenantId ) ) ;
133+ }
134+
135+ /**
136+ * Initialize the keycloak
137+ * make sure `silent-check-sso.html` is present in public folder
138+ * @param callback - Optional - callback function to excecute after succeessful authentication
139+ */
140+ public initKeycloak ( callback : ( authenticated ) => void = ( ) => { } ) : void {
141+ this . kc
142+ ?. init ( this . keycloakInitConfig )
143+ . then ( ( authenticated ) => {
144+ if ( authenticated ) {
145+ console . log ( "Authenticated" ) ;
146+ const tokenParsed = this . kc . tokenParsed ;
147+ if ( tokenParsed ) {
148+ const UserRoles =
149+ tokenParsed . roles || tokenParsed . role || tokenParsed . client_roles ;
150+ if ( ! UserRoles ) {
151+ callback ( false ) ;
78152 } else {
79- console . log ( "Token is still valid!" ) ;
80- }
81- } )
82- . catch ( ( err ) => {
83- console . error ( "Keycloak token update failed!" , err ) ;
84- clearInterval ( this . timerId ) ;
85- this . logout ( ) ;
86- } ) ;
87- } , this . getTokenExpireTime ( ) ) ;
88- }
89-
90- /**
91- *
92- * @param url - Valid keycloak url
93- * @param realm - Valid keycloak realm
94- * @param clientId - Valid keycloak clientId
95- * @param tenantId - Optional - Valid tenant Id for multitenant environments
96- * @returns KeycloakService instance
97- */
98- public static getInstance (
99- url : string ,
100- realm : string ,
101- clientId : string ,
102- tenantId ?: string
103- ) : KeycloakService {
104- return this . instance
105- ? this . instance
106- : ( this . instance = new KeycloakService ( url , realm , clientId , tenantId ) ) ;
107- }
108-
109- /**
110- * Initialize the keycloak
111- * make sure `silent-check-sso.html` is present in public folder
112- * @param callback - Optional - callback function to excecute after succeessful authentication
113- */
114- public initKeycloak ( callback : ( authenticated ) => void = ( ) => { } ) : void {
115- this . kc
116- ?. init ( this . keycloakInitConfig )
117- . then ( ( authenticated ) => {
118- if ( authenticated ) {
119- console . log ( "Authenticated" ) ;
120- const tokenParsed = this . kc . tokenParsed ;
121- if ( tokenParsed ) {
122- const UserRoles = tokenParsed . roles || tokenParsed . role || tokenParsed . client_roles ;
123- if ( ! UserRoles ) {
124- callback ( false ) ;
125- }
126- else {
127- StorageService . save ( StorageService . User . USER_ROLE , JSON . stringify ( UserRoles ) ) ;
153+ StorageService . save (
154+ StorageService . User . USER_ROLE ,
155+ JSON . stringify ( UserRoles )
156+ ) ;
128157 this . token = this . kc . token ;
129158 this . _tokenParsed = this . kc . tokenParsed ;
130159 StorageService . save ( StorageService . User . AUTH_TOKEN , this . token ! ) ;
@@ -137,45 +166,44 @@ import Keycloak, {
137166 callback ( true ) ;
138167 } ) ;
139168 this . refreshToken ( ) ;
140- }
141- } else {
142- this . logout ( ) ;
143169 }
144-
145170 } else {
146- console . warn ( "not authenticated!" ) ;
147- this . login ( ) ;
171+ this . logout ( ) ;
148172 }
149- } )
150- . catch ( ( err ) =>
151- console . error ( "Failed to initialize KeycloakService" , err )
152- ) ;
153- }
154- /**
155- * logs the user out and clear all user data from session.
156- */
157- public userLogout ( ) : void {
158- StorageService . clear ( ) ;
159- this . logout ( ) ;
160- }
161- /**
162- *
163- * @returns the user token
164- */
165- public getToken ( ) : string {
166- return this . token ! ;
167- }
168- /**
169- *
170- * @returns the user details
171- */
172- public getUserData ( ) :any {
173- return this . userData ;
174- }
173+ } else {
174+ console . warn ( "not authenticated!" ) ;
175+ this . login ( ) ;
176+ }
177+ } )
178+ . catch ( ( err ) =>
179+ console . error ( "Failed to initialize KeycloakService" , err )
180+ ) ;
181+ }
182+ /**
183+ * logs the user out and clear all user data from session.
184+ */
185+ public userLogout ( ) : void {
186+ StorageService . clear ( ) ;
187+ this . logout ( ) ;
188+ }
189+ /**
190+ *
191+ * @returns the user token
192+ */
193+ public getToken ( ) : string {
194+ return this . token ! ;
195+ }
196+ /**
197+ *
198+ * @returns the user details
199+ */
200+ public getUserData ( ) : any {
201+ return this . userData ;
202+ }
175203
176- public isAuthenticated ( ) : boolean {
177- return ! ! this . token ;
178- }
204+ public isAuthenticated ( ) : boolean {
205+ return ! ! this . token ;
179206 }
180-
181- export default KeycloakService ;
207+ }
208+
209+ export default KeycloakService ;
0 commit comments