Skip to content

ErKiran/keycloak_iam_learning

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

keycloak_iam_learning

Initial Lab Setup:

For the initial lab setup, we can spin up a keycloak server with docker.

https://www.keycloak.org/getting-started/getting-started-docker

docker run -p 127.0.0.1:8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:26.5.3 start-dev

In order to test the Oauth 2.0 Flow, we need to setup a client.

Client Basic

Client ID is important, it's like a identifier and whatever action we are performing is linked to the client id. We will be using client_id in all the subsequent request later.

Client Callback Important piece of the information on this image is the redirect url which needs to be set. When we hit the /auth endpoint to get the code and we can use the code to exchange the token. The code is one time use. We will see the flow later.

Oauth was primiraly build for the authorization but the disruption of Oauth was so heavy on the starting years and compaies were exploiting it for the authentication building their own layers on top of OAuth. Due to this scattered and ambigious implementation leads to developement of the OIDC which expans the OAuth and formalize the use as a authentication in a standard way.

That's enough background. Let's see the action.

Keeping this simple for now. Let's create a user without MFA or any overhead. I would do a MFA part on seperate post.

CreateUser

After the user is created we can setup credentials for the user which we will be using in a login process.

CreateUserCredentials

That's all needed to get started.

The starting point for the implementation is a /auth endpoint

auth

Let's do the flow on the browser.

oauth

We got these as a response

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJJQ041R1Rla1Y5akZKS0wxUXA0OXNNbS1XMW8wTEU0c0NGdGZwV1VkTU0wIn0.eyJleHAiOjE3NzEwMzc1MzUsImlhdCI6MTc3MTAzNzIzNSwiYXV0aF90aW1lIjoxNzcxMDM3MjE5LCJqdGkiOiJvbnJ0YWM6ZTAzNjc4NDctODA4My0yYjBlLWQ2NzQtNDMwYmViZWFmOWE5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9taW5pbGFiIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjU3MjRlNzNmLTlkZWYtNDEzNS1hNTczLWNkMGM4ZDc4NDUwZiIsInR5cCI6IkJlYXJlciIsImF6cCI6IndlYi1hcHAiLCJzaWQiOiJlMXRyenliaTFyX1NYTmM1YW1QUFk2WloiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJkZWZhdWx0LXJvbGVzLW1pbmlsYWIiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJUZXN0IFVzZXIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0dXNlciIsImdpdmVuX25hbWUiOiJUZXN0IiwiZmFtaWx5X25hbWUiOiJVc2VyIiwiZW1haWwiOiJ0ZXN0QHVzZXIuaW8ifQ.ZK26i7KS7GXIUVX27H4wvI8JDYlhSMP0meUO3_LOnKTGPamlQe6Z0NzQz7FhHK6KjrExHLu9JGcl4J4udYfy0DZEO6ENL4L3M_jLgJ4J1-RJxHg2o1xd0R9FAWR31GLKZtFdVmwZP71MUjFQveIQftwmPAUgqzmKjpycZ8Npfj9YIuG2c3n52sn28KTwITspDuFVdKD-M0UBbr1kmkPDHrXHjPXwAV9LlTA9ETV8dZ5CmASeSFLnK8Z2RVBwZiCj3WSe9nvryMbGUaG21IzhstDxrkFh-nZK4w3MTAZx-OhwGJK3b9CT-47VxmFpOd3UbYDAlHr8BFb9c2W5IJEsfw",
    "expires_in": 299,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJiZGEyOWY3Yy03MDc4LTQzOTEtODcyNi1mOTVkY2NlZjViMGUifQ.eyJleHAiOjE3NzEwMzkwMzYsImlhdCI6MTc3MTAzNzIzNiwianRpIjoiNjA1ZDAyNGMtYWE0Ny1jYTA2LWMyNDctZTRjNjBhMzliOGUxIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9taW5pbGFiIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9taW5pbGFiIiwic3ViIjoiNTcyNGU3M2YtOWRlZi00MTM1LWE1NzMtY2QwYzhkNzg0NTBmIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6IndlYi1hcHAiLCJzaWQiOiJlMXRyenliaTFyX1NYTmM1YW1QUFk2WloiLCJzY29wZSI6Im9wZW5pZCBhY3IgcHJvZmlsZSByb2xlcyBiYXNpYyBlbWFpbCB3ZWItb3JpZ2lucyJ9.qpdNG_seIQ23IE6nJ6-9f-fo3sQMeaBv4BuzdJKCSkeH8xCo9_suSkAfWL1PBHFqPxU3RzsM3IZNNPrZU3Drsg",
    "token_type": "Bearer",
    "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJJQ041R1Rla1Y5akZKS0wxUXA0OXNNbS1XMW8wTEU0c0NGdGZwV1VkTU0wIn0.eyJleHAiOjE3NzEwMzc1MzUsImlhdCI6MTc3MTAzNzIzNiwiYXV0aF90aW1lIjoxNzcxMDM3MjE5LCJqdGkiOiIwOGIwNWZjZS0zMmM3LTYyNzUtMmQ2Yi0wOGNkMWQ0ZTcyMTkiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL21pbmlsYWIiLCJhdWQiOiJ3ZWItYXBwIiwic3ViIjoiNTcyNGU3M2YtOWRlZi00MTM1LWE1NzMtY2QwYzhkNzg0NTBmIiwidHlwIjoiSUQiLCJhenAiOiJ3ZWItYXBwIiwic2lkIjoiZTF0cnp5Ymkxcl9TWE5jNWFtUFBZNlpaIiwiYXRfaGFzaCI6InBFbWVmcTBEdzFuUnNTNWpnSFA4TEEiLCJhY3IiOiIxIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiVGVzdCBVc2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdHVzZXIiLCJnaXZlbl9uYW1lIjoiVGVzdCIsImZhbWlseV9uYW1lIjoiVXNlciIsImVtYWlsIjoidGVzdEB1c2VyLmlvIn0.eqFKT_p4Ge35CEDn-P-kD9mMTtO5nq0XAsvGK578Ca2t9jVhE-Gjjaa0CaM2XAIl79Q0HfGy9NJQdGO5VFb6xiI-XxZr4YyOcohsAl8dEJJuNMCPp4RyQem8kgvqzckBl-s6nONHrtXYKeWQ3K5xLUd-g63HWW2o928j9jxQNXXDNdHT4mpnbvpXOsCTcp3Y4GfFQojT32wo_N_RG0nT9iqclt5cSNDhYIM_penjIJIk_xwSZURWibUFhvzYHqgXcrgjd56LibQaRk7NJEroxqYeEXwYYDoFVPHfzJS3niM3enQXFQ4epbfhWz8VS-K2tnLX9jWHbYjfi6ijQnTe5w",
    "not-before-policy": 0,
    "session_state": "e1trzybi1r_SXNc5amPPY6ZZ",
    "scope": "openid profile email"
}

