Keycloak OAuth2 PKCE#

Why Keycloak as authentication server#

Keycloak comes with several handy features built-in:

  • Two-factor authentication

  • Bruteforce detection

  • Social login (Facebook, Twitter, Google…)

  • LDAP/AD integration

Setting up a Keycloak server#

$ curl --output
$ unzip
$ cd keycloak-9.0.3/bin/
$ ./standalone

Creating a new realm#

Name: Security

Creating a client#

  • Client ID: spa-heroes

  • Valid Redirect URIs: http://localhost:4200/*

Standard flow is another name for the Authorization Code Flow as defined in the OAuth 2.0 specification.

Direct Access Grants Enabled may remain enabled for now. It will be easy to test our configuration later.

Creating roles and scopes#

  • Roles: user

  • Client Scopes: heroes

  • Clients -> Client Scopes -> Add heroes

Creating a user#

Users -> Add User

Setting up the front end and back end applications#

Angular app: tour of heroes#

$ curl -LO
$ unzip -d toh
$ cd toh
$ npm i


export class HeroService {
  private heroesUrl = 'http://localhost:18095/api/heroes';
ng serve


Implementing security#

Implicit flow versus code flow + PKCE#

In this example, we will use the authorization code grant flow with Proof Key for Code Exchange (PKCE) to secure the Angular app. It’s a very long name for what could be shortened to “code flow + PKCE” which is more secure than the implicit flow.

In fact, the implicit flow was never very secure to begin with.

The implicit flow was the easiest to understand, since it required one step less than the standard code flow:


PKCE is an addition on top of the standard code flow to make it usable for public clients. It is already in use for native and mobile clients. PKCE boils down to this:

  1. Give hash of random value to authorization server when logging in to ask for code

  2. Hand over the random value to authorization server when exchanging code for access token

  3. Authorization server returns access token after verifying that hash belongs to random value.


If a fraudster were to intercept our authorization grant (the code), he or she would still not have the code_verifier, which is stored in our SPA client.

Json web token (JWT)#

In its compact form, JSON Web Tokens consist of three parts separated by dots (.), which are:

  • Header: the type of the token and the signing algorithm being used

  • Payload: the payload, which contains the claims and additional data

  • Signature: to verify if the token was not tampered with

Therefore, a JWT typically looks like the following: xxxxx.yyyyy.zzzzz

The header (xxx) and payload (yyy) are base64 encoded. An access token is a good example of a JWT:


We can easily decode them using online tools like

Be careful with online tools to analyze JWT tokens. You are exposing access tokens to the world!

Resource server in spring boot#

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation ''
implementation ''
  • spring-boot-starter-security: starter dependency for Spring Security

  • spring-security-oauth2-resource-server: dependency to use our application as a Resource Server

  • spring-security-oauth2-jose: support for the Javascript Object Signing and Encryption framework

Configuration of the resource server#

public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) throws Exception {

Testing the setup#

$ curl -i http://localhost:18095/api/heroes

Without a token, the server responds with HTTP 401. This means we are not authorized. As we don’t have a login form available just yet, we can use the Direct Access Grants flow to obtain a token. This can come in very handy for testing different scenarios as well.

$export TOKEN=$(curl -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=spa-heroes" \
  -d "username=tester" \
  -d "password=123456" \
  -d "grant_type=password" \
  -X POST http://localhost:8080/auth/realms/Security/protocol/openid-connect/token | jq -r .access_token)
$echo $TOKEN
$ curl -i -X GET -H "Authorization: Bearer $TOKEN" http://localhost:18095/api/heroes

Make sure ‘Direct Access Grants Enabled’ is enabled in the Keycloak Client settings

Securing the Angular application#

$ npm i angular-oauth2-oidc --save


import { HttpClientModule } from '@angular/common/http';
import { OAuthModule } from 'angular-oauth2-oidc';

  imports: [
      resourceServer: {
          allowedUrls: ['http://localhost:18095/api'],
          sendAccessToken: true


<button class="btn btn-default" (click)="login()">
<button class="btn btn-default" (click)="logoff()">


import { Component } from '@angular/core';
import { OAuthService, NullValidationHandler, AuthConfig } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc';

  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
export class AppComponent {
  title = 'Tour of Heroes';
  constructor(private oauthService: OAuthService) {

  authConfig: AuthConfig = {
    issuer: 'http://localhost:8080/auth/realms/Security',
    redirectUri: window.location.origin + "/Security",
    clientId: 'spa-heroes',
    scope: 'openid profile email offline_access heroes',
    responseType: 'code',
    // at_hash is not present in JWT token
    disableAtHashCheck: true,
    showDebugInformation: true
  public login() {
  public logoff() {
  private configure() {
    this.oauthService.tokenValidationHandler = new NullValidationHandler();