oauth-2.0 如何在Keycloak中生成短期服务帐户访问令牌

j1dl9f46  于 2022-10-31  发布在  其他
关注(0)|答案(2)|浏览(262)

我正在为Keycloak文档中描述的服务帐户生成一个访问令牌。

curl --location --request POST 'http://my.domain/auth/realms/aRealm/protocol/openid-connect/token' \
--header 'Authorization: Basic YXBcnZlcjpNlpuMwRWk1Y02ZFRE15vVp2YUdHJibmNalVvMFmxzVNQVg==' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials'

我确实得到了一个令牌回来,但它在30分钟内到期,就像我在我的领域设置中配置它们一样,供正常用户使用。
但是对于作为服务帐户使用,我不想要这么长时间的令牌。即使是10-15秒也应该足够生成令牌,然后立即向服务器发出请求。
我在Google auth docs(https://cloud.google.com/iam/docs/create-short-lived-credentials-direct#rest_2)中看到过一个类似的功能,通过传递一个lifetime参数,您可以在其中请求具有特定生命周期的令牌。
Keycloak文档(https://www.keycloak.org/docs/latest/server_admin/#_service_accounts)没有提到任何关于短期令牌的内容。我是否遗漏了提到类似内容的章节?或者是否有其他方法可以创建短期令牌?
编辑:
创建的Keycloak客户端用于用户登录系统。对于这种情况,我希望他们的令牌在再次刷新之前有效30分钟。但是对于服务帐户使用(当我希望我的分布式代码向我自己的服务器发出API请求时),我希望这些令牌具有非常短的生命周期(足以发出API调用)。

3pvhb19x

3pvhb19x1#

查看您的需求,我感觉您将关注点和不同的身份验证流混合在一起。
创建的Keycloak客户端被用户用来登录系统。对于这个用例,我希望他们的令牌在再次刷新之前30分钟内有效。
在不了解有关应用程序的更多详细信息的情况下,此用例通常通过使用***公共客户端***来解决。该客户端通常使用标准流、隐式流或启用的直接访问授权(不推荐)。如果可能,最好使用标准流。这些流也可以用于 * 机密客户端 *--只要机密安全地存储在后端--但是,这不是“非常自然”,也没有必要。

服务帐户流(* 即 * OAuth2术语中的客户端凭据)强制使用 * 机密客户端 *。因此,在请求中使用 client_secret 参数。

因此,我们开始看到最佳实践与您所陈述的要求之间的冲突。
IMO最干净的解决方案是拥有一个 * 公共客户端 *,供用户使用适当的流程进行正常身份验证。在该客户端中,您可以将令牌生命周期设置为30分钟。对于:
但是对于服务帐户使用(当我希望我的分布式代码向我自己的服务器发出API请求时),我希望这些令牌具有非常短的生存期(足以进行API调用)。
对于该用例,请使用启用了服务帐户流的不同客户端 * 机密客户端 *。转到客户端Advanced Settings菜单,并将Access Token Lifespan设置为1分钟(例如)。此值将覆盖在领域级别设置的值(实际上,in practice in can be more complicated than that)。您可以选择的最小时间粒度是分钟,对于秒,您必须使用@ BenchVue所展示的方法。

w8f9ii69

w8f9ii692#

您可以通过PUT HTTP调用更改特定客户端的“访问.令牌.寿命”。
PUT调用**http://my.domain/auth/realms/aRealm/clients/client-id**
不记名令牌需要由具有realms-manage角色的管理员(或主管理员、manage-clients)获取
x1c 0d1x这是示例my.domain:访问的页面不存在。
a领域:测试
客户端标识:a6654164-d2 f5 - 451 d-8 f8 c-25 f00 f29 eec 5
“访问.令牌.寿命”:十五

curl --location --request PUT 'http://localhost:8180/auth/admin/realms/test/clients/a6654164-d2f5-451d-8f8c-25f00f29eec5' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Accept-Encoding: gzip, deflate, br' \
--header 'Accept-Language: en-US,en;q=0.9' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSQjNmSzRrSC1EWmxjLWQ0ZjB3b3JJM3c5d0ZEY0RidFZTS0xmTlJlcVVRIn0.eyJleHAiOjE2NTgwMDEzNjEsImlhdCI6MTY1Nzk2NTM2MSwianRpIjoiNGU4NzRhZWMtMGVjZC00MjEyLThiYmUtNGUxODM2OTRlMGM5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgwL2F1dGgvcmVhbG1zL21hc3RlciIsInN1YiI6ImFiOWNkMjc3LWFhYWEtNGI1My1iMzdlLTBhNzJiODZmZWI0OSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLWNsaSIsInNlc3Npb25fc3RhdGUiOiI2YjU5MTNlMC1lOGFkLTQzM2MtOTkyOC1lNTBjMjQ0MGNmN2MiLCJhY3IiOiIxIiwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwic2lkIjoiNmI1OTEzZTAtZThhZC00MzNjLTk5MjgtZTUwYzI0NDBjZjdjIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.LhOegp1UbzaWOrNWb5aKhIzatIvNNg0JPbZeKqnumJ2pdZKL3xTei2Uo6GPKg_YRp9G-YAuCVrQFNWbhP1fDBrDbAEvZHT0Ho4OmlysISFIGP3i9Hr1x3uILLPxGks0iiP7RzCiSubtIwNZRl3nro5bRXkEx24F1drD4hWKW95Z9VAFPkSUW3Urk5Hdgm991pUwUdQCPiyyNj7RL2uiHEsSEoypoT2CviZ518dElmnNFmPafg5K_j39atHX5DxwxEvT5cTfgD6Sg3CmrJupE3CfY31N8OfkmBCA__3mOx31btncK4uG9EsYujxSeHxEZhPV0gUwCx7ZykYkfKhl0OQ' \
--header 'Content-Type: application/json' \
--data-raw '{
  "id": "a6654164-d2f5-451d-8f8c-25f00f29eec5",
  "clientId": "my-test-client",
  "surrogateAuthRequired": false,
  "enabled": true,
  "alwaysDisplayInConsole": false,
  "clientAuthenticatorType": "client-secret",
  "redirectUris": [
    "http://localhost:8180/test/*"
  ],
  "webOrigins": [],
  "notBefore": 0,
  "bearerOnly": false,
  "consentRequired": false,
  "standardFlowEnabled": true,
  "implicitFlowEnabled": false,
  "directAccessGrantsEnabled": true,
  "serviceAccountsEnabled": true,
  "authorizationServicesEnabled": true,
  "publicClient": false,
  "frontchannelLogout": false,
  "protocol": "openid-connect",
  "attributes": {
    "access.token.lifespan": 15,
    "saml.multivalued.roles": "false",
    "saml.force.post.binding": "false",
    "frontchannel.logout.session.required": "false",
    "oauth2.device.authorization.grant.enabled": "false",
    "backchannel.logout.revoke.offline.tokens": "false",
    "saml.server.signature.keyinfo.ext": "false",
    "use.refresh.tokens": "true",
    "oidc.ciba.grant.enabled": "false",
    "backchannel.logout.session.required": "true",
    "client_credentials.use_refresh_token": "false",
    "saml.client.signature": "false",
    "require.pushed.authorization.requests": "false",
    "saml.allow.ecp.flow": "false",
    "saml.assertion.signature": "false",
    "id.token.as.detached.signature": "false",
    "client.secret.creation.time": "1657583810",
    "saml.encrypt": "false",
    "saml.server.signature": "false",
    "exclude.session.state.from.auth.response": "false",
    "saml.artifact.binding": "false",
    "saml_force_name_id_format": "false",
    "tls.client.certificate.bound.access.tokens": "false",
    "acr.loa.map": "{}",
    "saml.authnstatement": "false",
    "display.on.consent.screen": "false",
    "token.response.type.bearer.lower-case": "false",
    "saml.onetimeuse.condition": "false",
    "request.uris": null,
    "frontchannel.logout.url": null,
    "default.acr.values": null,
    "oauth2.device.polling.interval": null
  },
  "authenticationFlowBindingOverrides": {},
  "fullScopeAllowed": true,
  "nodeReRegistrationTimeout": -1,
  "protocolMappers": [
    {
      "id": "fc55831d-9f85-4db5-8340-38e246fcfdf0",
      "name": "Client ID",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usersessionmodel-note-mapper",
      "consentRequired": false,
      "config": {
        "user.session.note": "clientId",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "clientId",
        "jsonType.label": "String"
      }
    },
    {
      "id": "8e7e2a2c-90be-41a0-8a75-87099184a4a4",
      "name": "Client IP Address",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usersessionmodel-note-mapper",
      "consentRequired": false,
      "config": {
        "user.session.note": "clientAddress",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "clientAddress",
        "jsonType.label": "String"
      }
    },
    {
      "id": "bee09b85-a3db-471a-aa51-9f1504df0a6f",
      "name": "Client Host",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-usersessionmodel-note-mapper",
      "consentRequired": false,
      "config": {
        "user.session.note": "clientHost",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "claim.name": "clientHost",
        "jsonType.label": "String"
      }
    }
  ],
  "defaultClientScopes": [
    "web-origins",
    "acr",
    "roles",
    "profile",
    "email"
  ],
  "optionalClientScopes": [
    "address",
    "phone",
    "offline_access",
    "microprofile-jwt"
  ],
  "access": {
    "view": true,
    "configure": true,
    "manage": true
  }
}'

您可以通过GET HTTP调用检查它的值。“expires_in”:15告诉您生存期
在这里,这个值是15秒。它应该与我通过PUT调用发送的值相匹配。

curl --location --request GET 'http://localhost:8180/auth/admin/realms/test/clients/a6654164-d2f5-451d-8f8c-25f00f29eec5' \
> --header 'Accept: application/json, text/plain, */*' \
> --header 'Accept-Encoding: gzip, deflate, br' \
> --header 'Accept-Language: en-US,en;q=0.9' \
> --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMWkdvVDZ6WFdtMU5BNmh2WXhlbUFrVmFaQWJodnlmWlo4Q2JqRWJ0U3RrIn0.eyJleHAiOjE2NTc5Nzg4MTMsImlhdCI6MTY1Nzk2ODAxMywianRpIjoiOGQ5YjhmYTYtYTk3OS00NGE1LWJhN2YtYTg2M2Q1NjU3NGIwIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJzdWIiOiI1MzkxMDBmMS01ZmEzLTQxMTUtYTZlYi0zOGNhY2NjM2E3NGEiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJzZXNzaW9uX3N0YXRlIjoiNGRmNjQzMDgtYzg5NC00NGYxLTk4YjctYzZmNDg3YTYzYWYzIiwiYWNyIjoiMSIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjRkZjY0MzA4LWM4OTQtNDRmMS05OGI3LWM2ZjQ4N2E2M2FmMyIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6ImZpcnN0IGxhc3QiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyMSIsImdpdmVuX25hbWUiOiJmaXJzdCIsImZhbWlseV9uYW1lIjoibGFzdCIsImVtYWlsIjoidXNlcjFAdGVzdC5jb20ifQ.hjWKdTzSTjJpfW2hRqDOTML_5Uo2s5glAEDzVE567huW0LdYNtElvoi_wdLXNsYwCcVgW8juHsRHdoTlljT91zoZeY9VFg1YUJCF6k6uEkZXgSKvhm87jiyPMQa1Ex_b7wmOza0SFrhz3--PVMKgV6EJ2R1GtbwXfwQeLxsKslaIvjgAIliHNlIkxOA9bTnyFwOpvv93km1E9S3KjExcN1jq-KptslSa8lY35ExyCIatWbu_g9nrWlcQGg14qM8VfcJhrmL4ZDb0uaBk8HbZ52RNU8qu2pB584TsZ5iR29afRF5jTY-3pRiKUnTTOnQDrKLkqPvLhVwJPiRABcClDw'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2910  100  2910    0     0   265k      0 --:--:-- --:--:-- --:--:--  284k{"id":"a6654164-d2f5-451d-8f8c-25f00f29eec5","clientId":"my-test-client","surrogateAuthRequired":false,"enabled":true,"alwaysDisplayInConsole":false,"clientAuthenticatorType":"client-secret","redirectUris":["http://localhost:8180/test/*"],"webOrigins":[],"notBefore":0,"bearerOnly":false,"consentRequired":false,"standardFlowEnabled":true,"implicitFlowEnabled":false,"directAccessGrantsEnabled":true,"serviceAccountsEnabled":true,"authorizationServicesEnabled":true,"publicClient":false,"frontchannelLogout":false,"protocol":"openid-connect","attributes":{"access.token.lifespan":"15","saml.multivalued.roles":"false","saml.force.post.binding":"false","frontchannel.logout.session.required":"false","oauth2.device.authorization.grant.enabled":"false","backchannel.logout.revoke.offline.tokens":"false","saml.server.signature.keyinfo.ext":"false","use.refresh.tokens":"true","oidc.ciba.grant.enabled":"false","backchannel.logout.session.required":"true","client_credentials.use_refresh_token":"false","saml.client.signature":"false","require.pushed.authorization.requests":"false","saml.allow.ecp.flow":"false","saml.assertion.signature":"false","id.token.as.detached.signature":"false","client.secret.creation.time":"1657583810","saml.encrypt":"false","saml.server.signature":"false","exclude.session.state.from.auth.response":"false","saml.artifact.binding":"false","saml_force_name_id_format":"false","tls.client.certificate.bound.access.tokens":"false","acr.loa.map":"{}","saml.authnstatement":"false","display.on.consent.screen":"false","token.response.type.bearer.lower-case":"false","saml.onetimeuse.condition":"false"},"authenticationFlowBindingOverrides":{},"fullScopeAllowed":true,"nodeReRegistrationTimeout":-1,"protocolMappers":[{"id":"fc55831d-9f85-4db5-8340-38e246fcfdf0","name":"Client ID","protocol":"openid-connect","protocolMapper":"oidc-usersessionmodel-note-mapper","consentRequired":false,"config":{"user.session.note":"clientId","id.token.claim":"true","access.token.claim":"true","claim.name":"clientId","jsonType.label":"String"}},{"id":"8e7e2a2c-90be-41a0-8a75-87099184a4a4","name":"Client IP Address","protocol":"openid-connect","protocolMapper":"oidc-usersessionmodel-note-mapper","consentRequired":false,"config":{"user.session.note":"clientAddress","id.token.claim":"true","access.token.claim":"true","claim.name":"clientAddress","jsonType.label":"String"}},{"id":"bee09b85-a3db-471a-aa51-9f1504df0a6f","name":"Client Host","protocol":"openid-connect","protocolMapper":"oidc-usersessionmodel-note-mapper","consentRequired":false,"config":{"user.session.note":"clientHost","id.token.claim":"true","access.token.claim":"true","claim.name":"clientHost","jsonType.label":"String"}}],"defaultClientScopes":["web-origins","acr","roles","profile","email"],"optionalClientScopes":["address","phone","offline_access","microprofile-jwt"],"access":{"view":true,"configure":true,"manage":true}}

相关问题