This post tries to present a way on implementing an auth architectue using an API gateway, where multiple services are handled and OAuth/OIDC for securing the system. For an introduction to different ways on authentication and authorization methods, you can read my other post.

What is part of this architecture?

Kong

I am using the open source API gateway Kong, which already has plugin support for several authentication methods. For example there is a plugin for basic key authentication and OAuth 2.0, but there are not suitable for more complex architectures. One problem is the the management of users that are called consumers in Kong. You can find my customized Kong version here, also with a Dockerfile. Then I added the Middleman plugin for Kong to check the authentication and authorization at an own service.

Keycloak

Keycloak is my favourite authentication and authorization tool because of its wide range of functionalities. In this example I am using Keycloak as my authorization server and for identity management. You can find my customized Keycloak version here, also with a Dockerfile.

Ory Ladon

Ladon is a Go library for managing access policies. These policies store permissions based on a subject, action and resource relation. Because of this abstraction, you can use it for many purposes. In this case it used in an Go web server together with an SQL database to act as an authorization service. It stores policies and answers permissions requests. Therefore a user role will be treated as subject, a HTTP method will be treated as an action and a endpoint will be treated as a resource.

How does this architecture work?

This architecture is using a method where the authentication and authorization are done at the central place at the API gateway. Before an icoming request gets directedd to the upstream by Kong, a request to an auth-check service will be made, that contains the whole request information. This service will perform the authentication and authorization. Therefore it checks if an access token exists and blocks if not. If it exists it will validate the token by sending it to the Keycloak token introspection endpoint. If this is also successfull, the user role gets extracted and gets sent to the Authorization storage service Ladon. This service will check if the user role has the needed permissions to access a resource.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
                                 +----------------+
     +-----------+               |                |
     | User      |               |Secured Service |
     +----+------+               |                |                         +------------------+      +----------------+
          |                      +-------+--------+      OAuth/OIDC flow    |                  |      |                |
          |User flows                    |           +----------------------+ Keycloak Service +------+ Datebase       |
+------------------------+               |           |                      |                  |      | Users, Clients |
|         |              |               |           |                      +------------------+      +----------------+
|         |              |               |           |
|   +-----+----------+   |               |           |
|   |  Kong          +-------------------+-----------+----------------------------+
|   +----+-----------+   |                                                        |
|        |               |                                                  +-----+-----------+       +-----------------+
|        |               |                                                  |                 |       |                 |
|        |               |                                                  | Ladon           +-------+ Database        |
|        |               |                                                  |                 |       | Access Policies |
|        |               |                                                  +-----+-----------+       +-----------------+
|        |               |                                                        |
|        |               |                   +-----------------+                  |
|  +-----+------------+  |                   |                 |                  |
|  |  Middleman Plugin+----------------------+  Auth Service   +------------------+
|  +------------------+  |   POST with       |                 |      POST with request data
|                        |   request data    +-----------------+
|                        |                      Authentication: Token Validation
|                        |                      Authoriztation: Ladon
+------------------------+

Build

You have to build the docker images of each part of the architectures. You can find the repositories here:

Run

If you have built all parts of the architecture, then you can run it with this docker-compose file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
version: '2'
services:
    keycloak-db:
      container_name: keycloak-db
      image: postgres:latest
      environment:
          - POSTGRES_DB=keycloak
          - POSTGRES_USER=keycloak
          - POSTGRES_PASSWORD=password
    keycloak:
        container_name: keycloak
        image: keycloak:latest
        links:
            - keycloak-db:postgres
        environment:
            - KEYCLOAK_USER=user
            - KEYCLOAK_PASSWORD=password
            - PROXY_ADDRESS_FORWARDING=true
        depends_on:
            - keycloak-db
    auth:
        container_name: auth
        image: auth:latest
    ladon:
        container_name: ladon
        image: ladon
        environment:
            - POSTGRES_DB=ladon
            - POSTGRES_USER=user
            - POSTGRES_PASSWORD=password
        links:
            - ladon-db:ladon-db
        ports:
            - 8080:8080
        depends_on:
            ladon-db:
                condition: service_healthy
    ladon-db:
        container_name: ladon-db
        image: postgres:latest
        environment:
            - POSTGRES_DB=ladon
            - POSTGRES_USER=user
            - POSTGRES_PASSWORD=password
        healthcheck:
            test: ["CMD", "pg_isready", "-U", "postgres"]
            interval: 10s
            timeout: 30s
            retries: 5

Review

The advantage of this implementation is, that every microservice behind the gateway does not care about the authentication and authorization, which makes them very flexible and independent. This architectures suffers from a lot of requests, because with every incoming request, two request follows before it gets sent to the upstream service. Therefore a shorter solution could be to develop a Kong plugin in Lua, that validates the JWT Token.