在前两篇文章中,我们介绍了 OIDC 授权码以及授权码增强的 PKCE 模式,本次我们将重点围绕 (Client Credentials) 模式进行讲解 ,Client Credentials 模式是 OIDC 授权模式之一,它是一种用于客户端(应用程序)以自己的名义向 OIDC 服务端获取访问令牌(access token)的认证授权模式,常用于保护 API 或物联网场景。
【Authing是国内唯一以开发者为中心的全场景身份云产品,提供 1000+ API 和所有主流语言 SDK,拥有数十万开发者的社区生态。】
Client Credentials 模式用于进行服务器对服务器间的授权(M2M 授权),期间没有用户的参与。你需要提前创建编程访问账号,并将 AK、SK 密钥对交给你的资源调用方,需要注意的是,各厂商对此实现有所不同,例如 Okta 和 Auth0 对于 Client Credentials 的实现是将 Client ID 和 Client Secret 交给调用方,Authing 则是在应用下创建编程访问账号后将 AK/SK 交给调用方,调用方使用起来并没有什么不同,Authing 的方式更加适合在一个应用下有多个调用方进行管理。
⚠️ Client Credentials 模式不支持 Refresh Token。
整体上,有以下流程:
1.资源调用方将他的凭证 AK、SK 以及需要请求的权限 scope 发送到 Authing 授权端点。
2.如果凭证正确,并且调用方具备资源权限,Authing 为其颁发 AccessToken。
3.调用方携带 access_token 请求资源服务器。
4.资源服务器验证 Token 通过后,返回相关资源。
流程图如下:
老样子,需要先在 Authing 创建应用。
配置授权模式
创建编程访问账号 ,交给你 API 的调用方
注:在用户认证时 scope 所对应的是用户信息,在 AK/SK 获取 Token 时,scope 应该对应的是被授权的 API 权限。
1.1.2.1 Scope 权限规范
Authing 的 scope 权限项目以空格分隔,每一项的格式是
资源:资源标识符:资源操作
资源:资源标识符:资源操作。
以下是 Authing 支持的所有 scope 格式:
1、含义为编号为 1 的书籍资源的读取权限
book:1:read
2、含义为所有书籍资源的读取权限
book:*:read
3、含义为所有书籍资源的读取权限
book:read
4、含义为所有书籍资源的所有操作权限
book:*:*
5、含义为所有书籍资源的所有操作权限
book:*
6、含义为所有书籍资源的所有操作权限
book:
7、含义为所有资源的所有操作权限
*:*:*
8、含义为所有资源的所有操作权限
*:*
9、含义为所有资源的所有操作权限
*
1.1.2.2 在 Authing 权限管理中定义相关资源
我们定义下 book 资源:
1.1.2.3 对编程访问账号进行授权
我们在这里给刚才创建的编程访问账号调用方 A 进行授权,允许调用方 A 以 GET 请求访问 /book API ,并且只能获取 ID 为 20150 的这条订单
POST${host}/oidc/token 获取 Token
POST${host}/oidc/token/introspection 校验 Token
POST${host}/oidc/revocation 吊销 Token
以下要介绍的接口可以通过我们的在线 postman collection 自行 fork 体验
https://app.getpostman.com/run-collection/24730905-5d29e488-719e-4ffe-af21-a7c18298d328?action=collection%2Ffork&collection-url=entityId%3D24730905-5d29e488-719e-4ffe-af21-a7c18298d328%26entityType%3Dcollection%26workspaceId%3D13ff793c-024c-459d-b1f6-87e91c4769ed#?env%5BAuthing%20OIDC%5D=W3sia2V5IjoiaG9zdCIsInZhbHVlIjoiaHR0cHM6Ly9kZWVwbGFuZy5hdXRoaW5nLmNuIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImNsaWVudF9pZCIsInZhbHVlIjoiNjM4MmNmNDg2ZTVhNjk0NDNhZjI5NzFiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImNsaWVudF9zZWNyZXQiLCJ2YWx1ZSI6Ijc3NWMyM2NlMjkwYzkwZDQwNDUxNGU3MDgyMDkzZWIzIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImFjY2Vzc190b2tlbiIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImlkX3Rva2VuIiwidmFsdWUiOiIiLCJlbmFibGVkIjp0cnVlLCJ0eXBlIjoiZGVmYXVsdCJ9LHsia2V5IjoicmVmcmVzaF90b2tlbiIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifV0=
POST${host}/oidc/token
用户在 Authing 侧完成登录操作后, Authing 会将生成的 code 作为参数回调到 redirect_uri 地址,此时通过 code 换 token 接口即可拿到对应的访问令牌 access_token
请求示例:
curl --location 'https://{host}/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id={AccessKey}' \
--data-urlencode 'client_secret={SecretKey}' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=book:20150:GET book:20150:POST'
响应示例(成功):
{
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Il9xcEhRQXFvbXd0Z3BKX2xZVHNtN2FMVEU3YUtJb0tQeFN5by1faDdGUVkifQ.eyJhenAiOiI2M2Y0ODA5OTlkNGY0MDRiZTViNDQ0NGEiLCJhdWQiOiI2M2Y0ODI1ZDhhNWRmNjYyNTU4YjI4MTQiLCJzY29wZSI6ImJvb2s6MjAxNTA6R0VUIiwiaWF0IjoxNjgxNzExNDU2LCJleHAiOjE2ODE3MTIwNTYsImp0aSI6IlBiSks5djNFTS1rS1o3bms4VmdBRms3eVVPRzJES2NwYUQ2M2gxaThmVlkiLCJpc3MiOiJodHRwczovL29pZGMtY2xpZW50LWNyZWRlbnRpYWxzLmF1dGhpbmcuY24vb2lkYyJ9.qPcJU84C9Ztjm5dk-im8ntatPaB5P8j3ZPdW1eoi-V5po8k32jexUemSEHInEfqdxcnY7OyR1pph6JVjehmoCAX6gqA_3fv20hUjnWQNqcZegAiNea4jQbLKlMsYnTQhhJWmzhs64LwCJD1RqQy0VtoL2ZVVfAEpySHWL6TwWVz0AkvQpZbzkF6FRCa03rli_jc1BNtpGUhvNdtGs6xJMMLJZ31dptrLlSSWSQ71t05fqBfEiToN6-JkwKXJedpHBvFWt_-XncQbksdQQc6krTcgaWkrIbv6LblTrtAifXLfOsANweOAG8QoKLh55vSMMBXdzdw-IzXeCDuwQT5P2w",
"token_type":"Bearer",
"expires_in":600,
"scope":"book:20150:GET",
"rejected_scope":"book:20150:POST"
}
这里 scope 是该变成访问账号所允许访问资源,rejected_scope 则是我们申请但是被拒绝的权限,我们刚才给调用方 A 只分配了 book:20150:GET 的权限,所以在我们同时请求 book:20150:GET 和 book:20150:POST 两个权限时,book:20150:POST 权限则会被拒绝,调用方获取到 access_token 后则可携带此 token 调用受保护的API。
响应示例(失败):
{
"error": "server_error",
"error_description": "编程访问账号不存在!"
}
POST${host}/oidc/token/introspection
API 服务器收到接口调用请求后,可以调用此接口来校验 access_token 的有效性以及是否有权限调用对应的API。
此端点接受 access_token、id_token、refresh_token ,并返回一个布尔值,指示它是否处于活动状态。如果令牌处于活动状态,还将返回有关令牌的其他数据。如果 token 无效、过期或被吊销,则认为它处于非活动状态。
access_token 可以使用 RS256 签名算法或 HS256 签名算法进行签名。下面是这两种签名算法的区别:
RS256 是使用RSA算法的一种数字签名算法,它使用公钥/私钥对来加密和验证信息。RS256 签名生成的令牌比 HS256 签名生成的令牌更加安全,因为使用RSA密钥对进行签名可以提供更高的保护级别。使用RS256签名算法的令牌可以使用公钥进行验证,公钥可以通过 JWK 端点获取。
HS256是使用对称密钥的一种数字签名算法。它使用同一个密钥进行签名和验证。HS256 签名算法在性能方面比 RS256 签名算法更快,因为它使用的是对称密钥,而不是使用 RSA 公钥/私钥对来签名和验证。使用 HS256签名算法的令牌可以通过 shared secret (应用密钥)进行验证。
在实际应用中,RS256 算法更加安全,但同时也更加消耗资源,如果系统需要高性能,可以选择 HS256 签名算法。在线校验:
请求参数:
请求示例:
curl --location --request POST 'https://{host}/oidc/token/introspection' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id={应用ID}' \
--data-urlencode 'client_secret={应用密钥}' \
--data-urlencode 'token={ token }' \
--data-urlencode 'token_type_hint=access_token'
校验 access_token 响应示例(校验通过)
{
"active": true,
"aud": "63f4825d8a5df662558b2814",
"client_id": "63f480999d4f404be5b4444a",
"exp": 1681713127,
"iat": 1681712527,
"iss": "https://oidc-client-credentials.authing.cn/oidc",
"jti": "EpveFqLsgskNIre8fK-h0AOK6oBIfZH6erT5iSrRKmd",
"scope": "book:20150:GET",
"token_type": "Bearer"
}
校验 access_token 响应示例(校验未通过):
{
"active": false
}
POST${host}/oidc/token/revocation
API 服务可通过此接口撤销调用方的 access_token 。
请求参数:
请求示例:
curl --location --request POST 'https://{host}/oidc/token/revocation' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id={应用ID}' \
--data-urlencode 'client_secret={应用密钥}' \
--data-urlencode 'token= {token}' \
--data-urlencode 'token_type_hint=access_token'
响应示例(成功):
HTTP 200 OK
响应示例(失败):
{
"error": "xxxx","error_description": "xxxx"
}
本章我们介绍了使用 Client Credentials 模式来保护 API ,至此 OIDC 常用的授权模式我们已经介绍完毕。
【Authing 是国内唯一以开发者为中心的全场景身份云产品,提供 1000+ API 和所有主流语言 SDK,拥有数十万开发者的社区生态。】
以下是 OIDC & OAuth2.0 认证协议最佳实践系列文章目录,点击链接查看原文:
OIDC & OAuth2.0 协议及其授权模式详解|认证协议最佳实践系列【1】
OIDC & OAuth2.0 授权码模式接入 Authing | 认证协议最佳实践系列【2】
编辑于 2023-04-27 11:45