Keycloak token refresh for Administration
MODIFY
src/app/app-init.ts
src/app/app-init.ts
import {KeycloakService} from 'keycloak-angular';
import {environment} from '../environments/environment';
+import {TokenService} from './services/token.service';
/**
* Initialize the keycloak session
* @param keycloak the keycloak service
*/
-export function initializer(keycloak: KeycloakService): () => Promise<any> {
+export function initializer(keycloak: KeycloakService, tokenService: TokenService): () => Promise<any> {
+ // Store tokens in session storage for page reload
+ tokenService.getTokens().subscribe((tokens) => {
+ console.log('set token to session storage');
+ sessionStorage.setItem('api_mgmt_keycloak_token', tokens.token);
+ sessionStorage.setItem('api_mgmt_keycloak_refresh_token', tokens.refreshToken);
+ });
+
return (): Promise<any> => keycloak.init({
config: {
url: environment.keycloakAuthUrl,
@@ -22,25 +30,13 @@ export function initializer(keycloak: KeycloakService): () => Promise<any> {
bearerExcludedUrls: ['/assets', '/clients/public']
}).then(success => {
const keycloakInstance = keycloak.getKeycloakInstance();
- storeTokensInSessionStorage(keycloakInstance.token, keycloakInstance.refreshToken);
+ tokenService.setTokens(keycloakInstance.token, keycloakInstance.refreshToken);
+
// refresh token via interval
setInterval(() => {
- keycloak.updateToken().then(() => console.log('token refreshed')).catch(() => console.error('error refreshing token'));
- // set fresh token to session storage
- storeTokensInSessionStorage(keycloakInstance.token, keycloakInstance.refreshToken);
+ keycloak.updateToken().then(() => console.log('token refreshed finished')).catch(() => console.error('error refreshing token'));
+ tokenService.setTokens(keycloakInstance.token, keycloakInstance.refreshToken);
},
Math.min((keycloakInstance.tokenParsed.exp - 60) * 1000, 4 * 60 * 1000)); // refresh token minimum every 4 minutes
});
}
-
-/**
- * Store tokens in session storage
- * @param token the token
- * @param refreshToken the refresh token
- */
-function storeTokensInSessionStorage(token, refreshToken) {
- sessionStorage.setItem('api_mgmt_keycloak_token', token);
- sessionStorage.setItem('api_mgmt_keycloak_refresh_token', refreshToken);
-}
-
-

MODIFY
src/app/app.module.ts
src/app/app.module.ts
@@ -43,6 +43,7 @@ import {ToasterModule} from 'angular2-toaster';
import {SwaggerComponent} from './components/swagger/swagger.component';
import {MenuComponent} from './components/menu/menu.component';
import {AboutComponent} from './about/about.component';
+import {TokenService} from './services/token.service';
@NgModule({
@@ -90,7 +91,7 @@ import {AboutComponent} from './about/about.component';
provide: APP_INITIALIZER,
useFactory: initializer,
multi: true,
- deps: [KeycloakService]
+ deps: [KeycloakService, TokenService]
},
{
provide: 'API_MGMT_UI_REST_URL',

MODIFY
src/app/components/admin/services/keycloak-interaction.service.ts
src/app/components/admin/services/keycloak-interaction.service.ts
@@ -5,6 +5,7 @@ import { from, Observable, of } from 'rxjs';
import { map, mergeAll, mergeMap, share, single } from 'rxjs/operators';
import UserRepresentation from 'keycloak-admin/lib/defs/userRepresentation';
import {KeycloakUser} from '../../../services/api-data.service';
+import {TokenService} from '../../../services/token.service';
@Injectable({
providedIn: 'root'
@@ -23,13 +24,23 @@ export class KeycloakInteractionService {
* @param keycloakRestUrl the keycloak rest url
*/
constructor(private keycloak: KeycloakService,
+ private tokenService: TokenService,
@Inject('KEYCLOAK_AUTH_URL') private keycloakRestUrl: string,
@Inject('API_MGTM_REALM') private apiMgmtRealm: string) {
this.kcAdminClient = new KcAdminClient({
baseUrl: this.keycloakRestUrl,
realmName: this.apiMgmtRealm
});
- this.kcAdminClient.setAccessToken(keycloak.getKeycloakInstance().token);
+ // set initial token
+ const token = keycloak.getKeycloakInstance().token;
+ this.kcAdminClient.setAccessToken(token);
+ console.log('set token to admin library');
+
+ this.tokenService.getTokens()
+ .subscribe(tokens => {
+ this.kcAdminClient.setAccessToken(tokens.token);
+ console.log('set token to admin library');
+ });
}
/**

MODIFY
src/app/components/developer/developer.component.ts
src/app/components/developer/developer.component.ts
import {Component, OnInit} from '@angular/core';
import {KeycloakService} from 'keycloak-angular';
+import {TokenService} from '../../services/token.service';
@Component({
selector: 'app-developer',
@@ -15,14 +16,13 @@ export class DeveloperComponent {
* load the keycloak roles from keycloak service
* @param keycloak the keycloak service
*/
- constructor(private keycloak: KeycloakService) {
+ constructor(private keycloak: KeycloakService, private tokenService: TokenService) {
// to enforce that the token is updated we use Number.MAX_SAFE_INTEGER here as min validity (ensure the roles are up to date)
this.keycloak.updateToken(Number.MAX_SAFE_INTEGER).then(() => {
console.log('token refreshed');
const keycloakInstance = keycloak.getKeycloakInstance();
- // store token in session storage for page reload
- sessionStorage.setItem('api_mgmt_keycloak_token', keycloakInstance.token);
- sessionStorage.setItem('api_mgmt_keycloak_refresh_token', keycloakInstance.refreshToken);
+ // set token to token service
+ tokenService.setTokens(keycloakInstance.token, keycloakInstance.refreshToken);
// get keycloak client roles of devportal
// set ts-ignore here because our own property 'devportal' is not part of the type definition of the keycloak library
// @ts-ignore

ADD
src/app/services/token.service.spec.ts
+import { TestBed } from '@angular/core/testing';
+
+import { TokenService } from './token.service';
+
+describe('TokenService', () => {
+ beforeEach(() => TestBed.configureTestingModule({}));
+
+ it('should be created', () => {
+ const service: TokenService = TestBed.get(TokenService);
+ expect(service).toBeTruthy();
+ });
+});

ADD
src/app/services/token.service.ts
+import { Injectable } from '@angular/core';
+import {Subject} from 'rxjs';
+import {Token} from '@angular/compiler';
+
+export interface Tokens {
+ token: string;
+ refreshToken: string;
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TokenService {
+
+ private tokenSubject = new Subject<Tokens>();
+
+ constructor() { }
+
+ /**
+ * Set token to subject
+ * @param token the token
+ */
+ public setTokens(token: string, refreshToken: string) {
+ console.log('got fresh tokens');
+ this.tokenSubject.next({
+ token,
+ refreshToken
+ });
+ }
+
+ /**
+ * Get token from subject
+ */
+ public getTokens() {
+ return this.tokenSubject.asObservable();
+ }
+}

GW-305: Keycloak token refresh for Administration