Step-by-step guide how integrate Keycloak with Angular application
If you’re building a large enterprise application or a one that is publicly available you may want to introduce a concept of users, so that they will be able login to their accounts, put their information and do some stuff with your app, if they’re allowed to. With this blog post I would like to show how it could be implemented in Angular application using OAuth 2.0 and OpenID Connect frameworks an integrate it with a popular, open source identity provider — Keycloak.
This is a fourth and the last part of my series on OAuth 2.0. If you’re not familiar with I would recommend to stop here and go check the first one — Introduction to OAuth 2.0.
The aim of this post is to show you a basic set up an Angular application so that it will be integrated with Keycloak and it will be able to consume protected HTTP resource that requires an access token. A full description of how to set up Keycloak instance and how to create a simple protected resource with Spring Boot I’ve described in previous two articles. But don’t worry if you don’t want to do that, I’ll explain it later how.
The goal
For a purpose of this article I’ve prepared a very simple application which could be used to find out information about movies and display them in a table. Application supports two actions — either fetch all the data or select a single movie by its id.
All data are served by Java application, which requires to provide a valid OAuth 2.0 access token. If anything goes wrong, for example user will not be allowed to consume certain resource a toast message at the bottom of the screen will show up.
Apart from frontend app there are two other components in this project. Backend application and Keycloak. First one was already mentioned, in second there are defined users and clients, which are necessary when issuing for an access token.
Both parts are crucial to make our Angular application work. Both are required to be running. I assume that you might not be interested in going thru my previous tutorials to implement everything by yourself and that’s why I’ve prepared Docker compose file which will allow to start all necessary applications with a single command with my default configuration. Therefore clone my keycloak-security-example repository and checkout to a frontend-start-blog branch. It will be our starting point.
Once you’ve got that we need to start both services, so go to the terminal and in the root directory of a project run following command:
> docker-compose up -d backend
In a first run it could take a little while, but after that it should be good. To check if the applications are running you can use command:
> docker ps
IMAGE STATUS NAMES
keycloak-security-example_backend Up About a minute backend
jboss/keycloak:11.0.2 Up About a minute keycloak
postgres:13.0-alpine Up About a minute postgres
All three containers are Up
so we're good to proceed with configuring a hosts file on your machine. Its location depends on the OS:
So please find it, open in an admin mode or relevant and add following line:
127.0.0.1 keycloak
This step is necessary because the address of Keycloak server is different depending on the point of view. From the point of view of an app that is running in the web browser it’ll be http://localhost:8080
, but from the perspective of backend
application that is running inside the Docker network it will be http://keycloak:8080
. To overcome it above config is necessary.
If you like you can add similar entries for frontend
and backend
(IP address remains the same), because the side effect of it will be that instead of using for example http://localhost:4200
you can use more meaningful http://frontend:4200
. But it's totally optional and depends on your preferences.
Register frontend application in Keycloak
If you’re running my preconfigured Keycloak instance you can skip this part or just quickly go thru it.
Based on OAuth 2.0 protocol we need to register our application in Keycloak, because only allowed services can issue an access token.
Therefore, open the Keycloak page — http://localhost:8080, select Administration Console and provide following credentials:
username: admin
password: admin
After login, in the top right corner there will be displayed a name of a realm into which you’re logged in. Make sure that Test is selected.
Now go to the Clients page (it’s located in the left menu) and click Create button located on a right.
In a new view we need to provide only a client name, which will be frontend.
After hitting the Save button a detailed page for newly created client will show up. To finish configurating frontend client we need first to make sure that Access Type is set to public. It indicates that our client is public and will not require secret to get access token. This config is valid for client-side frontends which are not able to securely store secrets for their clients (because the entire code of an application is running in users browser).
Apart from that we need to set up a Root URL to http://localhost:4200 which is a root URL of our application. Also we need to provide a Valid Redirect URIs which is very important from security point of view, because here we’re defining into which URLs Keycloak can redirect after successful authentication. We should provide here only our application’s URLs, to prevent hackers from doing messy stuff. Therefore here I’ve got 3 entries: http://localhost:80/*
, http://localhost:4200/*
, http://localhost/*
.
The last thing that needs to be configured is Web Origins parameter and here I’m providing +
which will handle the CORS problem.
After that click Save button at the bottom of the screen to finish Keycloak configuration (all other steps, like user and roles definition was already covered in previous blog posts).
Main view of a page
Now we can deep dive into Angular project. It has only one component (apart from AppComponent
) - ContentComponent
.
To keep you sane and to keep this post short I’ll skip showing HTML and CSS files, as the aim of the project is not to look nice.
From the above code you can see that in order to connect to the backend application it uses the MovieBackendService
which encapsulates HTTP requests:
Again, fairly simple.
To make it work I’ve also set up a proxy configuration, both for development and production mode. For a first one I’ve created a proxy.conf.dev.json
file:
The target field indicates under which address the backend service is available. To apply it I’ve also modified the start
script in package.json
file:
For production proxy config I’ve created the default.conf
file, which is a configuration of Nginx server:
server { listen 80;
server_name frontend;
root /usr/share/nginx/html;
index index.html index.html; location /movies {
proxy_pass http://backend:9000/movies; } location / {
try_files $uri $uri/ /index.html;
}
}
The http://backend:9000/movies
is an address of the backend service (if a backend service is running in the same Docker network, or in the same Kubernetes cluster with the same Service name).
Having all of that we can now proceed with adding and configuring Keycloak libraries.
Basic configuration of Keycloak libraries
First step would be to add keycloak-angular dependencies to the project, therefore in a terminal run following command:
> npm install keycloak-angular keycloak-js
Having it we now need to config it with our application. What is important here, before running our own code first we need to authenticate a user, show Keycloak’s login page and so on. To achieve it we will use an Angular’s special InjectionToken
called APP_INITIALIZER
. A function, which will be assigned to this token will be executed before loading the rest of an application.
To create a custom function, that we will than assign to APP_INITIALIZER
just run this command:
> ng g class init/keycloak-init --type=factory --skip-tests
CREATE src/app/init/keycloak-init.factory.ts (22 bytes)
A new file was created. Open it and paste following content:
Above function takes as an argument a KeycloakService
which is an object from keycloak-angular
library so that we can use it in a body of a function to config it by providing URL to the server, realm name and application's client id which was created a step before.
For now all values are hardcoded here, which of course is not good, but we’ll tackle this problem later on.
Next we need to register this function in the app.module.ts
file, as a provider
:
Take a closer look in the imports
section. There is KeycloakAngularModule
added there.
Next step would be to create a Guard that will protect routes in an application. The library that we’re using is already providing a preconfigured, abstract KeycloakAuthGuard
class from which we will extend ours. But first let's create one:
> ng g guard guard/auth --skip-tests? Which interfaces would you like to implement? CanActivate
CREATE src/app/guard/auth.guard.ts (458 bytes)
The content of this Guard would be:
In the isAccessAllowed
method we're adding the piece that will redirect a user to a login page if she/he is not authenticated yet.
To finish this part, the only thing to do is to add this Guard to the app-routing.module.ts
.
Having all of that we can test it! Make sure that both backend and Keycloak services needs to be running ( docker-compose up -d backend
) and run the command:
> npm start> frontend@0.0.0 start .\keycloak-security-example\frontend
> ng serve --proxy-config src/assets/proxy.conf.dev.json- Generating browser application bundles...[HPM] Proxy created: /movies -> http://localhost:9000
[HPM] Subscribed to http-proxy events: [ 'error', 'close' ]
√ Browser application bundle generation complete.Initial Chunk Files | Names | Size
vendor.js | vendor | 5.11 MB
styles.css, styles.js | styles | 562.38 kB
polyfills.js | polyfills | 493.08 kB
main.js | main | 40.76 kB
runtime.js | runtime | 6.15 kB | Initial Total | 6.18 MBBuild at: 2021-02-20T15:55:55.362Z - Hash: 360dd85cc05c08ec0698 - Time: 14478ms** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
√ Compiled successfully.
Now if you open the http://localhost:4200/
page you should be redirected to the login page:
And provide credentials:
username: han/luke
password: password
After that you should be able to enter the main page! If you now try to play around with an app, by clicking All movies
button or selecting movie by id, in one of the cases you'll get this kind of an error:
This toast message inform the user that she/he doesn’t have sufficient roles. But how application knows that?
First of all after successful login OAuth2.0 tokens were fetched from the Keycloak server and stored in your web browser. Now if there is any request made to the backend service our great Keycloak library is adding the Authorization
HTTP header with an access token. And such is required to be consumed by the backend application. It validates a content of a token and returns OK response if everything is fine, or returns an error if token is corrupted or user is not allowed to consume specific endpoint (because she/he doesn't have sufficient roles).
Productionize Keycloak configuration
So far so good, we’ve got a working piece of code. But there is one problem with this approach. If we would need to change the Keycloak’s URL, realm name or client (development, test and production environment can be different) we would be forced to change it in the code for each version, as all these values are hardcoded.
To overcome this problem we need to externalize configuration of an app. My approach would be to pass those values from the environment variable during startup of an application. Because we’re using Docker it’s quite easy to achieve.
First step is to create separate configuration files for two cases — development and production. Therefore in the assets folder create a new config folder inside of which place these two following files:
- config.dev.json
- config.prod.json
In the latter config file there are no hardcode values, just a placeholder for environment variables, which will be replaced during the startup of an application.
Next step would be to make use of these two configuration. Therefore got to environments directory and add a configFile
value which will point out to a different config file.
- environment.ts
- environment.prod.ts
Also create a new file, environment.dev.ts which will be the same as the default one:
Having that we could now move on to the code and make use of it, but after the dev environment is not fully setup. We need to add this new environment to an Angular configuration.
Therefore open the angular.json file and locate the projects.frontend.architect.build.configurations
section and add dev
configuration which will be replacing the default environment.ts
file with environment.dev.ts
:
In the same file, scroll down a little bit to the serve
section and in configurations
add new dev
entry with browserTarget
:
Finally we can go to the package.json file in which we can specify that when we want to run the start
script we would like to enable the dev
profile, by adding the -c dev
parameter.
After this change once we run the npm start
the Angular app will be build with dev
configuration.
If we run an application right now nothing will change, because we only registered a new application profile, but haven’t make a use of it in the code. Therefore let’s now focus on the code itself.
Right now we’re initializing KeycloakService
before the entire application. What we need to do now is to load configuration before KeycloakService
. To do that first let's create a ConfigInitService
which will load a configuration file and assign it's values to a variable.
> ng g service init/config-init --skip-tests
CREATE src/app/init/config-init.service.ts (140 bytes)
And the content of this service class in which we’re using the environment’s value that points out to the correct configuration file (dev or prod).
Next, we need to go back to the keycloak-init.factory.ts and adjust it so that getConfig()
method of above service is invoked before KeycloakService
initialization.
This time a code is a little bit complicated. confiService
is returning the config
object from which we can get necessary values.
Last thing to do in the app code is to register a ConfigInitService
in app.module.ts
and pass it to the APP_INITIALIZER
.
If we run the npm start
command the app will start up and works as previously. Awesome, we're in the same situation as we were previously 😏.
Let’s move forward and change the Docker image definition. If you look at the Dockerfile
for frontend app it looks pretty straightforward:
It’s a two stage Dockerfile, in first step a project is build to HTML, CSS and JSes and in the second step all these files are copied to an NGINX server together with simple server configuration in default.conf file.
What we need to change in above file is to somehow replace placeholders from config.prod.json with relevant environment variables that will injected into Docker container during startup. Apart from that it would be to do similar thing with default.conf file in which we’ve got a proxy_pass
defined which routes traffic to the backend service. Instead of hardcoding the backend URL we can allow to dynamically assigning it during container startup.
Therefore in default.conf
file we need to replace the http://backend:900
value to ${BACKEND_BASE_PATH}
.
server {
listen 80;
server_name frontend;
root /usr/share/nginx/html;
index index.html index.html; location /movies {
proxy_pass ${BACKEND_BASE_PATH}/movies;
} location / {
try_files $uri $uri/ /index.html;
}
}
But we still have a problem, how to replace those placeholders with values from environment variables? Nothing simpler, we can use envsubt bash command.
First, let’s create a bash script located in the root folder of frontend project and call it replace_placeholders.sh:
In a first line we’re replacing the placeholders from config.prod.json and as a result it generates a config.json file with values from environment variables (which holds Keycloak url, realm and client info). With the second line we’re replacing only one placeholder with a base url to the backend application in default.conf — NGINX server configuration.
Having it setup we can change Dockerfile definition, so that it will copy a script to a container and run it during the startup.
Finally to run the application we need to declare those environment variables. Depending on how you would like to run the application there are many ways to achieve it. For example they can be declared in OS, passing them as parameters using Docker commands, using Docker Compose file or be declared in Kubernetes Deployment or ConfigMap.
For me the most convenient is to use Docker Compose (docker-compose.yaml file located in the project root)
version: "3.8"
services:
frontend:
build: ./frontend
container_name: frontend
ports:
- 80:80
environment:
- KEYCLOAK_URL=http://keycloak:8080
- KEYCLOAK_REALM=test
- KEYCLOAK_CLIENT_ID=frontend
- BACKEND_BASE_PATH=http://backend:9000
depends_on:
- keycloak
- backend
# definitions of other services
Now run the following command and a Docker container will be created and then started.
> docker-compose up -d frontend
To check if everything is working we can list Docker containers:
> docker ps
CONTAINER ID STATUS NAMES
1840d7564aeb Up 46 seconds frontend
cba18013881c Up 47 seconds backend
01f15608d210 Up 47 seconds keycloak
ac67959019f9 Up 48 seconds postgres
Than go to a web browser and type http://localhost
so it will redirect you to the login page and once you login you can play around with an app.
That's it for today and for entire series about OAuth 2.0 framework. I hope you've enjoyed and learnt some new things. Please let me know in the comments what do you think.
The entire code for this project can be found in my GitHub repository:
Here is the list of all other posts in this series:
- Introduction to OAuth 2.0
- Create and configure Keycloak OAuth 2.0 authorization server
- Implementing OAuth 2.0 access token validation with Spring Security