Let's elobarate the access_token in jwt.io

{
  "exp": 1771037535,
  "iat": 1771037235,
  "auth_time": 1771037219,
  "jti": "onrtac:e0367847-8083-2b0e-d674-430bebeaf9a9",
  "iss": "http://localhost:8080/realms/minilab",
  "aud": "account",
  "sub": "5724e73f-9def-4135-a573-cd0c8d78450f",
  "typ": "Bearer",
  "azp": "web-app",
  "sid": "e1trzybi1r_SXNc5amPPY6ZZ",
  "acr": "1",
  "allowed-origins": [
    "http://localhost:3000"
  ],
  "realm_access": {
    "roles": [
      "offline_access",
      "default-roles-minilab",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid profile email",
  "email_verified": false,
  "name": "Test User",
  "preferred_username": "testuser",
  "given_name": "Test",
  "family_name": "User",
  "email": "test@user.io"
}

Referesh Token

{
  "exp": 1771039036,
  "iat": 1771037236,
  "jti": "605d024c-aa47-ca06-c247-e4c60a39b8e1",
  "iss": "http://localhost:8080/realms/minilab",
  "aud": "http://localhost:8080/realms/minilab",
  "sub": "5724e73f-9def-4135-a573-cd0c8d78450f",
  "typ": "Refresh",
  "azp": "web-app",
  "sid": "e1trzybi1r_SXNc5amPPY6ZZ",
  "scope": "openid acr profile roles basic email web-origins"
}

ID Token

{
  "exp": 1771037535,
  "iat": 1771037236,
  "auth_time": 1771037219,
  "jti": "08b05fce-32c7-6275-2d6b-08cd1d4e7219",
  "iss": "http://localhost:8080/realms/minilab",
  "aud": "web-app",
  "sub": "5724e73f-9def-4135-a573-cd0c8d78450f",
  "typ": "ID",
  "azp": "web-app",
  "sid": "e1trzybi1r_SXNc5amPPY6ZZ",
  "at_hash": "pEmefq0Dw1nRsS5jgHP8LA",
  "acr": "1",
  "email_verified": false,
  "name": "Test User",
  "preferred_username": "testuser",
  "given_name": "Test",
  "family_name": "User",
  "email": "test@user.io"
}

So far we have user identity credentials auth code to get the token.

Now, we are implementing the heart of the OAuth Delegation or authorization. We need to create a client_scope.

client_scope

These client scopes are global to the realm meaning it can be attached to any available clients on the realm. To effective impose those scope we need to add that on the clients using

attach_client_scope

That's all we need to worry about for now to make the consent workflow working.

I have built a minimal node.js sever to simulate the transfer money mocked function to show the consent workflow.

Consent_Success

Consent_Failure

About

Learning IAM with KeyCloak

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors