Many systems need to have Authentication and Authorization to manage users and accesses. The completed way to handle it is OpenID Connect or OIDC, which is on top of OAuth2.

Architecture

What we want to achieve in the end is this architecture: Architecture

Terms

Before jumping into the details, let’s review some terms and definitions:

All these definitions are according to Wikipedia.

  • Authentication: is the act of proving an assertion, such as the identity of a computer system user.
  • Authorization: Authorization is the function of specifying access rights/privileges to resources, which is related to general information security and computer security, and to access control in particular.
  • OAuth: s an open standard for access delegation, commonly used as a way for internet users to grant websites or applications access to their information on other websites but without giving them the passwords.
  • OpenID Connect: is an open standard and decentralized authentication protocol promoted by the non-profit OpenID Foundation.
  • Access Token: contains the security credentials for a login session and identifies the user, the user’s groups, the user’s privileges, and, in some cases, a particular application.
  • JWT: is a proposed Internet standard for creating data with optional signature and/or optional encryption whose payload holds JSON that asserts some number of claims.
  • Claims-based Identity: is a common way for applications to acquire the identity information they need about users inside their organization, in other organizations, and on the Internet.

Keycloak

We have several options to set up OIDC, but in this post, I’m going to use Keycloak service. Keycloak supports OIDC and SAML. Based on the Keycloak official website, it is an “Open Source Identity and Access Management” service.

I will install and run it by Docker.

To have a connection between our Docker containers, we need to have a network:

$ docker network create keycloak-tyk

Now it’s time to run the Keycloak container:

$ docker run -d \
    --net keycloak-tyk \
    -p 8080:8080 \
    --name keycloak \
    -e KEYCLOAK_HOSTNAME=keycloak -e KEYCLOAK_ADMIN='admin' -e KEYCLOAK_ADMIN_PASSWORD='hxA3ibCP' \
    quay.io/keycloak/keycloak:18.0.0 start-dev

As you may already notice, it’s a development setup, not production. So please be careful with the production setup.

We’re exposing the port 8080, and the username and password of the Keycloak admin panel are there. For sure, you have to change the password to something else.

In order to generate a good password, you can use pwgen command:

$ pwgen -cns1
yEQ3Fieg
$ pwgen -cnsB1
4oTeNThy
$ pwgen -cnsBy1
gx&j/h4H

Please check the help of pwgen for more information and flags.

As we set the KEYCLOAK_HOSTNAME=keycloak we need to add 127.0.0.1 keycloak into /etc/hosts:

$ echo '127.0.0.1   keycloak' | sudo tee -a /etc/hosts

Now the Keycloak is running, and you can open up your web browser and go to https://keycloak:8080/, you should see something like this: Keycloak homepage

Then, log in with the entered username and password by clicking on “Administration Console”. This is the home page of Keycloak: Keycloak dashboard

Note: in this deployment, we didn’t consider the database to persist data. But, in actual deployment, you should, https://www.keycloak.org/server/db.

Now, we need to create a new one for Tyk by going into the “client” menu and clicking on the “create” button: Keycloak new client

Client ID:       tyk-gateway
Client Protocol: openid-connect
Root URL:        http://localhost:8000/

When you click on the “save” button, it creates the client and redirects you to the setting page of the client: Keycloak Tyk client settings

Here you can update the client’s settings, like Name, Description, and so on. The essential parts that need to be changed are Access Type and Valid Redirect URIs:

Access Type:              confidential
Service Accounts Enabled: On
Authorization Enabled:    On
Valid Redirect URIs:      http://localhost:8000/callback

After saving the changes, a new tab will appear on top of the page with the name “Credentials”, which we need to use in Tyk. Keycloak client credentials

Remember that we need that Secret in the Tyk config. We’ll get back to it in a few minutes.

Now go to the Mappers tab and create a new mapper for aud, which means “audience” related to the “client app”. In our case, it means Tyk. Keycloak aud mapper

Name:             aud
Token Claim Name: aud
Claim Value:      tyk-gateway
Claim JSON Type:  String

Now, let’s create some roles, for example: Finance and Delivery by going to the Role tab. Keycloak role attributes

Role name: Finance
Attributes: key=is_staff, value=true

Role name: Finance
Attributes: key=is_staff, value=false

Now, it’s time to create some users for this client. Go to the “users” menu and create a new user: Keycloak role attributes

Note: To make it simple for this tutorial, I put username and enabled the Email Verified checkbox.

By the Credentials tab, set a password for the user and save it. Make sure the Temporary flag is off.

Now let’s connect the user to the roles that we’ve created before: Keycloak role attributes

Select your client from the Client Roles and then from the appeared Available Roles select delivery and click on the Add selected button.

Also, create another user for the finance role, e.g., “finance-user1” and follow the same steps.

Now, the keycloak is ready to be tested! Let’s test to ensure we get the user’s token.

To find out the address to get the token, go to the home page of the Realm Settings (the menu has the same label), and click on OpenID Endpoint Configuration in the Endpoint section: Keycloak Realm Settings

You should see something like this: Keycloak Realm OpenID Endpoint Configuration

It contains data in JSON format, and the URL that we need is in the token_endpoint key, which in this case is http://keycloak:8080/realms/master/protocol/openid-connect/token address.

Now, let’s run the test to get an Access Token:

$ http --form POST http://keycloak:8080/realms/master/protocol/openid-connect/token client_id='tyk-gateway' client_secret='2pNW67LfoXC5KqsEtlBaTiodRCV5swGg' username='delivery-user1' password='1234' grant_type='password'

