本文使用最常用的 Authorization Code 的 Code 认证方式,IdP 采用 ZITADEL

首先依赖方会要求提供 discovery endpoint ,具体地址由 IdP 提供,一般为 {idp_domain}/.well-known/openid-configuration ,该链接包含认证所需的很多 endpoint。

然后在授权服务器注册应用,注册完成后会得到 Client IDClient Secret ,注册时填写的 callback 地址由依赖方提供,用于用户完成授权后客户端调用 callback 地址传递授权码。

# 认证流程

# 重定向授权页面

由依赖方在登录页面发起授权请求

1
2
3
4
5
6
{idp_domain}/oauth/v2/authorize
?response_type=code
&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI
&scope=SCOPE
&state=STATE

其中 CLIENT_ID 为注册应用时获得的 Client ID, REDIRECT_URI 为注册应用时填写的 callback 地址, SCOPE 为授权范围,具体范围需要参考 IdP,在 discovery endpointscopes_supported 也可以看到可以申请的授权范围, STATE 为由依赖方产生的随机值,用于防止 CSRF 攻击,在授权完成调用 callback 地址时会附加在链接中,便于依赖方校对。

# 重定向 callback 地址

用户授权完成后重定向至 callback url

1
2
3
{callback_url}
?code=CODE
&state=STATE

其中 CODE 为授权码, STATE 与上文一致

# 请求 Access Token

依赖方后端收到授权码,校验 state 后向 token endpoint 请求 Access Token

后端构建 Authorization 头:

1
Authorization: "Basic " + base64( formUrlEncode(client_id) + ":" + formUrlEncode(client_secret) )
1
2
3
4
5
6
7
8
9
{idp_domain}/oauth/v2/token

Content-Type: application/x-www-form-urlencoded
Authorization: Basic xxxxxxxxxxxxxxxxxxxxx

grant_type=authorization_code
code=CODE
redirect_uri=REDIRECT_URI
scope=SCOPE

CODEREDIRECT_URISCOPE 同上文一致

返回如下 Json:

1
2
3
4
5
6
{
"access_token": "XXXXXXXXXXXXXXXXXXXXXXXX",
"token_type": "Bearer",
"expires_in": 43199,
"id_token": "JWT"
}

ZITADEL refresh_token 需要在 scope 内添加 offline_access ,否则返回内容不会有 refresh_token
ZITADEL id_token 默认不会含有 email,profile 之类的信息(即使 scope 已声明),需要在应用设置,令牌设置内勾选 在 ID Token 中包含用户信息 ,或者直接向 userinfo_endpoint 请求用户信息。

# 获取用户信息

后端向 userinfo_endpoint 请求用户信息

1
2
{idp_domain}/oidc/v1/userinfo
Authorization: Bearer ACCESS_TOKEN

其中 ACCESS_TOKEN 为上文拿到的 Access Token

返回如下 Json

不同 IdP 可能不一样

1
2
3
4
5
6
7
8
9
10
11
{
"sub": "000000000000000000",
"name": "xxxxxxxxxxxxxxx",
"given_name": "xxxxxxxxxxxxx",
"family_name": "xxxxxxxxxxxxxx",
"locale": "zh",
"updated_at": 0000000000000000,
"preferred_username": "xxxx@xxxxxxxxxxxxxx",
"email": "xxxxxxxxxx@xxxxxxxxx.xxxxx",
"email_verified": true
}