Lejdi Prifti

0 %
Lejdi Prifti
Software Engineer
DevOps Engineer
ML Practitioner
  • Residence:
    Albania
  • City:
    Tirana
  • Email:
    info@lejdiprifti.com
Spring
AWS & Cloud
Angular
Team Player
Coordination & Leadership
Time Management
Docker & Kubernetes
ReactJs
JavaScript
Python
  • Java, JavaScript, Python
  • AWS, Kubernetes, Azure
  • Bootstrap, Materialize
  • Css, Sass, Less
  • Blockchain, Ethereum, Solidity
  • React, React Native, Flutter
  • GIT knowledge
  • Machine Learning, Deep Learning
0

No products in the basket.

Configure Keycloak authentication in Angular 17

17. May 2024

Overview

In this article, we are going to look at how we can configure Keycloak authentication in Angular 17. I have previously put up a basic Keycloak-authenticated Spring Boot service, and I’m intending to carry over this authentication procedure into my Angular 17 application as well. Let’s start!

Table of Contents

Dependency

Writing clean code is one of a developer’s top concerns. We don’t want to invent the wheel or clog the code with redundant junks. For this reason, we are going to use two important dependencies, that are keycloak-angular and keycloak-js.

The  keycloak-angular dependency makes using keycloak-js in Angular applications easier. It  provides an Angular-compatible Keycloak service that covers the keycloak-js methods.

Additionally, it offers the implementation of AuthGuard generically, allowing us to modify our own AuthGuard logic by inheriting the roles load and authentication logic.

Finally, it gives us an HttpClient interceptor that makes sure every HttpClient request has the authorization header included. However, we can disable the interceptor or prevent routes from receiving the authorization header if required.

				
					npm install keycloak-angular@^15.2.1 keycloak-js@^24.0.3
				
			

Environment configuration

There are two environments in any project: a development environment and a production environment. In the environment.dev.ts, I have specified the following configurations for my local Keycloak service.

The Keycloak instance is running on port 8080 and I’ve created a realm named monday. This realm includes a client with id frontend.

				
					export const environment = {
    production: false,
    title: 'Monday',
    keycloak: {
        realm: 'monday',
        clientId: 'frontend',
        url: 'http://localhost:8080'
    }
  };
				
			

The silent-check-sso.html page

In the assets directory, we are going to create what is known as the silent-check-sso.html page. The content of the page looks as shown in the code block below.

The silent-check-sso.html page is used for periodic checks to see if the user’s session is still active without causing any visible interruption to the user. In this case, the page sends a message containing the URL and origin to its parent window. This message could trigger further actions in the parent window, such as updating the UI or refreshing the user’s session.

				
					<html>
  <body>
    <script>
      parent.postMessage(location.href, location.origin);
    </script>
  </body>
</html>
				
			

Initialize Keycloak function

In a file named keycloak-init.factory.ts, we are going to define the initializeKeycloak function. This function initializes Keycloak, ensuring that users are authenticated and have active sessions using the provided configuration options and silent check SSO mechanism.

				
					import { KeycloakService } from "keycloak-angular";
import { environment } from "@env/environment";

export function initializeKeycloak(keycloak: KeycloakService) {

    return () =>
        keycloak.init({
            config: {
                url: environment.keycloak.url,
                realm: environment.keycloak.realm,
                clientId: environment.keycloak.clientId,
            },
            initOptions: {
                onLoad: 'check-sso',
                silentCheckSsoRedirectUri:
                    window.location.origin + '/assets/silent-check-sso.html'
            }
        });
}
				
			

Configuration in the application

The following code snippet written in app.config.ts file defines the configuration for our Angular application, specifically setting up providers for various services and initialization tasks.

Firstly, the routing configuration is provided using provideRouter(routes), where routes likely represents the routing configuration of the application.

Then, our initializer function for Keycloak authentication is set up using APP_INITIALIZER. Our function, initializeKeycloak will run before the application starts, ensuring Keycloak authentication is initialized properly. Additionally, we provide the KeycloakService as a dependency.

Lastly, we add the HTTP client configuration which includes interceptors for handling HTTP requests and responses. Interceptors in Angular are middleware components that allow you to intercept HTTP requests or responses and perform operations such as modifying headers or logging requests. In our case, we need to intercept HTTP requests to include the Bearer token generated by Keycloak. We achieve this by including the KeycloakBearerInterceptor, which is responsible for adding a Keycloak bearer token to outgoing HTTP requests, ensuring that the requests are authenticated with Keycloak before reaching the server.

				
					export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    {
      provide: APP_INITIALIZER,
      useFactory: initializeKeycloak,
      multi: true,
      deps: [KeycloakService]
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: KeycloakBearerInterceptor,
      multi: true
    },
    KeycloakService,
    provideHttpClient(withInterceptorsFromDi())
  ]
};
				
			

AuthGuard setup

Additionally, I have created a custom guard named AuthGuard that extends from the KeycloakAuthGuard. If the user is not authenticated, then the guard will redirect them to the home page. 

				
					@Injectable({
  providedIn: 'root'
})
export class AuthGuard extends KeycloakAuthGuard {
  
  constructor(
    protected override readonly router: Router,
    protected readonly keycloak: KeycloakService
  ) {
    super(router, keycloak);
  }
  
  async isAccessAllowed(): Promise<boolean | UrlTree> {
    if (!this.authenticated) {
      await this.router.navigate(['/home']);
    }
    return this.authenticated;
  }
}
				
			

In this case, the path /monday/dashboard is secured and can only be accessed if the user is authenticated.

				
					export const routes: Routes = [
    {
        path: 'monday',
        component: FullComponent,
        children: [
            { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
        ]
    },
    { path: '', redirectTo: '/home', pathMatch: 'full' },
    { path: '**', redirectTo: '/home' }
]
				
			

Login button

The login button is defined usually in a navigation bar component. My login button calls the login function that redirects the user to the Keycloak login page. After the user is logged in successfully, it will be redirected to the /monday/dashboard page. 

				
					async login(): Promise<void> {
    await this.keycloakService.login({
      redirectUri: window.location.origin + '/monday/dashboard',
    })
}
				
			

Hope you find this guide helpful! 

Checkout the blog for more articles.

Posted in AngularTags:
6 Comments
  • David

    It worked, thanks!

    19:35 21. May 2024 Reply
  • Jason

    Thanks for the clear explanation! It helped immensely!!

    Just one question, out of curiosity: is the “silent-check-sso.html” page the best way to solve the periodic auth checks? It looks like a weird way to implement that functionality… (not criticizing, just curious)

    15:25 5. June 2024 Reply
    • Hello and thank you for your message!

      When the check-sso is enabled, our browser will perform the full redirect to the Keycloak server and back to our application within a hidden iframe, saving our application’s resources from needing to be loaded and parsed twice: once during app initialization and once more following the redirect from Keycloak back to our application. This is the main benefit of this approach. In order for this to happen, we must provide the static HTML asset at the address specified in silentCheckSsoRedirectUri in order for Keycloak to be able to interact through the iframe.

      18:42 6. June 2024 Reply
  • Kevin

    Hey! And thanks for the great help!
    I am still receiving 401 Errors after a succesful login after my redirection to the site. Can you provide help there?

    14:33 18. June 2024 Reply
    • Hi and thank you for your comment!

      The usual cause of this problem is the Authorization token missing in the requests. That’s why I would suggest that you verify that the Authorization token is present first and then, verify the configuration your backend is using.

      12:37 29. June 2024 Reply
  • Tamas

    It is a common, standard way to do this. It looks weird, but is kind of a limitation of current web technology and current security protocols that there is no “cleaner” way to do this (AFAIK).

    14:24 11. September 2024 Reply
Write a comment