4

I have an angular page using angular-oauth2-oidc with Keycloak OIDC implicit flow.

After logging in with Keycloak, it will get redirected back to the landing page. The landing page [app.component.html] will then check with allowAccess() for valid tokens to display main-nav or app-cover accordingly.

From my understanding because this validation takes a small amount of time, so while checking these tokens, the landing page will first display app-cover and then quickly change to main-nav.

This behavior seems to provide a bad user experience, since the landing page flickers for a brief second before redirecting to main-nav. Is there a way I could inject a loading page or delay while getting these validations? Is there a problem with this conditional routing that causes this issue?

Or what is the best practice when switching b/w components?

Thanks

[app.component.html]

<main-nav *ngIf="allowAccess">
  <router-outlet></router-outlet>
</main-nav>

<app-cover *ngIf="!allowAccess"></app-cover>

[app.component.ts]

import { OAuthService, JwksValidationHandler} from 'angular-oauth2-oidc';
import { authConfig } from './auth.config';
import { Component } from '@angular/core';
export * from './auth/auth.guard.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private oauthService: OAuthService) {
    this.configureWithNewConfigApi();
  }

  private configureWithNewConfigApi() {
    this.oauthService.configure(authConfig);
    this.oauthService.tokenValidationHandler = new JwksValidationHandler();
    this.oauthService.loadDiscoveryDocumentAndTryLogin();
  }

  public get allowAccess() {
    return this.oauthService.hasValidAccessToken();
  }
}

[routing module]

RouterModule.forRoot([
      { path: '', component: HomeComponent },
      { path: 'notification', component: NotificationComponent, canActivate: [AuthGuard] },
      { path: 'settings', component: SettingsComponent, canActivate: [AuthGuard] },
    ]),
marcusturewicz
  • 2,394
  • 2
  • 23
  • 38
Alan Chang
  • 149
  • 12

1 Answers1

0

I had the same issue and I searched a lot for a solution, but I didn't find any. So, I had to figure how to solve it myself in order to provide a good UX.

When the user press login in your app they will be redirected to the Keycloak SSO page. After authentication, they'll be redirected back to your app. When Keycloak redirects the user to the app it provides some query params with the URL.

https://your_redirect_url?state=...&session_state=...&code=...

These query params are only provided when the user is redirected from the SSO page. When this happens I check in the AppComponent if one of those query params exists and if the user isn't logged in yet. If they're not logged in I show an overlay on the page with a spinner until the tokens are successfully loaded.

<app-overlay *ngIf="!didLoginComplete()"></app-overlay>
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  
  constructor(private activatedRoute: ActivatedRoute, private authService: AuthService) { }
  
  didLoginComplete() {
    let completed = true;
    this.activatedRoute.queryParams.subscribe(params => {
      if (params['code'] && !this.authService.isLoggedIn()) {
        completed = false;
      }
    })
    return completed;
  }
import { Injectable } from '@angular/core';
import { AuthConfig, NullValidationHandler, OAuthService } from 'angular-oauth2-oidc';
import { environment } from 'src/environments/environment';
import { filter } from 'rxjs/operators';
import { env } from 'process';

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

  constructor(public oauthService: OAuthService) {
    this.configure();
  }

  private configure() {
    this.oauthService.configure(environment.authConfig);
    this.oauthService.setupAutomaticSilentRefresh();
    this.oauthService.setStorage(localStorage);
    this.oauthService.loadDiscoveryDocumentAndTryLogin();
  }

  public isLoggedIn() {
    return this.oauthService.hasValidIdToken() && this.oauthService.hasValidAccessToken();
  }
}

I found also an issue on GitHub on the angular-oauth2-oidc repository, but the discussion is about how to deal with it when you're using Auth Guards. You can check it from here for more details: Tokens are not set immediately after redirect #221.

Amr Saeed
  • 177
  • 2
  • 12