Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Task] New Keycloak Device Flow #400

Closed
oguzcankirmemis opened this issue Jul 6, 2023 · 5 comments
Closed

[Task] New Keycloak Device Flow #400

oguzcankirmemis opened this issue Jul 6, 2023 · 5 comments
Assignees
Labels
enhancement New feature or request

Comments

@oguzcankirmemis
Copy link
Collaborator

oguzcankirmemis commented Jul 6, 2023

Current device authentication is too complex and needs simplification. The new auth flow I propose:

  • Define a client named 'device' in Keycloak. All device tokens will belong to the 'device' client. Tokens in the 'device' client will have necessary client scopes so that a device token can perform its usual operations.
  • Define a second client named 'device-onboarding' in Keycloak. The sole purpose of this client is to provide a mechanism for devices to onboard themselves, without needing access to a user's username and password. The tokens in this client can only do one thing, which is to exchange themselves for a device token.
  • Users can either directly login to 'device' client to manually onboard a device, or login to 'device-onboarding' client to receive a token that can be later used by a device to onboard itself.
  • The login to 'device-onboarding' client is just a regular openid-connect token request, no additions are required.
  • The login to 'device' client is requires four request headers:
    • X-Access-Type: should be set "device" -> mapped to "type" field in token
    • X-DeviceID: should be set -> mapped to "device_id" field in token
    • X-DeviceUID: legacy parameter used by OISP, until removed it should be set to some value (empty string suffices)
    • X-GatewayID: Gateway of the device, if unknown, it should be set to device id
  • With a token from 'device-onboarding' client, a device can send a token-exchange request for the 'device' client to Keycloak. Token-exchange requests to other clients are rejected by default. For an example to see how this is done, please refer to Keycloak docs.
@oguzcankirmemis oguzcankirmemis self-assigned this Jul 6, 2023
oguzcankirmemis added a commit to oguzcankirmemis/DigitalTwin that referenced this issue Jul 6, 2023
@oguzcankirmemis oguzcankirmemis added the enhancement New feature or request label Jul 6, 2023
@oguzcankirmemis
Copy link
Collaborator Author

oguzcankirmemis commented Jul 6, 2023

@wagmarcel I have created a reference implementation at #401. This PR should not break the current applications but allow us to migrate other services(mostly mqtt-gateway) step by step to the new flow.

@oguzcankirmemis
Copy link
Collaborator Author

oguzcankirmemis commented Jul 14, 2023

Here is the example flow, first the user requests a device-onboarding token:

foo@bar:~$  curl -X POST "http://keycloak.local/auth/realms/iff/protocol/openid-connect/token" \
   -d "client_id=device-onboarding" \ 
   -d "username=realm_user" \
   -d "password=HqYOBCcZypuhtPzzJltxjdp0TqgBbIiy" \
   -d "grant_type=password"
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJseHRIcnRUcGRmc1dXQm1QU29Zal9jNnRoNVFfenM5azVNY0Iwc1ZIUmJJIn0.eyJleHAiOjE2ODkzMzU2ODIsImlhdCI6MTY4OTMzNTM4MiwianRpIjoiZDVkNjdiMmEtMzQ0ZC00OTZhLWI1MWUtYzlhZDAyNDM5ZjhkIiwiaXNzIjoiaHR0cDovL2tleWNsb2FrLmxvY2FsL2F1dGgvcmVhbG1zL2lmZiIsInN1YiI6IjU0MmY5MmJiLTZmN2MtNDg1Yy1iYjIxLThlM2RhMWViMWQ4NyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImRldmljZS1vbmJvYXJkaW5nIiwic2Vzc2lvbl9zdGF0ZSI6IjdmMTAxZDYwLTExNDMtNDJmYS05ZWE4LWNmNTZlNDA1MThkZCIsInNjb3BlIjoiIiwic2lkIjoiN2YxMDFkNjAtMTE0My00MmZhLTllYTgtY2Y1NmU0MDUxOGRkIn0.PgGuTtdAHLVOWFCvntNZbM1kmub-kfQf3Xv6PJ-2ZjPSeKWvXA4dc6pXQKfb09dVt8hO0YCP2gyiE8I0SGyH5OrhK_TCG9FZOQ3aUO0-68lRJo9b-KNJRBBX6JPiVcp8evRBz2gHulcp7BTE0ND83GX6t7DPe75Nt_N_VmeavVKdIQrRCUEyKFdanNImgsg1Wg5SCr_IU0E0VdRQByl2q-X1EdBhSGC16EhVlW8NwTUTr2VWnasUubpcvYt7P8TU1l9WukSTbraz-8Ghg5cuoSKgZKjQT8vljXsk5jN2z4jKqjooZilUrUisCV9tHR0YSYofEypbxPv7FacdcolrMw",
  "expires_in":3600,
  "refresh_expires_in":60, 
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1NDhjZTVhYy0yMjhkLTQ3ZmUtYTAxZS1jMmRjNmQ4N2VmODEifQ.eyJleHAiOjIwMDQ2OTUzODIsImlhdCI6MTY4OTMzNTM4MiwianRpIjoiYmRhOGMwYzItNzFlNC00YmI3LWI0ZjMtYjEyZDdiNWY1MjM0IiwiaXNzIjoiaHR0cDovL2tleWNsb2FrLmxvY2FsL2F1dGgvcmVhbG1zL2lmZiIsImF1ZCI6Imh0dHA6Ly9rZXljbG9hay5sb2NhbC9hdXRoL3JlYWxtcy9pZmYiLCJzdWIiOiI1NDJmOTJiYi02ZjdjLTQ4NWMtYmIyMS04ZTNkYTFlYjFkODciLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiZGV2aWNlLW9uYm9hcmRpbmciLCJzZXNzaW9uX3N0YXRlIjoiN2YxMDFkNjAtMTE0My00MmZhLTllYTgtY2Y1NmU0MDUxOGRkIiwic2NvcGUiOiIiLCJzaWQiOiI3ZjEwMWQ2MC0xMTQzLTQyZmEtOWVhOC1jZjU2ZTQwNTE4ZGQifQ.sUE0oIJ9Smm024hJwugDH6AWs5noUEQslKC80gJq0Zc",
  "token_type":"Bearer",
  "not-before-policy":0,
  "session_state":"7f101d60-1143-42fa-9ea8-cf56e40518dd",
  "scope":""
}

