Keycloak Angular 2 - Check authenticated status Keycloak object

14.7k views Asked by At

I'm implementing the Keycloak authentication service in my Angular 2 project. I use a service for logging in, logging out etc.

Authenticating a user and logging out seems to work. I'm now trying to protect some routes. I now have a working AuthGuard. To check if the user is logged in (in the AuthGuard), I have a isAuthenticated() method in the service. This is the service:

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

declare let Keycloak: any;

@Injectable()
export class KeycloakService {
  private keycloak = new Keycloak('app/keycloak/keycloak.json');

  constructor() {
    this.keycloak.init({onload: 'check-sso'});
    console.log(this.keycloak);
  }

  public login() {
    this.keycloak.login();
  }

  public logout() {
    this.keycloak.logout();
  }

  public isAuthenticated() {
    return this.keycloak.authenticated;
  }
}

Flow: User logs in, user tries to reach protected route, AuthGuard checks if user is logged in via isAuthenticated().

Note: I don't want to authenticate the user for the complete Angular app. Only for some routes.

Problem

After the user logs in, the user is redirected to the Angular app. After this, the isAuthenticated() method returns still false. Here is why:

I logged the Keycloak object to the console. I found something I didn't understand.

Keycloak object after login redirect

Keycloak object after login redirect


Same Keycloak object after login redirect (but expanded)

Same Keycloak object after login redirect (but expanded)

First the authenticated property is false. After expanding the authenticated property is true.

Question

Is the way I try to maintain my Keycloak object the correct way?

Consulted sources

And others

2

There are 2 answers

3
hakamairi On BEST ANSWER

Basing on the community provided Angular2 example in keycloak's github you can spot some differences in interacting with keycloak js adapter. Mainly the actual check on the authenticated (and possibly userName) is done on the promise returned from init.

  static init(): Promise<any> {
    let keycloakAuth: any = new Keycloak('keycloak.json');
    KeycloakService.auth.loggedIn = false;

      return new Promise((resolve, reject) => {
        keycloakAuth.init({ onLoad: 'login-required' })
          .success(() => {
            KeycloakService.auth.loggedIn = true;
            KeycloakService.auth.authz = keycloakAuth;
            KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/demo/protocol/openid-connect/logout?redirect_uri=/angular2-product/index.html";
            resolve();
          })
          .error(() => {
            reject();
          });
      });
}

Also the official keycloak js adapter's documentation uses promise for the authenticated check

<head>
    <script src="keycloak.js"></script>
    <script>
        var keycloak = Keycloak();
        keycloak.init().success(function(authenticated) {
            alert(authenticated ? 'authenticated' : 'not authenticated');
        }).error(function() {
            alert('failed to initialize');
        });
    </script>
</head>
0
Badr-Eddine On
  1. If you use check-sso as a parameter to init function, the browser will be routed back to the application if the user is not logged in and will remain unauthenticated.You should use login-required instead to fix this problem.

  2. If you don't want to authenticate the user for the complete App, you should detach the logic of creating the adapter, to make things easier if you have more than one secured component. for exemple you can create a HOC.

PS : in the example below, I am using Reactjs, I hope you can find a similar way to do this in angular:

export default (WrappedComponent) => {
  return (props) => {
  const [isAutenticated, setIsAutenticated] = useState(false);
  const [keycloak, setKeycloak] = useState();

  const loadConfig = useCallback(() => {
    const keycloak = Keycloak("/keycloak.json"); //The configuration of the adapter in JSON format
    keycloak.init({ onLoad: "login-required" }).then((authenticated) => {
      setKeycloak(keycloak);
      setIsAutenticated(authenticated);
      });
  }, [Keycloak]);

  useEffect(() => {
    loadConfig();
  }, [loadConfig]);

  if (keycloak) {
    if (isAutenticated) {
      return <WrappedComponent {...props} keycloak={keycloak} />;
    } else return <AuthError message="Unable to authenticate" />;
  }
  return <Loader />;
  };
};
  1. you can find a useful source here