Securing Angular and Quarkus with Keycloak Pt 2
Published: 2020-10-07 17:54:46

In the previous article we created an initial user interface that enables Keycloak login and role recognition. The next step is to wire this up to a back end service to serve secure, role-based services.


---
Code for this article is located in Github here:

* [Petstore UI](https://github.com/cloudy-engineering/pet-store-ui/tree/secure-ui)
* [Petstore API](https://github.com/cloudy-engineering/pet-store-api)

---

In a typical scenario I would implement 2 different types services:
* Services for store employees to use serving up employee specific functions
* Services for customer specific functions

I'd also implement multiple levels of integration. For now we will start with something simple, create a single Quarkus microservice and manage access through the roles we created.

So, let's kick it off!

# Create the API Client

We are going back to the Keycloak admin UI to create a new client for the API to use:

Navigate to Clients, then click `Create`:

![api client](https://dev-to-uploads.s3.amazonaws.com/i/ec8sp4x8a644ytlusoim.png)

Once the client is created, navigate to the Roles tab to create the roles that we want the client to assume:

![api roles](https://dev-to-uploads.s3.amazonaws.com/i/cyq81381ec88i3ie17ft.png)

And we want to map these back to our users as in the previous article.

Navigate to Groups -> store-employees -> Role Mappings and add in the `api-employee` role:

![api role mappings employee](https://dev-to-uploads.s3.amazonaws.com/i/0f2qomsf343zywsc5mqe.png)

Do the same for the `api-customer` role:

![api role mappings customer](https://dev-to-uploads.s3.amazonaws.com/i/btgagwhn3hkz2s3min0n.png)

Next we want to start work on our service api.

# Creating our Service

We will start from scratch and create a standard Quarkus service:

```bash
$ mvn io.quarkus:quarkus-maven-plugin:1.7.2.Final:create \
    -DprojectGroupId=com.brightfield.streams \
    -DprojectArtifactId=petstore-api
```

Some added dependencies that will make life easier include:

```xml
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jackson</artifactId>
    </dependency>
```

Before we secure our application, let's get something running.

# Implementing the Endpoints
There are 3 endpoints we will implement:
* GET /pets 
  * accessible by both groups (store-employees and customers)
* GET /sales 
  * accessible only by the store-employees
* GET /rewards 
  * accessible by the logged in customer

In the project, let's create a couple of resources and representations:

**PetResource.java**
```java
package com.cloudyengineering.pets;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;


@Path("/v1/pets")
@Produces("application/json")
public class PetResource {

    @GET
    public Response getPets() {
        List<Pet> pets = new ArrayList<>();

        return Response.ok(pets).build();
    }
}
```

**SalesResource.java**
```java
package com.cloudyengineering.pets;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("/v1/admin")
@Produces("application/json")
public class SalesResource {
    
    @GET
    public Response getSales() {
        List<Transaction> transactions = new ArrayList<>();

        return Response.ok(transactions).build();
    }
}
```

**Pet.java**
```java
package com.cloudyengineering.pets;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Pet {
    
    @JsonProperty("pet_id")
    private Integer petId;

    @JsonProperty("pet_type")
    private String petType;

    @JsonProperty("pet_name")
    private String petName;

    @JsonProperty("pet_age")
    private Integer petAge;

    public Integer getPetId() {
        return petId;
    }

    public void setPetId(Integer petId) {
        this.petId = petId;
    }

    public String getPetType() {
        return petType;
    }

    public void setPetType(String petType) {
        this.petType = petType;
    }

    public String getPetName() {
        return petName;
    }

    public void setPetName(String petName) {
        this.petName = petName;
    }

    public Integer getPetAge() {
        return petAge;
    }

    public void setPetAge(Integer petAge) {
        this.petAge = petAge;
    }

}
```

**Transaction.java**
```java
package com.cloudyengineering.pets;

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Transaction {

    @JsonProperty("txn_id")
    private String transactionId;

    @JsonProperty("txn_amount")
    private Double transactionAmount;

    @JsonProperty("txn_date")
    private Date transactionDate;

    @JsonProperty("txn_method")
    private String transactionMethod;

    public String getTransactionId() {
        return transactionId;
    }

    public void setTransactionId(String transactionId) {
        this.transactionId = transactionId;
    }

    public Double getTransactionAmount() {
        return transactionAmount;
    }

    public void setTransactionAmount(Double transactionAmount) {
        this.transactionAmount = transactionAmount;
    }

    public Date getTransactionDate() {
        return transactionDate;
    }

    public void setTransactionDate(Date transactionDate) {
        this.transactionDate = transactionDate;
    }

    public String getTransactionMethod() {
        return transactionMethod;
    }

    public void setTransactionMethod(String transactionMethod) {
        this.transactionMethod = transactionMethod;
    }
}
```
As you can see, these are just placeholders so let's fill in some logic:

```java
    @GET
    public Response getPets() {
        List<Pet> pets = new ArrayList<>();
        Pet pet1 = new Pet();
        pet1.setPetId(1);
        pet1.setPetAge(6);
        pet1.setPetName("Oliver");
        pet1.setPetType("Dog");

        Pet pet2 = new Pet();
        pet2.setPetId(2);
        pet2.setPetAge(1);
        pet2.setPetName("Buster");
        pet2.setPetType("Cat");

        Pet pet3 = new Pet();
        pet3.setPetId(3);
        pet3.setPetAge(2);
        pet3.setPetName("Violet");
        pet3.setPetType("Bird");
        
        pets = Lists.asList(pet1, new Pet[]{pet2, pet3});

        return Response.ok(pets).build();
    }
```

**TransactionResource.java**
```java
    @GET
    public Response getSales() {
        List<Transaction> transactions = new ArrayList<>();

        Transaction txn1 = new Transaction();
        txn1.setTransactionId(UUID.randomUUID().toString());
        txn1.setTransactionAmount(12.56);
        txn1.setTransactionDate(Date.from(Instant.now()));
        txn1.setTransactionMethod("Cash");

        Transaction txn2 = new Transaction();
        txn2.setTransactionId(UUID.randomUUID().toString());
        txn2.setTransactionAmount(56.16);
        txn2.setTransactionDate(Date.from(Instant.now()));
        txn2.setTransactionMethod("Credit Card");

        Transaction txn3 = new Transaction();
        txn3.setTransactionId(UUID.randomUUID().toString());
        txn3.setTransactionAmount(88.99);
        txn3.setTransactionDate(Date.from(Instant.now()));
        txn3.setTransactionMethod("Credit Card");

        transactions = Lists.asList(txn1, new Transaction[]{txn2, txn3});

        return Response.ok(transactions).build();
    }
```

Let's quickly give it a test!

```bash
$ ./mvnw quarkus:dev
Listening for transport dt_socket at address: 5005
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-10-04 18:30:09,620 INFO  [io.quarkus] (Quarkus Main Thread) pet-store-api 1.0-SNAPSHOT on JVM (powered by Quarkus 1.8.1.Final) started in 3.296s. Listening on: http://0.0.0.0:
8080
2020-10-04 18:30:09,622 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-10-04 18:30:09,622 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy, resteasy-jackson]
```

```bash
$ http :8080/v1/pets
HTTP/1.1 200 OK
Content-Length: 188
Content-Type: application/json

[
    {
        "pet_age": 6,
        "pet_id": 1,
        "pet_name": "Oliver",
        "pet_type": "Dog"
    },
    {
        "pet_age": 1,
        "pet_id": 2,
        "pet_name": "Buster",
        "pet_type": "Cat"
    },
    {
        "pet_age": 2,
        "pet_id": 3,
        "pet_name": "Violet",
        "pet_type": "Bird"
    }
]
```

```bash
$ http :8080/v1/admin
HTTP/1.1 200 OK
Content-Length: 357
Content-Type: application/json

[
    {
        "txn_amount": 12.56,
        "txn_date": 1601862235453,
        "txn_id": "cb09b51d-541a-45b5-9c27-13a09b480dfd",
        "txn_method": "Cash"
    },
    {
        "txn_amount": 56.16,
        "txn_date": 1601862235454,
        "txn_id": "2895fe10-31ae-417a-8d7d-28ccdf0fa08b",
        "txn_method": "Credit Card"
    },
    {
        "txn_amount": 88.99,
        "txn_date": 1601862235454,
        "txn_id": "afbec85c-b919-40e4-9b02-9e5779534b0b",
        "txn_method": "Credit Card"
    }
]
```

Hmmm... those dates don't look too friendly, let's change them:

**Transaction.java**
```java
    @JsonProperty("txn_date")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
    private Date transactionDate;
```

And again...
```bash
$ http :8080/v1/admin
HTTP/1.1 200 OK
Content-Length: 381
Content-Type: application/json

[
    {
        "txn_amount": 12.56,
        "txn_date": "05-10-2020 01:46:17",
        "txn_id": "8d5e875b-06ec-4cf6-b357-4e14525b831c",
        "txn_method": "Cash"
    },
    {
        "txn_amount": 56.16,
        "txn_date": "05-10-2020 01:46:17",
        "txn_id": "5764bef6-1c78-4efd-8016-119759b263c5",
        "txn_method": "Credit Card"
    },
    {
        "txn_amount": 88.99,
        "txn_date": "05-10-2020 01:46:17",
        "txn_id": "67a5f766-4a9f-4fa4-8e22-59ecf9171e55",
        "txn_method": "Credit Card"
    }
]
```

Nice! It works but is isn't quite secure yet.

# Securing the API
There will be 2 dependencies we will be adding to secure our service:

**pom.xml**
```xml
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-oidc</artifactId>
    </dependency>

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-keycloak-authorization</artifactId>
    </dependency>
  </dependencies>
```

**application.properties**
```
quarkus.oidc.auth-server-url=http://localhost:8081/auth/realms/petshop-realm
quarkus.oidc.client-id=pet-store-api
quarkus.oidc.credentials.secret=itsasecret
quarkus.oidc.authentication.scopes=profile

quarkus.http.cors.origins=http://localhost:4200
quarkus.http.cors.methods=GET,OPTIONS
quarkus.http.cors=true
```

As you can see we have declared the client id `petshop-api` but we've also supplied the secret `itsasecret`, let's make sure that is configured!

Navigate to: Clients -> pet-store-api

Change Access Type to `bearer only` and hit save. You should see the tabs above change!

![bearer token](https://dev-to-uploads.s3.amazonaws.com/i/hjxkr39aqzu1vo5u8w7k.png)

Next navigate to Credentials in the tabs above and you should see your client secret:

![api credentials](https://dev-to-uploads.s3.amazonaws.com/i/5s5eesz49y7c0fek2cuy.png)

Let's copy this generated secret to our Pet Store API:

```
quarkus.oidc.auth-server-url=http://localhost:8081/auth/realms/petshop-realm
quarkus.oidc.client-id=pet-store-api
quarkus.oidc.credentials.secret=05da844f-c975-4571-8767-cbc8078e7b64
quarkus.oidc.authentication.scopes=profile
```

If we run our API now and try and access it we should get a 401 Unauthorized:

```bash
$ http :8080/v1/admin
HTTP/1.1 200 OK
Content-Length: 381
Content-Type: application/json

[
    {
        "txn_amount": 12.56,
        "txn_date": "06-10-2020 09:34:58",
        "txn_id": "a19f4089-5f20-4355-ab4a-a18367347d6d",
        "txn_method": "Cash"
    },
    {
        "txn_amount": 56.16,
        "txn_date": "06-10-2020 09:34:58",
        "txn_id": "6457746c-9c07-4cea-b8c5-fb30dc4e2f3d",
        "txn_method": "Credit Card"
    },
    {
        "txn_amount": 88.99,
        "txn_date": "06-10-2020 09:34:58",
        "txn_id": "09125408-0787-41d0-9976-8e56c5937bb3",
        "txn_method": "Credit Card"
    }
]
```

Wait a minute! That's not right! Oh I remember, we haven't specified security around our endpoints! Let's update the code:

**SalesResource.java**
```java
@Path("/v1/admin")
@Produces("application/json")
public class SalesResource {
    
    @GET
    @RolesAllowed({"api-employee"})
    public Response getSales() {
        List<Transaction> transactions;

        Transaction txn1 = new Transaction();
        txn1.setTransactionId(UUID.randomUUID().toString());
        txn1.setTransactionAmount(12.56);
        txn1.setTransactionDate(Date.from(Instant.now()));
        txn1.setTransactionMethod("Cash");

        Transaction txn2 = new Transaction();
        txn2.setTransactionId(UUID.randomUUID().toString());
        txn2.setTransactionAmount(56.16);
        txn2.setTransactionDate(Date.from(Instant.now()));
        txn2.setTransactionMethod("Credit Card");

        Transaction txn3 = new Transaction();
        txn3.setTransactionId(UUID.randomUUID().toString());
        txn3.setTransactionAmount(88.99);
        txn3.setTransactionDate(Date.from(Instant.now()));
        txn3.setTransactionMethod("Credit Card");

        transactions = Lists.asList(txn1, new Transaction[]{txn2, txn3});

        return Response.ok(transactions).build();
    }
}
```

**PetResource.java**
```java
@Path("/v1/pets")
@Produces("application/json")
public class PetResource {

    @GET
    @RolesAllowed({"api-customer"})
    public Response getPets() {
        List<Pet> pets;
        Pet pet1 = new Pet();
        pet1.setPetId(1);
        pet1.setPetAge(6);
        pet1.setPetName("Oliver");
        pet1.setPetType("Dog");

        Pet pet2 = new Pet();
        pet2.setPetId(2);
        pet2.setPetAge(1);
        pet2.setPetName("Buster");
        pet2.setPetType("Cat");

        Pet pet3 = new Pet();
        pet3.setPetId(3);
        pet3.setPetAge(2);
        pet3.setPetName("Violet");
        pet3.setPetType("Bird");

        pets = Lists.asList(pet1, new Pet[]{pet2, pet3});

        return Response.ok(pets).build();
    }
}
```

And try again...

```bash
$ http :8080/v1/admin
HTTP/1.1 401 Unauthorized
Content-Length: 0
$ http :8080/v1/pets
HTTP/1.1 401 Unauthorized
Content-Length: 0
```

Great! It's secure!

Ready to integrate the User Interface?

# Connecting our UI with the secure services

When we last visited the User Interface, we were able to get user login working as well as ensure that different roles had different views. The changes we are going to make to the UI include:

* Add a new view to list Pets for customers and employees
* Add a new view to list Sales for employees
* Add a new view to list Rewards for customers
* Add in an AuthGuard to ensure different roles don't try and cheat and bypass the URIs

## Creating the Views

Let's start creating new views!

Start by creating the new PetsComponent, SalesComoponent and RewardsComoponent:

```bash
$ ng g c pet
CREATE src/app/pet/pet.component.css (0 bytes)
CREATE src/app/pet/pet.component.html (18 bytes)
CREATE src/app/pet/pet.component.spec.ts (605 bytes)
CREATE src/app/pet/pet.component.ts (263 bytes)
UPDATE src/app/app.module.ts (947 bytes)
$ ng g c sales
CREATE src/app/sales/sales.component.css (0 bytes)
CREATE src/app/sales/sales.component.html (20 bytes)
CREATE src/app/sales/sales.component.spec.ts (619 bytes)
CREATE src/app/sales/sales.component.ts (271 bytes)
UPDATE src/app/app.module.ts (1025 bytes)
$ ng g c rewards
CREATE src/app/rewards/rewards.component.css (0 bytes)
CREATE src/app/rewards/rewards.component.html (22 bytes)
CREATE src/app/rewards/rewards.component.spec.ts (633 bytes)
CREATE src/app/rewards/rewards.component.ts (279 bytes)
UPDATE src/app/app.module.ts (1111 bytes)
$ ng g guard auth
? Which interfaces would you like to implement? CanActivate
CREATE src/app/auth.guard.spec.ts (331 bytes)
CREATE src/app/auth.guard.ts (457 bytes)
```

I'm not going to go full on MVC approach here so we will create 1 service to call the different APIs:

```bash
$ ng g s store
CREATE src/app/store.service.spec.ts (352 bytes)
CREATE src/app/store.service.ts (134 bytes)
```

Let's focus on the 2 main areas:
* StoreService
* AuthGuard

## StoreService

Based on our original endpoints, lets create 3 functions:
* `getPets(): Observable<Pet[]>`
* `getSales(): Observable<Transaction[]>`
* `getRewards(): Observable<Reward[]>`

**store.service.ts**
```typescript
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Pet, Reward, Transaction } from './_model';
import { environment as env } from '../environments/environment';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class StoreService {

  constructor(private http : HttpClient) { }

  getPets(): Observable<Pet[]> {
    const uri = `${env.api_host}/v1/pet`;
    return this.http.get<Pet[]>(uri);
  }

  getSales(): Observable<Transaction[]> {
    const uri = `${env.api_host}/v1/sales`;
    return this.http.get<Transaction[]>(uri);
  }

  getRewards(): Observable<Reward[]> {
    const uri = `${env.api_host}/v1/rewards`;
    return this.http.get<Reward[]>(uri);
  }
}
```

Let's wire in the service to our components and render the results:

**sales.component.ts**
```typescript
import { Component, OnInit } from '@angular/core';
import { StoreService } from '../store.service';
import { map, catchError } from 'rxjs/operators';
import { Transaction } from '../_model';
import { of } from 'rxjs';

@Component({
  selector: 'app-sales',
  templateUrl: './sales.component.html',
  styleUrls: ['./sales.component.css']
})
export class SalesComponent implements OnInit {

  sales: Transaction[];

  constructor(private store: StoreService) { }

  ngOnInit(): void {
    const sale$ = this.store.getSales().pipe(
      map(results => {
        this.sales = results;
      }),
      catchError(error => {
        console.log(error);
        return of([]);
      })
    );

    sale$.subscribe(data => data);
  }

}
```

**sale.component.html**
```html
<p>sales works!</p>
<table>
    <tr>
        <th>Transaction ID</th>
        <th>Transaction Date</th>
        <th>Transaction Amount</th>
        <th>Payment Method</th>
    </tr>
    <tr *ngFor="let sale of sales">
        <td>{{sale.txn_id}}</td>
        <td>{{sale.txn_date}}</td>
        <td>{{sale.txn_amount}}</td>
        <td>{{sale.txn_method}}</td>
    </tr>
    <tr *ngIf="sales === undefined">
        <td colspan="4">No data</td>
    </tr>
</table>
```

**pet.component.ts**
```typescript
import { Component, OnInit } from '@angular/core';
import { StoreService } from '../store.service';
import { map } from 'rxjs/operators';
import { Reward } from '../_model';

@Component({
  selector: 'app-rewards',
  templateUrl: './rewards.component.html',
  styleUrls: ['./rewards.component.css']
})
export class RewardsComponent implements OnInit {

  rewards: Reward[];

  constructor(private store: StoreService) { }

  ngOnInit(): void {
    this.store.getRewards().pipe( 
      map(results => {
        this.rewards = results;
      })
    );
  }

}
```

**pet.component.html**
```html
<p>pet works!</p>
<table>
    <tr>
        <th>Pet ID</th>
        <th>Pet Type</th>
        <th>Pet Age</th>
        <th>Pet Name</th>
    </tr>
    <tr *ngFor="let pet of pets">
        <td>{{pet.pet_id}}</td>
        <td>{{pet.pet_type}}</td>
        <td>{{pet.pet_age}}</td>
        <td>{{pet.pet_name}}</td>
    </tr>
    <tr *ngIf="pets === undefined">
        <td colspan="4">No data</td>
    </tr>
</table>
```

As you can see we are storing the hostname for the api in the `environment` object and constructing our URIs from it.

You can see the source code for the structure of the model classes.

Let's log in as Charlene Masters and see what resolves:

![cust pets](https://dev-to-uploads.s3.amazonaws.com/i/c2nj1u38m1sapl7ob2wd.png)

![cust sales](https://dev-to-uploads.s3.amazonaws.com/i/4j1rak3v82l91ije5kw9.png)

As you can see we get a result based upon the allocated roles. But how dos the API know what roles the user has? If you open the developer console and select the Network tab, take a look at the XHR Request to the server:

![bearer token header](https://dev-to-uploads.s3.amazonaws.com/i/wtu632mlkgzxi643q0a2.png)

If you take a look, you can see the bearer token being passed as part of the API request!

Because we've mapped the `pet-store-api` client roles to the Group, these roles are passed across as part of the bearer token to the API, honoring the access the user has.

# Setting up our Guard

So in any standard application, Charlene should not be able to access the Sales view at all. In the next section we are going to setup the AuthGuard to check her roles and ensure we decline access to the SalesComponent.

First in order to use Keycloak to guard our route, we need to make some changes to our AuthGuard:

**auth.guard.ts**
```typescript
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';

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

  constructor(protected router: Router, protected service: KeycloakService) {
    super(router, service)
  }

  isAccessAllowed(route: ActivatedRouteSnapshot,state: RouterStateSnapshot) : Promise<boolean> {
    return new Promise((resolve, reject) => {
        resolve(true)
    });
  }  
}
```

Here we are using the Keycloak wrapper to ensure the correct authenticated calls back to the Keycloak server are made.

What's important in this class is the `isAccessAllowed()` method. This is the entrypoint that will dictate if the user can activate the component.

Let's do a check on the path and see if they should access the sales data:

**auth.guard.ts**
```typescript
  isAccessAllowed(route: ActivatedRouteSnapshot,state: RouterStateSnapshot) : Promise<boolean> {
    return new Promise((resolve, reject) => {
      const userRoles: string[] = this.service.getUserRoles();
      console.log(`Roles: ${userRoles}`);

      if (state.url === '/sales' && userRoles.indexOf('employee') >= 0) {
        console.log('Permission allowed');
        resolve(true)
      } else {
        console.log('Permission not allowed');
        resolve(false);
      }
      
    });
```

Setting up this guard in our `app-routing.module.ts` is as simple as:

```typescript
const routes: Routes = [
  { path: 'pets', component: PetComponent },
  { path: 'rewards', component: RewardsComponent },
  { path: 'sales', component: SalesComponent, canActivate: [AuthGuard] }
];
```

Now if you access the `sales.component` as Charlene you should be able to see a message `Permission not allowed` in the console and the component is never activated.

---
***NOTE***

You can see we are printing out the roles the user has and if you take a look you'll notice that
Charlene has the following roles: `api-customer,customer,manage-account,manage-account-links,view-profile,offline_access,uma_authorization`

---

So we've now been able to secure our User Interface and our backend services. There is, of course, more you can do with this but it should give you a head start on secure applcations.

In Part 3, we will wire up the RewardsComponent and load user specific information!