The resulting token will be minimal:

{
  "iat": 1689336564,
  "jti": "10da8068-795a-4ed1-8e43-ff8de460bdff",
  "iss": "http://keycloak.local/auth/realms/iff",
  "aud": "http://keycloak.local/auth/realms/iff",
  "sub": "testdevice",
  "typ": "Offline",
  "azp": "device-onboarding",
  "session_state": "2242f745-ce19-46dd-9441-7a95d6f04cde",
  "scope": "oisp-frontend device_id gateway mqtt-broker offline_access type",
  "sid": "2242f745-ce19-46dd-9441-7a95d6f04cde"
}

Also notice that there is a refresh token which only has 60 seconds expire time. This is a limitation in Keycloak, we cannot remove refresh tokens entirely, nevertheless the only thing this refresh token can do is to get another device onboarding token. There should be a workaround to also set the expire time to 0 if desired.

With the onboarding token, we make a token exchange request to get a new device token:

foo@bar:~$ token=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJseHRIcnRUcGRmc1dXQm1QU29Zal9jNnRoNVFfenM5azVNY0Iwc1ZIUmJJIn0.eyJleHAiOjE2ODkzMzU2ODIsImlhdCI6MTY4OTMzNTM4MiwianRpIjoiZDVkNjdiMmEtMzQ0ZC00OTZhLWI1MWUtYzlhZDAyNDM5ZjhkIiwiaXNzIjoiaHR0cDovL2tleWNsb2FrLmxvY2FsL2F1dGgvcmVhbG1zL2lmZiIsInN1YiI6IjU0MmY5MmJiLTZmN2MtNDg1Yy1iYjIxLThlM2RhMWViMWQ4NyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImRldmljZS1vbmJvYXJkaW5nIiwic2Vzc2lvbl9zdGF0ZSI6IjdmMTAxZDYwLTExNDMtNDJmYS05ZWE4LWNmNTZlNDA1MThkZCIsInNjb3BlIjoiIiwic2lkIjoiN2YxMDFkNjAtMTE0My00MmZhLTllYTgtY2Y1NmU0MDUxOGRkIn0.PgGuTtdAHLVOWFCvntNZbM1kmub-kfQf3Xv6PJ-2ZjPSeKWvXA4dc6pXQKfb09dVt8hO0YCP2gyiE8I0SGyH5OrhK_TCG9FZOQ3aUO0-68lRJo9b-KNJRBBX6JPiVcp8evRBz2gHulcp7BTE0ND83GX6t7DPe75Nt_N_VmeavVKdIQrRCUEyKFdanNImgsg1Wg5SCr_IU0E0VdRQByl2q-X1EdBhSGC16EhVlW8NwTUTr2VWnasUubpcvYt7P8TU1l9WukSTbraz-8Ghg5cuoSKgZKjQT8vljXsk5jN2z4jKqjooZilUrUisCV9tHR0YSYofEypbxPv7FacdcolrMw
foo@bar:~$ curl -X POST "http://keycloak.local/auth/realms/iff/protocol/openid-connect/token" \
   -d "client_id=device-onboarding" \
   -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
   -d "subject_token=${token}" \
   -d "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token" \
   -d "audience=device" \
   -H "X-GatewayID: testgw" \
   -H "X-DeviceID: testdevice" \
   -H "X-Access-Type: device" \
   -H "X-DeviceUID: uid"
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJseHRIcnRUcGRmc1dXQm1QU29Zal9jNnRoNVFfenM5azVNY0Iwc1ZIUmJJIn0.eyJleHAiOjE2ODk0MjM2MDQsImlhdCI6MTY4OTMzNzIwNCwianRpIjoiODg0NTc4ZGYtNjY2Mi00NDFjLWFlNzAtYmZlOTZlZDE5MzQ3IiwiaXNzIjoiaHR0cDovL2tleWNsb2FrLmxvY2FsL2F1dGgvcmVhbG1zL2lmZiIsImF1ZCI6WyJvaXNwLWZyb250ZW5kIiwibXF0dC1icm9rZXIiLCJkZXZpY2UiXSwic3ViIjoidGVzdGRldmljZSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImRldmljZS1vbmJvYXJkaW5nIiwic2Vzc2lvbl9zdGF0ZSI6IjIyNDJmNzQ1LWNlMTktNDZkZC05NDQxLTdhOTVkNmYwNGNkZSIsInNjb3BlIjoib2lzcC1mcm9udGVuZCBkZXZpY2VfaWQgZ2F0ZXdheSBtcXR0LWJyb2tlciBvZmZsaW5lX2FjY2VzcyB0eXBlIiwic2lkIjoiMjI0MmY3NDUtY2UxOS00NmRkLTk0NDEtN2E5NWQ2ZjA0Y2RlIiwiZGV2aWNlX2lkIjoidGVzdGRldmljZSIsInR5cGUiOiJkZXZpY2UiLCJnYXRld2F5IjoidGVzdGd3In0.uImPE7-iaEp9gZ70qt32li1xXozCaJm2ZqKmY9wVGIgt0By3NPc_Go-fysWaI9IapRSaYS2yuOl7cyjZvwlAgOY07YhNpC7xzQtguptCn5od9t7zwQyUu28mMLpyZ3nDoltdsg3uvybSkgPOnnKLnp0gNP0VXmmdn61U8C5h2YCJdqlPQwsd2C2O-Tfy0n0QwDiS-007_6o7pMkIT8ulpTTZSrXhG7tdmPVXdFGZUOWq75vVW-yND2oJxeM9W2lVDaNYIcnKxTEYcCeZ_sPmUIK3kMdH5eeGaWhuEs6UEw5oktApNH735rCPLvZhMvvjrkFgax0bSwJK9JS5sq8S_Q",
  "expires_in":86400,
  "refresh_expires_in":0,
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1NDhjZTVhYy0yMjhkLTQ3ZmUtYTAxZS1jMmRjNmQ4N2VmODEifQ.eyJpYXQiOjE2ODkzMzcyMDQsImp0aSI6IjI2Y2VmZDNiLTJmNjktNGM2OC1hY2YwLTM0NmViNTJhNWVlMyIsImlzcyI6Imh0dHA6Ly9rZXljbG9hay5sb2NhbC9hdXRoL3JlYWxtcy9pZmYiLCJhdWQiOiJodHRwOi8va2V5Y2xvYWsubG9jYWwvYXV0aC9yZWFsbXMvaWZmIiwic3ViIjoidGVzdGRldmljZSIsInR5cCI6Ik9mZmxpbmUiLCJhenAiOiJkZXZpY2Utb25ib2FyZGluZyIsInNlc3Npb25fc3RhdGUiOiIyMjQyZjc0NS1jZTE5LTQ2ZGQtOTQ0MS03YTk1ZDZmMDRjZGUiLCJzY29wZSI6Im9pc3AtZnJvbnRlbmQgZGV2aWNlX2lkIGdhdGV3YXkgbXF0dC1icm9rZXIgb2ZmbGluZV9hY2Nlc3MgdHlwZSIsInNpZCI6IjIyNDJmNzQ1LWNlMTktNDZkZC05NDQxLTdhOTVkNmYwNGNkZSJ9.zx3oOu2LpTPsLz6L_peaQYFGSznMItg35K3-3fNakfc",
  "token_type":"Bearer",
  "not-before-policy":0,
  "session_state":"2242f745-ce19-46dd-9441-7a95d6f04cde",
  "scope":"oisp-frontend device_id gateway mqtt-broker offline_access type"
}