Reference: https://www.keycloak.org/docs/latest/authorization_services/

Note: the client_secret is the one I’ve mentioned we need for tyk.

So, it’s the result:

HTTP/1.1 200 OK
Cache-Control: no-store
Content-Type: application/json
Pragma: no-cache
Referrer-Policy: no-referrer
Set-Cookie: KEYCLOAK_LOCALE=; Version=1; Comment=Expiring cookie; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/master/; HttpOnly
Set-Cookie: KC_RESTART=; Version=1; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/master/; HttpOnly
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
content-length: 2226

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHaXItNGgwTTdibElDZmhfbFNiMWdsek9lZHhSc09DZ2NKbTVIbUg5Q1pjIn0.eyJleHAiOjE2NTUwNjEzMTAsImlhdCI6MTY1NTA2MTI1MCwianRpIjoiNjZlMmVjY2ItYjg4OC00YThhLWEyNjUtZTIzNTZiZjQ0OTBmIiwiaXNzIjoiaHR0cDovL2tleWNsb2FrOjgwODAvcmVhbG1zL21hc3RlciIsImF1ZCI6WyJ0eWstZ2F0ZXdheSIsImFjY291bnQiXSwic3ViIjoiNmUyODliNzUtYTRmOS00ZmVmLWJhZGEtNmI3YTBiOGMyOGE0IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidHlrLWdhdGV3YXkiLCJzZXNzaW9uX3N0YXRlIjoiYWZmNjI4YWUtN2U3MC00NzAyLTgzNTUtMWU0M2RhYTM5MjcwIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwMDAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtbWFzdGVyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7InR5ay1nYXRld2F5Ijp7InJvbGVzIjpbImRlbGl2ZXJ5Il19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJzaWQiOiJhZmY2MjhhZS03ZTcwLTQ3MDItODM1NS0xZTQzZGFhMzkyNzAiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGVsaXZlcnktdXNlcjEifQ.dCJdSvzetzrem1Y5CcYOqvj09-0iai-TUU59rP_7ejEZK-VdPWiORZlmVpfMOHnv0u_SniPjmEacOxdIN_L4dWcCKUyUvAHtFl-qP0kTTR05cd7z91w2YDaQZz-y4-JTSAZG3x2gcU5j5sWheMxBDx1jFGsqSCdzDukUJE5Nuu_NtHvxgZ0JvkPNs9QOI6eqXGhDRyFKiblodHS_3LjyOg2WOcXzORi7-bTEhE3_SdDZYiy2qrMCtksJPZ4Q67v4IM3W6j9Y2BBjG_XVx-OnMdFHxtT4zezeTbkQE74sI2UzMigZ2QNt6TLDAejN7xhTDBxo8av6I58e4aJlwIj9oQ",
    "expires_in": 60,
    "not-before-policy": 0,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIwODYzZGZmMi01MmQxLTQxMDktYTEyMy0xMjg0ODAyNGMzOGIifQ.eyJleHAiOjE2NTUwNjMwNTAsImlhdCI6MTY1NTA2MTI1MCwianRpIjoiN2Y2ZWE5NTctMmJmNS00M2NjLWFlNTctMTU3NmM4ZDE4MGI0IiwiaXNzIjoiaHR0cDovL2tleWNsb2FrOjgwODAvcmVhbG1zL21hc3RlciIsImF1ZCI6Imh0dHA6Ly9rZXljbG9hazo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJzdWIiOiI2ZTI4OWI3NS1hNGY5LTRmZWYtYmFkYS02YjdhMGI4YzI4YTQiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoidHlrLWdhdGV3YXkiLCJzZXNzaW9uX3N0YXRlIjoiYWZmNjI4YWUtN2U3MC00NzAyLTgzNTUtMWU0M2RhYTM5MjcwIiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiYWZmNjI4YWUtN2U3MC00NzAyLTgzNTUtMWU0M2RhYTM5MjcwIn0.kOjebFQs-uHl_cd5giiZzvI-UTMU5pt0QaFQIaCxmcs",
    "scope": "profile email",
    "session_state": "aff628ae-7e70-4702-8355-1e43daa39270",
    "token_type": "Bearer"
}

It contains all the required data.

If you decode the access_token by https://jwt.io/ website, you’ll see what kind of information it does contain in the payload:

{
  "exp": 1655061310,
  "iat": 1655061250,
  "jti": "66e2eccb-b888-4a8a-a265-e2356bf4490f",
  "iss": "http://keycloak:8080/realms/master",
  "aud": [
    "tyk-gateway",
    "account"
  ],
  "sub": "6e289b75-a4f9-4fef-bada-6b7a0b8c28a4",
  "typ": "Bearer",
  "azp": "tyk-gateway",
  "session_state": "aff628ae-7e70-4702-8355-1e43daa39270",
  "acr": "1",
  "allowed-origins": [
    "http://localhost:8000"
  ],
  "realm_access": {
    "roles": [
      "default-roles-master",
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "tyk-gateway": {
      "roles": [
        "delivery"
      ]
    },
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "profile email",
  "sid": "aff628ae-7e70-4702-8355-1e43daa39270",
  "email_verified": true,
  "preferred_username": "delivery-user1"
}

As it’s clear, in the resource_access, the user role is defined, and aud contains the client name tyk-gateway, which in the next post I’m going to use for a Tyk gateway.