keycloak-angular: Infinite Loop with url params after redirection from keycloak server

Versions.

The versions for the keycloak angular and the keycloak-js are given below.

Angular version - 8.2.4 keycloak-angular - 7.2.0 keycloak-js - 10.0.1

Repro steps.

After configuring the keycloak as given in the documentation, the site is redirecting to the keycloak page and shows login page. But once logged in, it redirects back to the application and causes infinite redirection loop with url parameters. The redirection flow is given below.

  1. The angular application redirects to keycloak.
  2. Keycloak after authentication redirects back to angular application with session_state, and code values as url parameters.

http://localhost:5000/?state=e3eb1104-4e49-4f35-8277-7a02d10a7ec1&session_state=99826a89-eb5d-48a8-a73c-76cd9b007401&code=85a45a70-f647-4bec-984e-389e410d5757.99826a89-eb5d-48a8-a73c-76cd9b007401.93de3ba7-4a58-418e-893f-8340217592fb 3. The angular application then redirects to root url http://localhost:5000

  1. Once the root url is loaded, the login redirect is triggered to keycloak. And the process repeats again.

I tried to debug this issue. But the redirection seems to happen within keycloak library, and the application code seems to have no control over it.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (1 by maintainers)

Most upvoted comments

For me, it was solved by setting initialNavigation to “enabledNonBlocking” in the parameters of the router module (it was set to “enabledBlocking”).

 RouterModule.forRoot(appRoutes, {
      initialNavigation: 'enabledNonBlocking',
      preloadingStrategy: PreloadAllModules
    })

The only problem is that “enabledBlocking” is required for the server side rendering to work

I had this issue too. We later found out that the reason behind it was incorrectly set up keycloak. Add * in weborigins of your realm settings. It solves this issue.

I hope that this works for you too. If not try the solutions above.

Ok. This here works without the infinite Redirect. BUT, it does not work when navigating into the page via angular router. only on direct access via url. It tells me, that the service does not wait for the onReady event before returning authenticated for the first time.

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  Router,
  RouterStateSnapshot
} from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard extends KeycloakAuthGuard {

  didRetry = false;

  constructor(
    protected readonly router: Router,
    protected readonly keycloak: KeycloakService
  ) {
    super(router, keycloak);
  }

  public async isAccessAllowed(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ) {

    const keycloakInstance = this.keycloak.getKeycloakInstance();

    return new Promise<boolean>(resolve => {
      keycloakInstance.onReady = async (authenticated) => {
        if (!authenticated) {
          await this.keycloak.login({
            redirectUri: window.location.origin + state.url
          });
          resolve(false);
          return;
        }

        // Get the roles required from the route.
        const requiredRoles = route.data.roles as string[];

        // Allow the user to to proceed if no additional roles are required to access the route.
        // noinspection SuspiciousTypeOfGuard
        if (!(requiredRoles instanceof Array) || requiredRoles.length === 0) {
          resolve(true);
          return;
        }

        // Allow the user to proceed if all the required roles are present.
        resolve(requiredRoles.every((role) => this.roles.includes(role)));
      };
    });

  }
}

@twopelu I can confirm that it is some race condition. It is quite simple to prove.

  1. Use the guard from the example and let in run into the issue
  2. Stop the browser reloading (X)
  3. Modify your guard like shown below
  4. Access the protected route directly by entering the url

You will see that isLoggedIn() returns false first. After the 1 second delay it becomes true. This should be solvable.

But anyway, the guard should return false in case of not authenticated user.

if (!this.authenticated) {
      this.keycloak.isLoggedIn().then(a => console.warn({a}))
      setTimeout(() => {
        this.keycloak.isLoggedIn().then(b => console.warn({b}))
      }, 1000);
      // await this.keycloak.login({
      //   redirectUri: window.location.origin + state.url,
      // });
      return Promise.resolve(false);
    }