OIDC

最后更新:2021-12-03

1. 应用介绍

1.1. OIDC是什么?

OIDC,是OpenID Connect的简称,它是基于OAuth2协议的身份认证授权标准协议。 可以简单理解为:OIDC = OAuth2 + (Identity或Authentication)。

使用OIDC的前提是已经掌握了OAuth2。OAuth2是一个授权(Authorization)协议, 但它无法提供完善的身份认证信息(一般使用时将OAuth2与其他框架如Spring Security整合起来对外提供身份认证授权服务)。 OIDC在OAuth2基础上提供了ID Token(OAuth2中有Access Token)来解决用户身份认证的问题, ID Token一般使用JWT格式数据,由于JWT有自包含性,防篡改机制以及适合于网络上传输等特点,使得ID Token能 安全地传递给第三方客户端并能进行校验(verify)。

此外,在OIDC中还提供了诸如Userinfo EndpointDiscovery Endpoint等接口扩展了协议的适用性。

更多关于OIDC的介绍请查看官方站点http://openid.net/connect/

1.2. IDaaS平台OIDC概述

在IDaaS平台中,我们把OIDC作为标准应用协议进行实现,只要部署了IDaaS产品就能在’添加应用’的’标准协议’中找到OIDC的身影,可直接使用。

平台提供的OIDC支持能力如下:

  • Grant Type ,支持authorization_codeimplicitPKCE

  • 密钥轮转,支持不自动轮转每月轮转每季度轮转每年轮转

  • Response Mode,支持queryfragmentform_post

  • 支持自定义access_tokenid_tokenrefresh_token有效时间。

  • 支持配置应用IP白名单。

2. 如何使用

2.1. 管理OIDC应用

2.1.1. 创建OIDC应用

以IT管理员登录IDaaS平台,依次进入:添加应用 -> 标准协议,找到 OIDC应用,点击’添加应用’,如下图:

在打开的添加应用界面上填入需要对应的应用提供的相关信息,主要参数说明:

  • Redirect URI,应用接收code的URI地址,对应OAuth2中参数redirect_uri

  • Response Mode,选择一种即可。

  • Grant Type,选择具体的值即可,若选择PKCE具体请查看’PKCE使用说明’部分内容。

  • 轮转类型,选择OIDC密钥轮换的策略,具体请查看’密钥轮转’部分内容。

输入完各项信息后,点击’提交’即创建完成OIDC应用(若配置有错误在提交时会进行校验并提示信息)。

2.1.2. 修改OIDC应用

提示:默认创建应用成功后的状态为’启用’的,要修改应用需要先把状态禁用。

  • 应用状态可修改为启用或禁用。

  • 可开启或关闭应用二次认证。

  • 点击’修改应用’可更新应用的信息。

  • 点击’删除应用’可删除已有的数据。

2.1.3. 查看OIDC应用

在上图的’应用信息’一栏中点击’查看详情’进入OIDC应用详细信息,如下图:

详情中的很多信息需要复制下来,在调用OIDC API进行集成时需要使用到。

  • Client Id,Client Secret。

  • Discovery Document URL,对应OIDC协议中的Discovery Endpoint

  • OIDC PublicKey,OIDC中第三方客户端用于校验id_token

  • Authorize URL,SP发起认证的URL。

  • 各类token的有效时间值。

  • 应用IP白名单(可选 )。

  • IDP SLO 地址,OIDC退出时的URL地址格式。

2.2. OIDC中的API

在IDaaS平台上创建OIDC应用后,并获取到各类信息后(如 Client Id, Client Secret),需要通过API来进行集成。 下面对提供的各类API进行介绍。

2.2.1. Discovery Endpoint

Discovery Endpoint是OIDC协议中规定的用于获取OIDC各类信息的接口。

URL定义

GET /public/api/application/plugin_oidc/oidc/.well-known/openid-configuration HTTP/1.1

请求示例

https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/.well-known/openid-configuration

响应示例

{
    "response_types_supported": [
        "id_token",
        "code",
        "id_token token"
    ],
    "claims_supported": [
        "sub",
        "aud",
        "scope",
        "iss",
        "nonce",
        "exp",
        "iat",
        "enterpriseId"
    ],
    "jwks_uri": "https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/jwks",
    "grant_types_supported": [
        "authorization_code",
        "implicit",
        "refresh_token",
        "PKCE"
    ],
    "subject_types_supported": [
        "public"
    ],
    "id_token_signing_alg_values_supported": "RS256",
    "scopes_supported": [
        "OPENID"
    ],
    "issuer": "https://{IDaaS_server}/",
    "authorization_endpoint": "https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/authorize",
    "token_endpoint": "https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/token",
    "userinfo_endpoint": "https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/user_info"
}

对于各字段的含义请查看OIDC协议中相关文档:https://openid.net/specs/openid-connect-discovery-1_0.html

提示:Discovery Endpoint 完整地址可在应用详情中获取到(即 Discovery Document URL)。