The header parameter descriptions:

  • X-Access-Type: must be set to "device"
  • X-DeviceUID: Legacy parameter, must be set to a unique deviceUID, this comes from the GET device endpoint in OISP. To make sure the "accounts" field below is correct, this must be set correctly. If the "accounts" field is unnecessary, this header can be ignored.
  • X-DeviceID: Id of the device, this is mapped to the "sub" field of the token, which is legacy and will be removed. Instead use the "device_id" attribute in the token below.
  • X-GatewayID: gateway id of the device, set it to device id if unknown

The resulting token is a full capable device token, that is compatible with OISP and has a offline refresh token:

{
  "exp": 1689423604,
  "iat": 1689337204,
  "jti": "884578df-6662-441c-ae70-bfe96ed19347",
  "iss": "http://keycloak.local/auth/realms/iff",
  "aud": [
    "oisp-frontend",
    "mqtt-broker",
    "device"
  ],
  "sub": "testdevice",
  "typ": "Bearer",
  "azp": "device-onboarding",
  "session_state": "2242f745-ce19-46dd-9441-7a95d6f04cde",
  "scope": "oisp-frontend device_id gateway mqtt-broker offline_access type",
  "sid": "2242f745-ce19-46dd-9441-7a95d6f04cde",
  "device_id": "testdevice",
  "type": "device",
  "gateway": "testgw",
  "accounts":  [
    "id":  "c6c1c8fe-5e22-4e88-881e-1103fe07e85b",
    "role": "device"
  ]
}

