Securing an Angular and Spring Boot Application with Keycloak
Tags: springboot, authentication, keycloak, angular
Published: 2024-05-22 02:46:48

---
canonical_url: https://phasetwo.io/blog/secure-spring-boot/
---

[Spring Boot](https://spring.io/projects/spring-boot) is a open-source tool which uses Java-based frameworks for building web applications.

In this article we'll be using [Keycloak](https://www.keycloak.org/) to secure an [Angular](https://angular.io/) application and access secured resources from a Spring Boot Web application.

{% cta https://phasetwo.io/ %} Phase Two {% endcta %} is a [Keycloak as a Service](https://phasetwo.io/) provider enabling SaaS builders to accelerate time-to-market with powerful enterprise features like SSO, identity, and user management features. Phase Two enhances Keycloak through a variety of open-source extentions for modern SaaS use cases. Phase Two supports both hosted and on-premise deployment options. 

_What is Keycloak?_

[Keycloak](https://www.keycloak.org/) has been a leader in the Identity and Access Management world since its launch almost 8 years ago. It is an open-source offering under the stewardship of [Red Hat](https://www.redhat.com/)

> **INFO**

If you just want to skip to the code, visit the Phase Two [Spring Boot example](https://github.com/p2-inc/examples/tree/main/frameworks/spring-boot-keycloak). We are also building [Keycloak examples](https://github.com/p2-inc/examples) for other frameworks.


## TOC

* [Spring Boot Quick Start](#spring-boot-qs)
* [Keycloak Setup](#keycloak-setup)
* [Spring Boot Configure](#springbootapp)
* [Angular](#angular)

---

## Setting up a Spring Boot project <a name="spring-boot-qs"></a>

In order to setup a Spring Boot project, a JDK version must be chosen. As of the time of writing, to be inline with the latest changes from Keycloak 24, the Java 17 baseline will be used. Other JDK versions can also be used for developing the resource server according to the preference of the developer.

Starting with Spring Boot 2.x the Keycloak client adapters were deprecated. In Spring Boot 3.x we will use native functionalities of the [`spring-boot-oauth2-resource-server`](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html) to be able to configure the application security context.

### Quick Start

To get this project up and running locally on your computer you can clone the Phase Two [Spring Boot example](https://github.com/p2-inc/examples/tree/main/frameworks/spring-boot-keycloak) or follow the instructions below to generate a project from scratch.

1. Set up the Spring Boot project.

   To kickstart a project, we will use (and recommend) using the Spring Boot [Initializr](https://start.spring.io/), a Web-based tool that provides a simple UI to generate the project.

   Provide the following values to spring initializr for the project metadata:

   ```
   Group: com.example
   Artifact: spring-boot-keycloak
   Name: spring-boot-keycloak
   Description: Demo project for Spring Boot
   Package name: com.example.spring-boot-keycloak
   ```

![Spring Boot Initializer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bpnqvu0v4u6ki9301bvi.png)

2. Add the required dependencies in spring initializr.

   For the purpose of this project we will add the following dependencies:

   - Oauth2 Resource Server
   - Spring Web
   - Spring Security

   This will result in the following lines within `build.gradle`.

   ```
   implementation 'org.springframework.boot:spring-boot-starter-security'
   implementation 'org.springframework.boot:spring-boot-starter-web'
   implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
   ```

   Generate the project with those settings. Open the .zip in your preferred text editor.

3. Setup JDK 17 for the project. Follow instructions on the [JDK setup](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/) page.


## Setting up a Keycloak Instance <a name="keycloak-setup"></a>

> **TIP**
If you already have a functioning Keycloak instance, you can skip to the next section.

{% details **Keycloak Setup Details** %} 
Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a Phase Two [free Keycloak](https://phasetwo.io/) starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

- Visit the sign-up [page](https://phasetwo.io/dashboard/).
- Enter an email, use a Github account, or use an existing Google account to register.

![Register](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1jovhdjmmnys3kjp1wb.png)

- Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

![Email Link](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s73nhw4jl43li164nm5i.png)

- After creating an account, a [realm](https://www.youtube.com/watch?v=ZTFlc-3pG1M) is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.
- Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

  ![Create shared deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7oknshhzra67byi0uxoz.png)

- After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

![Deployments](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arwn5n54hbzwocobkmzs.png)

At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.
{% enddetails %}


## Setting up an OIDC Client

We need to create a [OpenID Connect](https://www.keycloak.org/docs/latest/server_admin/#con-oidc_server_administration_guide) Client in Keycloak for the app to communicate with. 

{% details **Details** %}

Keycloak's [docs](https://www.keycloak.org/docs/latest/server_admin/#_oidc_clients) provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

1.  Open the Admin UI by clicking **Open Console** in the Phase Two Dashboard.
1.  Click **Clients** in the menu.
1.  Click **Create client**.
1.  Leave **Client type** set to **OpenID Connect**.
1.  Enter a **Client ID**. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.
1.  Supply a **Name** for the client.
1.  Click **Next**.
![General settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vmlbu0gbf3wdmmacy23.png)
1.  Under the Capability Config section, leave the defaults as selected. This can be configured further later.
- Client authentication to Off.
- Authorization to Off.
- Standard flow checked. Direct access grants checked. All other items unchecked.
1.  Click **Next**.
![Capbility config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rv9fpa1jgny2300c6btd.png)
1.  Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example application:
**URI and Origin Details**
The choice of `localhost` is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    _Valid redirect URI_ (allows redirect back to application)
    ```
    http://localhost:3000/*
    ```
    _Web origins_ (allows for Token auth call)
    ```
    http://localhost:3000
    ```

1.  Click **Save**
![Login settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lk6j7sjbv2b5ns7jc85a.png)
{% enddetails %}

### OIDC Config

{% details **Details** %}

We will need values to configure our application. To get these values follow the instructions below.

1. Click **Clients** in the menu.
1. Find the Client you just created and click on it. In the top right click the **Action** dropdown and select **Download adapter config**.
1. Select **Keycloak OIDC JSON** in the format option. The details section will populate with the details we will need.
   - Note the `realm`, `auth-server-url`, and `resource` values.
![Adapter config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hjxmpf25hhyogdno2yht.png)

{% enddetails %}

## Adding a Non-Admin User

> **INFO**
It is bad practice to use your Admin user to sign in to an Application.

Since we do not want to use our Admin user for signing into the app we will build, we need to add another non-admin user.

{% details **Details** %}
1.  Open the Admin UI by clicking **Open Console** in the Phase Two Dashboard.
1.  Click **Users** in the menu.
1.  Click **Add user**.
1.  Fill out the information for Email, First name, and Last name. Click **Create**.
1.  We will now set the password for this user manually. Click **Credentials** (tab) and click **Set Password**. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
1.  Click **Save** and confirm the password by clicking **Save password**
{% enddetails %}

---

## Install and configure Spring Boot  <a name="springbootapp"></a>

Now that we've setup and configured Keycloak using [Phase Two](https://phasetwo.io/dashboard/) and cloned or created our Spring Boot application template, we will need to configure the project to leverage the capabilities provided by Keycloak.

1. Configure application settings

   Update your `application.yaml` configuration file with the Keycloak security configuration (it's possible your download includes a `application.properties` file instead).

   ```yaml
   spring:
     application:
       name: spring-boot-keycloak
     security:
       oauth2:
         resourceserver:
           jwt:
             issuer-uri: $http-keycloak-url/auth/realms/$your-realm
             jwk-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
   ```

   Replace

   - `$http-keycloak-url` with the Keycloak URL from the Phase Two hosted Keycloak instance.
   - `$your-realm` with the Keycloak realm created earlier in this tutorial.

   If you are using the local Keycloak instance from the cloned example, use the local address for `$http-keycloak-url`.

   The below Java code omits any imports, reference our example for necessary imports or use your text editor to assist with populating the imports.

2. Configure Spring Boot resource server

   Under `src.main.java.com.springbootkeycloak` create a new package, `config`, and create a class `SecurityConfig.java`. In this class, add the `HttpSecurity` settings:

   ```java
   @Configuration
   @EnableWebSecurity
   @EnableMethodSecurity
   public class SecurityConfig {

       private final JwtClaimsConverter jwtAuthConverter;

       public SecurityConfig(JwtClaimsConverter jwtAuthConverter) {
           this.jwtAuthConverter = jwtAuthConverter;
       }

       @Bean
       public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
           http.authorizeHttpRequests(authz ->
                   authz
                           .requestMatchers("/api/**")
                           .authenticated()
           );
           http.oauth2ResourceServer(oauth2ResourceServer ->
                   oauth2ResourceServer.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter))
           );
           http.csrf(AbstractHttpConfigurer::disable);

           http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
           return http.build();
       }
   }
   ```

   This configuration will make the Spring Boot act as an OAuth2 Resource Server's with JWT authentication. This configuration is part of the functionality provided by the `spring-boot-starter-oauth2-resource-server` dependency. Read more about it's configuration [here](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html#oauth2resourceserver-jwt-sansboot).

3. Add JWT token convert configuration

   In the same `config` package, create another class, `JwtClaimsConverter.java`. Add a converter for extracting the security context attributes from the `access_token` received from Keycloak.

   ```java
   @Component
   public class JwtClaimsConverter implements Converter<Jwt, AbstractAuthenticationToken> {

       @Override
       public AbstractAuthenticationToken convert(Jwt jwt) {
         var authorities = extractRealmRoles(jwt);
           return new JwtAuthenticationToken(jwt, authorities, getPrincipalFromClaim(jwt));
       }

       private String getPrincipalFromClaim(Jwt jwt) {
           var claimName = "preferred_username";
           return jwt.getClaim(claimName);
       }

       private Collection<GrantedAuthority> extractRealmRoles(Jwt jwt) {
           Map<String, Object> resource = jwt.getClaim("realm_access");
           Collection<String> roles;
           if (resource == null
                   || (roles = (Collection<String>) resource.get("roles")) == null) {
               return Set.of();
           }
           return roles.stream()
                   .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                   .collect(Collectors.toSet());
       }
   }
   ```

   The provided example uses the `preferred_username` claim for populating the _principal_ of the security context and the `realm_access.roles` to populate the _authorities_.

   This configuration is part of the functionality provided by the `spring-boot-starter-oauth2-resource-server` dependency. Read more about it's configuration [here](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html#oauth2resourceserver-jwt-authorization-extraction).

4. Create the secured API resources:

   In `src.main.java.com.springbootkeycloak` create a new package, `web`, and create a new class `TestController.java`.

   To test the security integration two resource endpoints are defined:

   - `/api/test/anonymous`
   - `/api/test/user`

   Implemented with this code:

   ```java
   @RestController
   @RequestMapping("/api/test")
   public class TestController {

       @RequestMapping(value = "/anonymous", method = RequestMethod.GET)
       public ResponseEntity<String> getAnonymous() {
           return ResponseEntity.ok("Hello Anonymous");
       }

       @PreAuthorize("hasRole('ROLE_user')")
       @RequestMapping(value = "/user", method = RequestMethod.GET)
       public ResponseEntity<String> getUser()
       {
           return ResponseEntity.ok("Hello Secured with user role.");
       }
   }

   ```

   Because both endpoints have the prefix `/api` they will require a secure context in order to access them. Furthermore, the `/api/test/user` endpoint is secured using a predefined authority `ROLE_user`. This is a Realm role that can be created and applied to your example user from earlier in this tutorial.

   This logic can be used to extend access and authorization to any part of the application.

   Start the application running with `./gradlew bootRun`.

### Testing the secured endpoints

The secured endpoints can be tested using `curl` with the `Authorization` header. The `Authorization` header must contain the `access_token`.

```bash
curl --location 'http://localhost:8080/api/test/anonymous' \
    --header 'Authorization: Bearer {{$access_token}}'
```

```bash
curl --location 'http://localhost:8080/api/test/user' \
    --header 'Authorization: Bearer {{$access_token}}'
```

To generate an access token, you can use the `openid-connect/token` endpoint from Keycloak.

```bash
curl -X POST \
  --location "https://$http-keycloak-url/auth/realms/$keycloak-realm/protocol/openid-connect/token" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=$test-user&password=$password&grant_type=password&client_id=$client-name&client_secret=$client-secret'
```

Substitute the values from your Keycloak instance and test user for `$http-keycloak-url`, `$keycloak-realm`, `$test-user`, `$password`, `$client-name`, and `$client-secret`.

In the returned HTTP response, the `access_token` will be present. Use this token to test the secured endpoints in the example curl's above.

At this point, your Spring Boot application is secured with Keycloak, but there is no "Frontend" to the application. In the next section, we will add an Angular SPA to demonstrate sign-in with Keycloak.

## Integration with Angular <a name="angular"></a>

In order to access the secured resources of the Spring Boot server, we will create a client application which will authenticate our users. After Authentication, that user will then have access to the secured resources via their JWT token.

### Generate Angular Application

Our [Spring Boot example](https://github.com/p2-inc/examples/tree/main/frameworks/spring-boot-keycloak) already has a basic Angular application setup. We will use that for the rest of this setup.

In the example folder, open the `angularclient` folder.

If you do want to start your own Application, follow the instructions below:

- Setup a new Angular application following these [instructions](https://angular.io/start)
- Use the Angular Oauth2 OIDC [library](https://github.com/manfredsteyer/angular-oauth2-oidc) to integrate authentication and authorization.

### Securing views

In the `/angularclient/src/app` folder, the `app.module.ts` file is the entry point for the Angular application. The Angular application will need to be configured in order to access user information only after authentication.

```javascript
@NgModule({
  declarations: [
    AppComponent,
    MainpageComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    HttpClientModule,
    OAuthModule.forRoot()
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: applicationInitializerFactory,
      deps: [OAuthService],
      multi: true
    },
    {provide: LOCAL_STORAGE_TOKEN, useFactory: localStorageFactory},
    {provide: OAuthStorage, useFactory: localStorageFactory}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {...}
```

The app is initialized with the `OAuthService` as a dependency. Tokens from the `OAuthService` are stored in the browser's `localStorage`.

To configure the `OAuthService`'s `authorization code` login flow with the `angular-oauth2-oidc` library add the following configuration:

```javascript
function configure() {
  oauthService.configure({
    // URL of the SPA to redirect the user to after login
    redirectUri: window.location.origin + "/index.html",
    // The SPA's id. The SPA is registered with this id at the auth-server
    clientId: "$your-public-keycloak-client",
    // set the scope for the permissions the client should request
    scope: "openid",
    // url for  /.well-known/openid-configuration endpoint
    issuer: "http://$http-keycloak-url:8888/auth/realms/$your-keycloak-realm",
    disablePKCE: true,
    //initialize the code flow
    responseType: "code",
    showDebugInformation: true,
  });
}
```

Replace `http-keycloak-url`, `$your-public-keycloak-client`, and `$your-keycloak-realm` with your actual Keycloak configurations.

Start the application with `npm run start`

#### User Authentication

In the `user.component.html` file, we authenticate the user to the logged in state and conditionally render the login and logout buttons.

```html
<div *ngIf="isLoggedIn">
  <!-- Content for logged-in users -->
  <div class="mb-2 text-p2blue-700 text-2xl">Authenticated</div>
  <div class="mb-6 text-p2blue-700 text-md">
    <div *ngIf="userInfo">
      <p><span class="font-bold">Username</span>: {{ userInfo.username }}</p>
      <p><span class="font-bold">Email</span>: {{ userInfo.email }}</p>
      <p><span class="font-bold">Roles</span>: {{ userInfo.roles }}</p>
    </div>
  </div>
  <button [class]="buttonClasses" (click)="signOut()">Sign Out</button>
</div>
<div *ngIf="!isLoggedIn">
  <div class="mb-6 text-p2blue-700 text-2xl">Not authenticated.</div>
  <button [class]="buttonClasses" (click)="signIn()">Sign In</button>
</div>
```

the `isLoggedIn` function can be found in the `user.component.ts` file.

```javascript
this.isLoggedIn = this.oauthService.hasValidAccessToken();
```

Clicking the Log In or Log Out buttons will redirect to the Keycloak login page or log the user out.

#### Use Angular guards to secure routes

We can achieve route restriction by using guards. If the access token is not valid the guard will initiate the login flow. You could optionally apply this at the router level to enforce a full page login.

```javascript
export class AuthGuard implements CanActivate {

  constructor(private oauthService: OAuthService) {
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree>{
    if(!this.oauthService.hasValidAccessToken()) {
      this.oauthService.initLoginFlow();
    }
    return of(true);
  }
}
```


## Learning more

Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our [blog](https://phasetwo.io/blog) has many use cases from [customizing login pages](https://phasetwo.io/blog/customizing-login-pages), setting up [magic links](https://phasetwo.io/blog/set-up-magic-links) (password-less sign in), and [Organization](https://phasetwo.io/product/organizations) workflows.