2.2.2. SP发起认证API(Authorize URL)

提示:Authorize URL 完整地址可在应用详情中获取到(属性名为 Authorize URL),若在Discovery Endpint中authorization_endpoint获取。 针对需要传递state参数校验场景,state传递前需要URL编码,需要校验请URL编码后比对一致性

URL定义

GET /public/api/application/plugin_oidc/oidc/authorize HTTP/1.1

请求参数:

  • scope,一般值为openid

  • response_type,根据Grant Type来决定,可选值有两个id_token,id_token token(Grant Type为implicit时)。

  • client_id,对应应用的Client Id值。

  • state,会话状态值,由发起方提供,认证完成后响应一致的值,防篡改。

  • redirect_uri,应用的Redirect URL值。

Redirect URI 配置为http://baidu.com,Response Mode 配置为query,Grant Type为implicit时

请求示例

https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/authorize?response_type=id_token&scope=openid&client_id=g4lzUvgXgO1KkABsXfJbZyDH&state=xxx&redirect_uri=https://…

响应示例

http://baidu.com/?id_token=xxxxx…&token_type=bearer&state=xxx

请求示例

https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/authorize?response_type=id_token token&scope=openid&client_id=g4lzUvgXgO1KkABsXfJbZyDH&state=xxx&redirect_uri=https://…

响应示例

http://baidu.com/?id_token=xxxxx…&access_token=xxxx…&token_type=bearer&expires_in=7200&state=xxx

SP发起认证时通过浏览器或webview触发,会跳转到IDaaS登录进行认证,认证通过后根据不同的Grant Type进行响应(如响应code)。

2.2.3. 通过code获取token API

通过code获取token API使用在当Grant Typeauthorization_code时,这是第二步骤(第一步骤是上面的’SP发起认证’)。

URL定义

POST /public/api/application/plugin_oidc/oidc/token HTTP/1.1

请求参数:

  • client_id,对应应用的Client Id值。

  • client_secret,对应应用的Client Secret值。

  • code,SP发起认证成功后获取到的code值。

  • grant_type,应用的Grant Type值,如authorization_code

  • redirect_uri,应用的Redirect URL值。

响应示例

响应的数据包括以下几部分内容:

  • access_token

  • id_token

  • expires_in,access_token有效时间,单位:秒,过期了需要通过refresh_token 去刷新。

  • token_type,固定值Bearer

  • refresh_token,刷新token时使用。

此API一般是通过httpclient等工具由第三方应用内部调用IDaaS的API完成,用户使用无感知。


说明:在请求URL中同时未获取到client_idclient_secret时,会采用Oauth2协议的client_secret_basic方式在请求头中继续获取。刷新token接口同理适用。
请求示例

实现方式

HTTP Basic authentication scheme.
提供一个http header:Authorization,
其中内容是:Authorization: Basic Base64_Encoder(<client_id> + ":" + <client_secret>)

2.2.4. 刷新token API

刷新token API与 通过code获取token API是同一个,只是参数有些区别。

URL定义

POST /public/api/application/plugin_oidc/oidc/token HTTP/1.1

请求参数:

  • client_id,对应应用的Client Id值。

  • client_secret,对应应用的Client Secret值。

  • grant_type,固定值refresh_token

  • refresh_token,认证成功后响应的refresh_token值。

响应示例

响应的数据包括以下几部分内容:

  • access_token

  • id_token

  • expires_in,access_token有效时间,单位:秒,过期了需要通过refresh_token 去刷新。

  • token_type,固定值Bearer

  • refresh_token,刷新token时使用。

此API一般是通过httpclient等工具由第三方应用内部调用IDaaS的API完成,用户使用无感知。

2.2.5. 简化模式(implicit)获取token API

简化模式(implicit)获取token API 与 SP发起认证API(Authorize URL)是一样的,只是参数有些区别。

URL定义

GET /public/api/application/plugin_oidc/oidc/authorize HTTP/1.1

请求参数:

  • scope,一般值为openid

  • response_type,可选id_token,code,id_token token(Grant Type为implicit时)。

  • client_id,对应应用的Client Id值。

  • state,会话状态值,由发起方提供,认证完成后响应一致的值,防篡改。

  • redirect_uri,应用的Redirect URL值。

请求示例

https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/authorize?response_type=token&scope=openid&client_id=g4lzUvgXgO1KkABsXfJbZyDH&state=xxx&redirect_uri=https://….

响应示例

 https://localhost:8082/myoidc-client/implicit#access_token=eyJraWQiOiJteW...&expires_in=7200&state=28dd5xxx

SP发起认证时通过浏览器或webview触发,会跳转到IDaaS登录进行认证,认证通过后跳转会redirect_uri,通过hash形式传递token值。

2.2.6. Userinfo Endpoint

Userinfo Endpoint 用于第三方应用获取更多的用户信息,需要有access_token才能访问。

URL定义

GET /public/api/application/plugin_oidc/oidc/user_info HTTP/1.1

请求示例

https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/user_info?access_token={access_token}

响应示例

{
    "sub": "admin",
    "iss": "http://xxxxxx/public/api/application/plugin_oidc/oidc",
    "aud": "xxxxxxx6CPpztCvzN6tjB",
    "uuid": "xxxxxxx2baed8e7a77zqtOGeqBXbm",
    "username": "admin",
    "displayname": "默认管理员",
    "email": "xxxxxx@qq.com",
    "enterpriseuuid": "xxxxxxxxx2680db8c5vO0sEI4Zo",
    "ouid": "xxxxxxxxx2474493775",
    "enterprisename": "test",
    "ouname": "test"
}

主要响应参数说明:

  • sub,一般是IDaaS账户名。

  • email,IDaaS账户邮箱。

  • ouid,账户所在IDaaS组织机构唯一标识。

  • externalId,账户在IDaaS平台的外部Id(可用作账户的唯一标识)。

说明:一般通过校验id_token后可获取用户基本信息(如用户名),需要更多用户信息时才调用此API获取。

GET /public/api/application/plugin_oidc/oidc/user_info/extra HTTP/1.1

请求示例

https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/user_info/extra?access_token={access_token}

响应示例

{
  "extendFields": {
    "age": "30",
    "major": "计算机"
  }
}

主要响应参数说明:

  • extendFields,扩展字典信息

说明:如果需要获取账户的扩展属性(如扩展字典),可通过此接口获得

2.3. PKCE使用说明

2.3.1. 什么是PKCE

PKCE是Proof Key for Code Exchange的缩写,PKCE模式属于授权码(authorization_code)模式的一个扩展,主要适用于无后端服务器来接收和处理Authorization Code授权码的应用。 应用决定加密方式并生成密文,IDP通过校验密文的合法性来判断应用的身份,以此来增强应用端和IDP之间的校验,防止通信劫持。

整体的流程如下图:

更多PKCE信息请访问 https://tools.ietf.org/html/rfc7636

2.3.2. 如何使用PKCE

1.在创建OIDC应用时将Grant Type选择为PKCE

2.在SP发起认证API(Authorize URL)时增加PKCE的参数:

  • code_challenge,加密后的密文

  • code_challenge_method,加密方式,可选值S256plain(不加密)

一个请求示例:

https://{IDaaS_server}/public/api/application/plugin_oidc/oidc/authorize?response_type=code&scope=openid&client_id=g4lzUvgXgO1KkABsXfJbZyDH&state=xxx&redirect_uri=https://...&code_challenge=NWI1YTUxMzgxMjkxNTNlOTdhNjQwMjI3NDYzNDg1ZmM5YTNjZjBjYTk4MTdiZmJiYzU5NmM0MzU4Y2Q5Njc1Yw&code_challenge_method=S256

发起请求后,会跳转到IDaaS登录认证成功后响应code。

3.在IDaaS响应返回code值后,调用获取token的API时要增加PKCE参数:

  • code_verifier,加密的字符串

请求示例如下图:

2.4. 密钥轮转

2.4.1. 密钥轮转作用

密钥轮转一般用于定期更新用于加密/解密(或签名/验签)的Key,以防止Key泄漏增强系统安全性。

OIDC中的密钥轮转需要使用到JWTJWK相关知识,可参考相关协议介绍。

2.4.2. 如何使用

整体的流程图如下所示:

  1. 当SP应用获取到id_token后,会从header中获取到kid值。

  2. 通过kid去检查使用的JWK的kid是否一致(在SP应用中会缓存一份从IDaaS中获取到的JWK)。

  3. 若一致则使用对应的JWK进行校验id_token

  4. 若不一致则通过kid值通过jwks_uri(Discovery Endpoint中提供)去获取新的JWK值并校验id_token

OIDC应用中支持两种方式进行密钥轮转。

方式一:立即轮转。

在应用详情的OIDC PublicKey处点击’立即轮转密钥’即可。

方式二:定时轮转。

定时轮转需要在添加应用时选择具体的’轮转类型’,如下图:

在选定具体的轮转周期后,在到期时的当天凌晨3点IDaaS平台通过定时任务去更新新的密钥。

说明:轮转开始时间以应用的创建时间来计算。

3. 常见QA

3.1. 第三方客户端(SP)如何校验id_token

  1. id_token的校验首先要通过jwks_uri(Discovery Endpoint中响应)获取到 PublicKey,也可以在OIDC应用详情里复制OIDC PublicKey

  2. 访问网站https://jwt.io/libraries找到各种开发语言的library,并根据里面提供的使用文档对id_token进行校验。

3.2. 如何判断轮转密钥成功?

轮转密钥后可将新生成的id_token中的kid值与之前已有的JWK中的kid值进行对比,不一致则说明轮转成功。 也可通过访问网站https://jwt.io复制id_token到 Encoded 框中查看header内容进行查看,如下图:

提示:轮转成功后不能再使用旧的JWK校验id_token,会校验失败。