In future, the "accounts" field will be also removed. In general any legacy field coming from OISP that is no longer needed will be removed.

oguzcankirmemis added a commit to oguzcankirmemis/DigitalTwin that referenced this issue Jul 14, 2023
oguzcankirmemis added a commit to oguzcankirmemis/DigitalTwin that referenced this issue Jul 19, 2023
oguzcankirmemis added a commit that referenced this issue Jul 19, 2023
related #400

Signed-off-by: Oguzcan Kirmemis <[email protected]>
@oguzcankirmemis
Copy link
Collaborator Author

oguzcankirmemis commented Jul 21, 2023

DeviceAuthFlow.pdf
Here are the diagrams for the new flows:
DeviceAuthFlow (1)_page-0001

@oguzcankirmemis
Copy link
Collaborator Author

DeviceAuthFlow (1)_page-0002

@oguzcankirmemis
Copy link
Collaborator Author

DeviceAuthFlow (1)_page-0003

@wagmarcel wagmarcel added this to the API stable (beta) milestone Nov 3, 2023
wagmarcel added a commit to wagmarcel/DigitalTwin that referenced this issue Dec 30, 2023
This PR provides a cleaned-up and simplified device agent. The device agent is responsible
to connect to MQTT, manage the device token and forwards metrics provided over local udp/tcp protocol.
The orginial oisp device admin has been removed and replaced by scripts to manage device onboarding
and do test data sending.
The original oisp-agent had many ways to communicate to the backend. This has been removed. Now, only the SparkplugB oriented
data sending for NGSI-LD data is supported. The agent relies only on two services: (1) Keycloak and (2) MQTT.
Since the onboarding only relies on Keycloak, there is a little change on activation procedure. Details are described in the README.

Related Epic: IndustryFusion#432
Related User Story: IndustryFusion#446
Related Tasks: IndustryFusion#400, IndustryFusion#445, IndustryFusion#447

Signed-off-by: marcel <[email protected]>
wagmarcel added a commit to wagmarcel/DigitalTwin that referenced this issue Dec 30, 2023
This PR provides a cleaned-up and simplified device agent. The device agent is responsible
to connect to MQTT, manage the device token and forwards metrics provided over local udp/tcp protocol.
The orginial oisp device admin has been removed and replaced by scripts to manage device onboarding
and do test data sending.
The original oisp-agent had many ways to communicate to the backend. This has been removed. Now, only the SparkplugB oriented
data sending for NGSI-LD data is supported. The agent relies only on two services: (1) Keycloak and (2) MQTT.
Since the onboarding only relies on Keycloak, there is a little change on activation procedure. Details are described in the README.

Related Epic: IndustryFusion#432
Related User Story: IndustryFusion#446
Related Tasks: IndustryFusion#400, IndustryFusion#445, IndustryFusion#447

Signed-off-by: marcel <[email protected]>

fix of copyright years

Signed-off-by: marcel <[email protected]>
abhijith-hr pushed a commit that referenced this issue Jan 3, 2024
This PR provides a cleaned-up and simplified device agent. The device agent is responsible
to connect to MQTT, manage the device token and forwards metrics provided over local udp/tcp protocol.
The orginial oisp device admin has been removed and replaced by scripts to manage device onboarding
and do test data sending.
The original oisp-agent had many ways to communicate to the backend. This has been removed. Now, only the SparkplugB oriented
data sending for NGSI-LD data is supported. The agent relies only on two services: (1) Keycloak and (2) MQTT.
Since the onboarding only relies on Keycloak, there is a little change on activation procedure. Details are described in the README.

Related Epic: #432
Related User Story: #446
Related Tasks: #400, #445, #447

Signed-off-by: marcel <[email protected]>

fix of copyright years

Signed-off-by: marcel <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants