0

I have a problem and I wanted to ask if someone might help me out there :)

In my application have a config.json file which contains some information depending on it's stages (e.g. dev, IN, Prod ...).

{
  "queryUrl": "http://localhost:4200",
  "commandUrl": "http://localhost:8282",
  "ssoUrl": "https:someurlcode",
  "ssoRealm": "someRealm",
  "ssoClient": "dev"
}

This file I wanted to exchange after the application is build since its different for the different stages. (This works fine in the Jenkins Build)

Now I have written a Keycloak Service, where I create the token:

import { Injectable } from '@angular/core';
// @ts-ignore
import Keycloak from 'keycloak-js';
// @ts-ignore
import * as config from '../../../assets/config.json';

@Injectable({
  providedIn: 'root'
})
export class KeycloakService {

  private keycloakAuth: any;

  private ssoUrl: string;
  private ssoRealm: string;
  private ssoClient: string;

  constructor() {
      this.ssoUrl = config.ssoUrl;
      this.ssoRealm = config.ssoRealm;
      this.ssoClient = config.ssoClient;

  }

  init(): Promise<any> {
    console.log('init ' + this.ssoClient);

    return new Promise((resolve, reject) => {
      this.keycloakAuth =  Keycloak({
        url: this.ssoUrl,
        realm: this.ssoRealm,
        clientId: this.ssoClient
      });
      this.keycloakAuth.init({ onLoad: 'login-required' })
          .then(() => {
            console.log('keycloak success');
            resolve();
          })
          .catch(() => {
            console.log('keycloak error');
            reject();
          });
    });
  }
  getToken(): string {
    return this.keycloakAuth.token;
  }
}

As you may see I load the Json via an import statement. If I exchange the file now, it still uses the old file --> guess that's obvious since its taken at build time.

Next here is my http interceptor:

import {Injectable} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {KeycloakService} from './keycloak.service';
import {Observable, throwError} from 'rxjs';
import {catchError, retry} from 'rxjs/operators';
import {Router} from '@angular/router';

/**
 * Passes on the KeyCloak token
 */
@Injectable()
export class KeycloakInterceptor implements HttpInterceptor {

    constructor(private kcService: KeycloakService, private router: Router) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const authToken = this.kcService.getToken() || '';
        request = request.clone({
            setHeaders: {
                Authorization: 'Bearer ' + authToken
            }
        });
        return next.handle(request).pipe(
            retry(1),
            catchError((error: HttpErrorResponse) => {
                if (error.status !== undefined) {
                    this.router.navigate(['/error'], {queryParams: {errorCode: error.status}});
                } else {
                    return throwError(error);
                }
            })
        );
    }
}

and also some insights from the app.module.ts

   {
      provide: APP_INITIALIZER,
      useFactory: keycloakFactory,
      deps: [KeycloakService],
      multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: KeycloakInterceptor,
      multi: true
    }

....

export function keycloakFactory(keycloakService: KeycloakService) {
  return ()  => keycloakService.init();
}

I tried it a couple of times to load it in the init function etc. but always got an error like undefined etc...

Hope someone can help me...

Cheers Jack

3
  • import is not there to serve this use case. Use HTTP in a web application or the file system in a node application/build script. Commented Aug 28, 2020 at 16:05
  • True that's the actual problem, if I use http.get, the data won't be loaded before I need it in the interceptor Commented Aug 30, 2020 at 11:17
  • Then you need a non-intercepted request. Using a module mechanism to load the configuration just to bypass your interceptor is improper Commented Aug 31, 2020 at 19:28

1 Answer 1

1

Configuration.js

window.securityConfiguration = {
  security: {
    "queryUrl": "http://localhost:4200",
    "commandUrl": "http://localhost:8282",
    "ssoUrl": "https:someurlcode",
    "ssoRealm": "someRealm",
    "ssoClient": "dev"
  }
};

Then in your application

return new Promise((resolve, reject) => {
  this.keycloakAuth =  Keycloak({
    url: window.securityConfiguration.ssoUrl,
    realm: window.securityConfiguration.ssoRealm,
    clientId: window.securityConfiguration.ssoClient
  });
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the Idea, but how will your realise that in Type Script ?
I don't do type script... It's just the answer for your question above.. Maybe check how you can expose/re-use a global var in your application. Sorry I won't be able to help you in that part as I don't do Type script..
@JackJones all JavaScript is TypeScript.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.