- 1. 什么是OIDC
- 2. OIDC 协议族
- 3. OIDC 核心概念
- 4. OIDC基于Authorization Code认证请求的基本流程
- 5. 常用OIDC endpoints
- 6. 优势
- 7. 参考文档
1. 什么是OIDC
OIDC是OpenID Connect的简称,OIDC=(Identity, Authentication) + OAuth 2.0。它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议
2. OIDC 协议族
-
Core:必选。定义OIDC的核心功能,在OAuth 2.0之上构建身份认证,以及如何使用Claims来传递用户的信息。 http://openid.net/specs/openid-connect-core-1_0.html
-
Discovery:可选。发现服务,使客户端可以动态的获取OIDC服务相关的元数据描述信息(比如支持那些规范,接口地址是什么等等)。 http://openid.net/specs/openid-connect-discovery-1_0.html
-
Dynamic Registration :可选。动态注册服务,使客户端可以动态的注册到OIDC的OP(这个缩写后面会解释)。 http://openid.net/specs/openid-connect-registration-1_0.html
-
OAuth 2.0 Multiple Response Types :可选。针对OAuth2的扩展,提供几个新的response_type。 http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
-
OAuth 2.0 Form Post Response Mode:可选。针对OAuth2的扩展,OAuth2回传信息给客户端是通过URL的querystring和fragment这两种方式,这个扩展标准提供了一基于form表单的形式把数据post给客户端的机制。 http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
-
Session Management :可选。Session管理,用于规范OIDC服务如何管理Session信息。 http://openid.net/specs/openid-connect-session-1_0.html
-
Front-Channel Logout:可选。基于前端的注销机制,使得RP(这个缩写后面会解释)可以不使用OP的iframe来退出。 http://openid.net/specs/openid-connect-frontchannel-1_0.html
-
Back-Channel Logout:可选。基于后端的注销机制,定义了RP和OP直接如何通信来完成注销。 http://openid.net/specs/openid-connect-backchannel-1_0.html
3. OIDC 核心概念
- OIDC 主要术语
- EU:End User:用户。
- RP:Relying Party ,用来代指OAuth2中的受信任的客户端,身份认证和授权信息的消费方;
- OP:OpenID Provider,有能力提供EU认证的服务(比如OAuth2中的授权服务),用来为RP提供EU的身份认证信息;
- ID Token:JWT格式的数据,包含EU身份认证的信息。
- UserInfo Endpoint:用户信息接口(受OAuth2保护),当RP使用Access Token访问时,返回授权用户的信息,此接口必须使用HTTPS。
- OIDC 工作流程
- RP发送一个认证请求给OP;
- OP对EU进行身份认证,然后提供授权;
- OP把ID Token和Access Token(需要的话)返回给RP;
- RP使用Access Token发送一个请求UserInfo EndPoint;
- UserInfo EndPoint返回EU的Claims。
- ID Token: OIDC对OAuth2最主要的扩展就是提供了ID Token。ID Token是一个安全令牌,是一个授权服务器提供的包含用户信息(由一组Cliams构成以及其他辅助的Cliams)的JWT格式的数据结构。ID Token的主要构成部分如下(使用OAuth2流程的OIDC)。
- iss = Issuer Identifier:必须。提供认证信息者的唯一标识。一般是一个https的url(不包含querystring和fragment部分)。
- sub = Subject Identifier:必须。iss提供的EU的标识,在iss范围内唯一。它会被RP用来标识唯一的用户。最长为255个ASCII个字符。
- aud = Audience(s):必须。标识ID Token的受众。必须包含OAuth2的client_id。
- exp = Expiration time:必须。过期时间,超过此时间的ID Token会作废不再被验证通过。
- iat = Issued At Time:必须。JWT的构建的时间。
- auth_time = AuthenticationTime:EU完成认证的时间。如果RP发送AuthN请求的时候携带max_age的参数,则此Claim是必须的。
- nonce:RP发送请求的时候提供的随机字符串,用来减缓重放攻击,也可以来关联ID Token和RP本身的Session信息。
- acr = Authentication Context Class Reference:可选。表示一个认证上下文引用值,可以用来标识认证上下文类。
- amr = Authentication Methods References:可选。表示一组认证方法。
- azp = Authorized party:可选。结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用。
- ID Token通常情况下还会包含其他的Claims(毕竟上述claim中只有sub是和EU相关的,这在一般情况下是不够的,必须还需要EU的用户名,头像等其他的资料,OIDC提供了一组公共的cliams,请移步这里http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)。另外ID Token必须使用JWS进行签名和JWE加密,从而提供认证的完整性、不可否认性以及可选的保密性。
{
"iss": "https://server.example.com",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1311281970,
"iat": 1311280970,
"auth_time": 1311280969,
"acr": "urn:mace:incommon:iap:silver"
}
- 认证
因为OIDC基于OAuth2,所以OIDC的认证流程主要是由OAuth2的几种授权流程延伸而来的,有以下3种:
- Authorization Code Flow:使用OAuth2的授权码来换取Id Token和Access Token。
- Implicit Flow:使用OAuth2的Implicit流程获取Id Token和Access Token。
- Hybrid Flow:混合Authorization Code Flow+Implici Flow。
OAuth2中还有基于Resource Owner Password Credentials Grant和Client Credentials Grant的方式来获取Access Token,为什么OIDC没有扩展这些方式呢?
- Resource Owner Password Credentials Grant是需要用途提供账号密码给RP的,账号密码给到RP了,还要什么自行车(ID Token)。。。
- Client Credentials Grant这种方式根本就不需要用户参与,更谈不上用户身份认证了。这也能反映授权和认证的差异,以及只使用OAuth2来做身份认证的事情是远远不够的,也是不合适的。
4. OIDC基于Authorization Code认证请求的基本流程
4.1 OIDC基于Authorization Code的认证请求
这种方式使用OAuth2的Authorization Code的方式来完成用户身份认证,所有的Token都是通过Token EndPoint(OAuth2中定义:https://tools.ietf.org/html/rfc6749#section-3.2)来发放的。构建一个OIDC的Authentication Request需要提供如下的参数:
- scope:必须。OIDC的请求必须包含值为“openid”的scope的参数。
- response_type:必选。同OAuth2。
- client_id:必选。同OAuth2。
- redirect_uri:必选。同OAuth2。
- state:推荐。同OAuth2。防止CSRF, XSRF。
以上这5个参数是和OAuth2相同的。除此之外,还定义了如下的参数:
- response_mode:可选。OIDC新定义的参数(OAuth 2.0 Form Post Response Mode),用来指定Authorization Endpoint以何种方式返回数据。
- nonce:可选。ID Token中的出现的nonce就是来源于此。
- display : 可选。指示授权服务器呈现怎样的界面给EU。有效值有(page,popup,touch,wap),其中默认是page。page=普通的页面,popup=弹出框,touch=支持触控的页面,wap=移动端页面。
- prompt:可选。这个参数允许传递多个值,使用空格分隔。用来指示授权服务器是否引导EU重新认证和同意授权(consent,就是EU完成身份认证后的确认同意授权的页面)。有效值有(none,login,consent,select_account)。none=不实现现任何认证和确认同意授权的页面,如果没有认证授权过,则返回错误login_required或interaction_required。login=重新引导EU进行身份认证,即使已经登录。consent=重新引导EU确认同意授权。select_account=假如EU在授权服务器有多个账号的话,允许EU选择一个账号进行认证。
- max_age:可选。代表EU认证信息的有效时间,对应ID Token中auth_time的claim。比如设定是20分钟,则超过了时间,则需要引导EU重新认证。
- ui_locales:可选。用户界面的本地化语言设置项。
- id_token_hint:可选。之前发放的ID Token,如果ID Token经过验证且是有效的,则需要返回一个正常的响应;如果有误,则返回对应的错误提示。
- login_hint:可选。向授权服务器提示登录标识符,EU可能会使用它登录(如果需要的话)。比如指定使用用户使用blackheart账号登录,当然EU也可以使用其他账号登录,这只是类似html中input元素的placeholder。
- acr_values:可选。Authentication Context Class Reference values,对应ID Token中的acr的Claim。此参数允许多个值出现,使用空格分割。
以上是基于Authorization Code方式的OIDC的认证请求所需的参数。在OIDC的其他认证流程中也会有其他的参数或不同的参数值(稍有差异)。一个简单的示例如下:
GET /authorize?
response_type=code
&scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1 Host: server.example.com
4.2 基于Authorization Code的认证请求的响应
在授权服务器接收到认证请求之后,需要对请求参数做严格的验证,具体的规则参见http://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation,验证通过后引导EU进行身份认证并且同意授权。在这一切都完成后,会重定向到RP指定的回调地址,并且把code和state参数传递过去。比如:
HTTP/1.1 302 Found
Location: https://client.example.org/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
4.3 基于Authorization Code的认证请求,获取ID Token
RP使用上一步获得的code来请求Token EndPoint,这一步同OAuth2,就不再展开细说了。然后Token EndPoint会返回响应的Token,其中除了OAuth2规定的部分数据外,还会附加一个id_token的字段。id_token字段就是上面提到的ID Token。例如:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6qJp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJNqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7TpdQyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoSK5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"
}
其中看起来一堆乱码的部分就是JWT格式的ID Token。在RP拿到这些信息之后,需要对id_token以及access_token进行验证(具体的规则参见http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation和http://openid.net/specs/openid-connect-core-1_0.html#ImplicitTokenValidation)。至此,可以说用户身份认证就可以完成了,后续可以根据UserInfo EndPoint获取更完整的信息。
4.4 Implicit Flow和Hybrid Flow
Implicit Flow的工作方式是在OAuth2 Implicit Flow上附加提供id_token,当然,认证请求的参数和基于Authorization Code的稍有不同,具体的差异参见http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest,这里就不做详细介绍了。
4.5 Hybrid Flow
Hybrid Flow=Authorization Code Flow+Implicit Flow
4.6 UserInfo Endpoint
UserIndo EndPoint是一个受OAuth2保护的资源。在RP得到Access Token后可以请求此资源,然后获得一组EU相关的Claims,这些信息可以说是ID Token的扩展,比如如果你觉得ID Token中只需包含EU的唯一标识sub即可(避免ID Token过于庞大),然后通过此接口获取完整的EU的信息。此资源必须部署在TLS之上,例如:
GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer SlAV32hkKG
成功之后响应如下:
HTTP/1.1 200 OK
Content-Type: application/json
{
"sub": "248289761001",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"preferred_username": "j.doe",
"email": "janedoe@example.com",
"picture": "http://example.com/janedoe/me.jpg"
}
其中sub代表EU的唯一标识,这个claim是必须的,其他的都是可选的。
5. 常用OIDC endpoints
- Authorization endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/authorize
- Coverage map endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/coverage_mapendpoint/<provider_name>/coverage_map)
- Discovery endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/.well-known/openid-configurationopenid-configuration)
- Introspection endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/introspect
- JSON Web Key (JWK) endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/jwk
- Logout endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/logout
- Registration endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/registrationendpoint/<provider_name>/registration)
- Revocation endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/revoke
- Token endpoint URL: https://<host_name>:<port_number>/oidc/endpoint/<provider_name>/token
6. 优势
- OIDC使得身份认证可以作为一个服务存在。
- OIDC可以很方便的实现SSO(跨顶级域)。
- OIDC兼容OAuth2,可以使用Access Token控制受保护的API资源。
- OIDC可以兼容众多的IDP作为OIDC的OP来使用。
- OIDC的一些敏感接口均强制要求TLS,除此之外,得益于JWT,JWS,JWE家族的安全机制,使得一些敏感信息可以进行数字签名、加密和验证,进一步确保整个认证过程中的安全保障。
7. 参考文档
[认证授权] 4.OIDC(OpenId Connect)身份认证(核心部分): https://www.cnblogs.com/linianhui/p/openid-connect-core.html
https://w3.innovate.ibm.com/tools/sso/home.html
https://github.ibm.com/Danube-Engine/RAM-GUARDS
Oauth2.0: https://tools.ietf.org/html/rfc6749
JWT: https://tools.ietf.org/html/rfc7519
OpenID Connect core: https://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest