Keycloak SSO with Angular Universal SSR

Hi,

I have a angular project where I use Angular Universal, I integrated keycloak now but it does not work, after researching for a while I found out That Keycloak does not support Angular Universal or SSR for a fact. Bu they were all old posts I wanted to ask now if there is a way to still implement this?

Hello, I have already observed this error.
Further “googling” didn’t help me either.
It would also be great if someone could support there.
Parallel I’m trying to create a workaround myself.
I would then publish my results here.

Hey, i resolved that problem with a Custom Service. You need to create some files first you need some Service that checks for your browser version

import {Inject, Injectable, Injector, PLATFORM_ID} from '@angular/core';
import {isPlatformBrowser} from "@angular/common";
import {KeycloakService} from "keycloak-angular";

@Injectable()
export class SsrService {
  private readonly browser: boolean;
  private readonly keycloakInstance: KeycloakService | undefined;

  constructor( @Inject(PLATFORM_ID) platformId: Object, private injector: Injector) {
    this.browser = isPlatformBrowser(platformId);
    if(this.browser){
      this.keycloakInstance = this.injector.get(KeycloakService);
    }
  }

  isBrowser(): boolean{
    return this.browser;
  }

  get keycloak(): KeycloakService | undefined{
    return this.keycloakInstance;
  }
}

Now you need to modules one for your browser, one for your server.

Browser

import {APP_INITIALIZER, NgModule} from '@angular/core';
import { environment } from '../environments/environment';
import {KeycloakAngularModule, KeycloakService} from "keycloak-angular";


function initializeKeycloak(keycloak: KeycloakService): any {
  return () =>
    keycloak.init({
      config: {
        url: environment.auth_api,
        realm: environment.realm_name,
        clientId: environment.client_id
      },
      initOptions: {
        checkLoginIframe: false,
        onLoad: 'check-sso',
        silentCheckSsoRedirectUri:
          window.location.origin + '/assets/silent-check-sso.html',
      },
      bearerExcludedUrls: ['/assets', '/tinymce', '/clients/public'],
    });
}

@NgModule({
  imports: [
    KeycloakAngularModule
  ],
  providers: [{
    provide: APP_INITIALIZER,
    useFactory: initializeKeycloak,
    multi: true,
    deps: [KeycloakService],
  }]
})
export class KeycloakLoadingModule {
  constructor() {}
}

Server

import {NgModule} from '@angular/core';

@NgModule({
  imports: [],
  providers: []
})
export class KeycloakLoadingServer {
  constructor() {}
}

Duplicate your app.module.ts as browser-app.module.ts and change its name to BrowserAppModule. Import KeycloakLoadingServer Module in app.module.ts and KeycloakLoadingModule in browser-app.module.ts.

At the end in your main.ts change

function bootstrap() {
     platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));
}

to

function bootstrap() {
     platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));
}

Thats it. Everywhere you are using KeycloakService Injection now replace it with SsrService and use this.ssr.keycloak to get your Keycloak Service. Check with if(this.ssr.keycloak) if keycloak is set.

Hi @EnricoMessall ,
that function is duplicated
function bootstrap() {
** platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));**
}
can you correct this function please ?
Thanks

Hello.
A workaround solution have been proposed here :
https://stackoverflow.com/questions/75573255/angular-ssr-universal-website-not-working-with-keycloak-js-adapter-and-keycloa/76530251#76530251