0

I am trying to fetch a user on the angular app initialization from my api which needs a Token provided by Keycloak.

First thing first: Why I am trying to do such things ?

I am new to Angular and web development in general. I'm developing a portal for users which are authenticated via Keycloak. A user can access to only his own information and, in order to avoid fetching user on each component, I wanted to fetch it on app initialization in a UserService to be able to share it to my all app via my service.

I'm using Angular 15.

So I tried the following: first fetch Keycloak token, then fetch the user from my api. As I understand it, I have to nest my second Observable, which is the one in charge of the fetched user in the first one which is the Keycloak one. And if I successfully retrieve my token and my user with the token, the app still launch before the return of my second Observable and I can't display user information at the first render, which is what I want.

I don't understand how to say to Angular to wait for the second Observable.

My app module

  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initializeUser,
      multi: true,
      deps: [UserService, KeycloakService]
    }
  ]
function initializeUser(userService: UserService, keycloak: KeycloakService) {
  return () => {
    return keycloak.init({
      config: {
        url: 'https://mykeycloakurl.com',
        realm: environment.authenticationRealm,
        clientId: 'dashboard-users'
      },
      initOptions: {
        onLoad: 'check-sso',
        silentCheckSsoRedirectUri:
          window.location.origin + '/assets/silent-check-sso.html'
      }
    }).then(_ => userService.init())
  }
}

My User service

@Injectable({
    providedIn: 'root',
  })
export class UserService {
    public user!: User
    constructor(public userRepository: UserRepository){}

    init(){
        
       return this.userRepository.getById((myUserId).subscribe(user=>{
            this.user= user
        })
    }
}

My repository

getById(userId: typeof uuid): Observable<User> {
    return this.http.get<User>(UserPaths.computeUserPath(userId)).pipe(
      map(user=> {
        return plainToInstance(User, user);
      })
    );
}

Already seen those topic:

But didn't found my answer in them as they are a bit too old for my angular version.

If someone can tell me what I'm doing wrong or if I simply don't use the correct pattern/angular-tool to achieve such goal (have a user available in the whole app with only one fetch), I would strongly appreciate.

EDIT: I've also tried to wrap the return of the initializeUser function in a Promise<Boolean>. Here is the code :

function initializeUser(userService: UserService, keycloak: KeycloakService) {
    return new Promise<Boolean>(resolve => 
      keycloak.init({
        config: {
          url: 'https://mykeycloakurl.com',
          realm: environment.authenticationRealm,
          clientId: 'dashboard-user'
        },
        initOptions: {
          onLoad: 'check-sso',
          silentCheckSsoRedirectUri: window.location.origin + '/assets/silent-check-sso.html'
        }
      }).then(_ => userService.init().subscribe(user=> {
        userService.user= user;
        resolve(true);
      }));
    )
}

And so the update of the service init():

init(): Observable<User>{
   return this.userRepository.getById(myUserid)
}

If it waits correctly for the user to be fetched (I thought I resolved this), it no longer redirect on Keycloak login if the authentication is expired or invalid.

0 Answers0