本文使用最常用的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"
}

ZITADELrefresh_token需要在scope内添加offline_access,否则返回内容不会有refresh_token
ZITADELid_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
}