| tags: [ Security ] categories: [ Development ]
OAuth 和 OpenID Connect
1. 参考
- https://oauth.net/2/
- https://openid.net/developers/specs/
- https://www.keycloak.org/documentation.html
2. 选型
-
API Gateway: https://landscape.cncf.io/category=api-gateway&format=card-mode&grouping=category
-
OpenID Provider Server
-
OpenID Provider Library
-
Relying Party Server
-
Nginx + zmartzone/lua-resty-openidc(OpenID 官方认证)
- APISIX: https://github.com/apache/apisix/blob/master/doc/plugins/openid-connect.md (还有个 authz-keycloak)
- Kong + nokia/kong-oidc: https://github.com/nokia/kong-oidc , 有两个值得关注的 fork: kong-enhanced-oidc, JoshTheGoldfish/kong-oidc
-
Nginx + 自行实现 Lua 插件
- Kong + openid-connect(商业版): https://docs.konghq.com/hub/kong-inc/openid-connect/ (开源版里的 oauth2 不支持 OIDC)
- Gluu gateway: https://github.com/GluuFederation/gluu-gateway/tree/version_4.2.1/lib/kong/plugins/gluu-openid-connect
- 3scale APIcast: https://github.com/3scale/APIcast/blob/master/gateway/src/apicast/oauth/oidc.lua
-
Envoy
- https://github.com/datawire/ambassador
- https://github.com/solo-io/gloo 使用 coreos/go-oidc
-
Java
-
Go
-
-
Relying Party Library
3. 流程
Relying Party 根据 OpenID Connect Discovery 找到 authorization_endpoint 和 token_endpoint 等。
Web 浏览器通过远程 Relying Party 请求 Resource Server,使用 authorization code flow。
# 1. 通过 Relying Party 请求 Resource Server http://xxx/foo
GET /foo HTTP/1.1
HTTP/1.1 302 Found
Location: http://keycloak/auth/realms/test/protocol/openid-connect/auth?scope=openid&response_type=code&client_id=some-client&redirect_uri=http%3A%2F%2Fxxx%2Fcallback&state=...
# 2. 重定向到 OpenID Provider(Keycloak),显示登录框,然后输入用户名和密码后提交 (标准没有定义这步)
# session_code 嵌在 HTML form 的 action 属性里。
POST /auth/realms/test/login-actions/authenticate?session_code=4CFTbpoDb-vLjI6MGFqj8YvKdif_tHrO9S5voqKcTMo&execution=326a0f64-2d3d-4a68-8d5d-89fae0eb8e6b&client_id=some-client&tab_id=xzJWmZ5C9ZI HTTP/1.1
username=john&password=johnpwd&credentialId=
HTTP/1.1 302 Found
Location: http://xxx/callback?code=xxxxx&state=...&session_state=yyyy
# 3. 回到 Relying Party 的 redirect_uri,relying party 可以根据 state 参数找到原始的请求地址
GET /callback?code=xxxxx&state=...&session_state=yyyy HTTP/1.1
HTTP/1.1 302 Found
Set-Cookie: JSESSIONID=....; path=/foo
Location: http://xxx/foo
# 4. 回到正常请求 Resource Server http://xxx/foo
GET /foo HTTP/1.1
Cookie: JSESSIONID=...
在上面第 3 步里,Relying Party 请求 OpenID Provider 的 token endpoint:
POST /auth/realms/test/protocol/openid-connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ....
grant_type=authorization_code&code=....&redirect_uri=http%3A%2F%2Fxxx%2Fcallback
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "...",
"token_type": "Bearer",
"refresh_token": "...",
"expires_in": 3600,
"id_token": "...jwt..."
}