在上上一篇基于OIDC的SSO的登录页面的截图中有出现QQ登录的地方。这个其实是通过扩展OIDC的OpenID Provider来实现的,OpenID Provider简称OP,OP是OIDC的一个很重要的角色,OIDC用它来实现兼容众多的用户认证方式的,比如基于OAuth2,SAML和WS-Federation等等的用户认证方式。关于OP在[认证授权] 4.OIDC(OpenId Connect)身份认证授权(核心部分)(OIDC可以兼容众多的IDP作为OIDC的OP来使用)中有提到过,但是并未详细解释。

由于QQ的开发者账号申请不方便,故而在一下的示例中使用了Github的OAuth 2.0作为替代(原理是一模一样的),源码中已增加对Github OAuth 2.0 的支持

由于dev顶级域名已被Google所持有并且强制Chrome对dev使用https(不便于查看http消息),故而改为了test顶级域名

上一篇博客中的登录时采用的本地的账户和密码来运行的。本篇则为OIDC Server添加一个OP:Github OAuth 2.0。这就使得oidc-server.test可以使Github来登录,并且SSO的客户端可以不做任何改动(除非客户端需要指定采用何种认证方式,即使如此也是非常非常微小的改动)。本篇涉及到的部分有(本系列的源代码位于https://github.com/linianhui/oidc.example):

  1. oauth2.github.aspnetcore这个项目,它基于aspnetcore2实现了Github OAuth 2.0认证。
  2. oidc-server.test站点,对应的是web.oidc.server.ids4这个项目,引用了上面的这个项目。
  3. oidc-client-implicit.test站点,作为oidc的客户端,Github登录的最终消费者(它无需关注Github登录的任何细节)。

1 OIDC-Client

1.1 指定oidc-server.test使用Github认证(可选)

下图是上一篇中起始页面,这次我们点击Oidc Login(Github)这个链接(客户端也可以不指定采用Github进行认证,推迟到进入oidc-server.test之后进行选择)。

我们知道这个链接会返回一个302重定向,重定向的地址是发往oidc-server.test的认证请求,我们看下这个请求和上一次有什么差异:

除了红色部分之外,其他地方并没有任何的不同。那么我们就可以理解为时 acr_values=idp:github (其中idp是Identity Provider的缩写,即身份提供商,和OP的OpenId Provider属于一类含义,只是不同的叫法)这个参数改变了oidc-server.test的认证行为,使其选择了Github进行登录。

至此我们可以得出一个结论,那就是Github登录无需在 oidc-server.test 的客户端这边进行处理,只需指定一个参数即可,比如如果oidc-server.test还支持了微信登录,那么客户端就可以通过传递acr_values=idp:wechat即可直接使用微信登录。但是oidc-server.test内部是怎么实现的呢?这里有两件事情需要处理:

  1. oidc-server.test要能够识别oidc客户端传递过来的这个参数,如果参数有效,则使用参数指定的OP进行登录,如果没有指定,则采用默认的登录方式(本地的用户和密码体系)。参数是 acr_values(Authentication Context Class Reference values),它是oidc协议规定的一个参数,Ids4实现了对这个参数的支持。
  2. oidc-server.test需要支持使用Github进行登录,并且关联到ids4组件。

下面我们看看oidc-server.test这个站点是如何完成这两件事情的。

2 OIDC-Server

2.1 识别客户端发送的IDP信息

oidc-server.test这个站点中,在集成ids4组件的时候,有这么一段代码:

 public static IServiceCollection AddIds4(this IServiceCollection @this)
{
@this
.AddAuthentication()
.AddQQConnect("qq", "QQ Connect", SetQQConnectOptions)
.AddGithub("github", "Github", SetGithubOptions); @this
.AddIdentityServer(SetIdentityServerOptions)
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Resources.AllIdentityResources)
.AddInMemoryApiResources(Resources.AllApiResources)
.AddInMemoryClients(Clients.All)
.AddTestUsers(Users.All); return @this;
} private static void SetGithubOptions(GithubOAuthOptions options)
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = GlobalConfig.Github.ClientId;
options.ClientSecret = GlobalConfig.Github.ClientSecret;
}

AddGithub 这个扩展方法是我自己写的,位于文章开始提到的oauth2.github.aspnetcore项目中。我们暂且先不关注其内部是如何实现的,这里有两个重要的信息。

  1. “github”,这是方法的第1个参数,指定了Github作为aspnetcore这个框架种支持的一种认证方式的唯一标识符,也就是一个scheme名字。
  2. options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; 其含义是把上面指定的github这个认证方式,作为ids4的外部登录来使用。其实ExternalCookieAuthenticationScheme 也是个字符串而已 public const string ExternalCookieAuthenticationScheme = "idsrv.external"; ,这个字符串是ids4定义的一个外部登录的sheme名字。所有的外部登录如果想要和ids4集成,都需要使用它来关联。

2.2 集成Github登录

有了上述两个信息,ids4就可以在接收到 acr_values=idp:github这样的参数时,就可以自动的从aspnetcore框架中已经注册的认证scheme中查找名为gtihub的认证方式,然后来触Github登录的流程。并且在Github认证完成后,进入ids4定义的外部登录流程中。从Fiddler中可以看到这个重定向的过程:

然后Github就打开了它的登录页面:

这部分的控制代码位于GithubOAuthHandler类继承的OAuthHandler基类 BuildChallengeUrl(AuthenticationProperties properties, string redirectUri) 方法中:

 protected virtual string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
{
var scopeParameter = properties.GetParameter<ICollection<string>>(OAuthChallengeProperties.ScopeKey);
var scope = scopeParameter != null ? FormatScope(scopeParameter) : FormatScope(); var state = Options.StateDataFormat.Protect(properties);
var parameters = new Dictionary<string, string>
{
{ "client_id", Options.ClientId },
{ "scope", scope },
{ "response_type", "code" },
{ "redirect_uri", redirectUri },
{ "state", state },
};
return QueryHelpers.AddQueryString(Options.AuthorizationEndpoint, parameters);
}

BuildChallengeUrl 方法返回的URL地址,正是上图中Github的认证页面。

2.3 处理Github OAuth 2.0 的回调&保存Github的用户信息

然后输入账号密码登录Github,随后Github会采用OAuth 2.0的流程,重定向到oidc-server.test的回调地址上。

这个回调地址是标准的OAuth 2的流程,返回了code和state参数,OAuthHandler类的 protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync() 方法会根据code得到github的access_token,然后进一步的获取到github的用户信息(位于GithubOAuthHandler类)。

 protected override async Task<AuthenticationTicket> CreateTicketAsync(
ClaimsIdentity identity,
AuthenticationProperties properties,
OAuthTokenResponse tokens)
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, base.Options.UserInformationEndpoint);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
var httpResponseMessage = await base.Backchannel.SendAsync(httpRequestMessage, base.Context.RequestAborted);
if (!httpResponseMessage.IsSuccessStatusCode)
{
throw new HttpRequestException($"An error occurred when retrieving Github user information ({httpResponseMessage.StatusCode}).");
}
var user = JObject.Parse(await httpResponseMessage.Content.ReadAsStringAsync());
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, base.Context, base.Scheme, base.Options, base.Backchannel, tokens, user);
context.RunClaimActions();
await base.Events.CreatingTicket(context);
return new AuthenticationTicket(context.Principal, context.Properties, base.Scheme.Name);
}

随后把这些信息加密保存到了名为“idsrv.external”(还记得在一开始的时候设置的options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme吧)的cookie中。

2.4. 根据保存的Github用户信息查找已关联的oidc-server.test的用户(或新建)

在上一步保存完github的用户信息到cookie中后,ids4便开始根据github的用户信息查找是否已经绑定了已有的用户,如果没有则新建一个。我这里模拟了一个新建用户的页面(简单的设置了下昵称和用户头像-来自github):

随后,ids4保存这个新用户的信息,并且用它登录系统(并清空保存的github的用户信息)。

2.5 构造id_token & 重定向到客户端

随后的流程就和[OIDC in Action] 1. 基于OIDC(OpenID Connect)的SSO - 第5步时一样的了,这里就不介绍了,完成后客户端或得到了id_token,读取到了其中的github的用户信息。

总结

剖析oidc-server.test如何利用ids4来扩展第三方的登录认证方式。文章中的例子是利用ids4来处理的,其他的比如node.js或者java等等平台,代码也许不一样,但是核心流程是一样的:

  1. 即先使用github登录,获取到认证用户的信息。
  2. 然后利用这些信息链接到自有账号体系,最终使用自有的账号体系完成认证。
  3. 扩展登录的信息可以根据需要放到发放给客户端的idtoken中,但是只是作为辅助信息存在的。

本例只是使用OAuth 2.0(IDP)作为了OIDC的OP,但是并不仅限于此,还支持SAML,WS-Federation,Windows AD,或者常用的手机短信验证码等等方式,其实OIDC并不关系是如何完成用户认证的,它关心的只是得到用户认证的信息后,按照统一的规范的流程把这个认证信息(id_token)安全的给到OIDC的客户端即可。

如有错误指出,欢迎指正!

参考

idp vs op :http://lists.openid.net/pipermail/openid-specs/2006-November/003807.html

acr_values:http://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1

github OAuth文档:https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/

ids4 Sign-in with External Identity Providers:https://identityserver4.readthedocs.io/en/release/topics/signin_external_providers.html

[OIDC in Action] 3. 基于OIDC(OpenID Connect)的SSO(添加Github OAuth 2.0的支持)的更多相关文章

  1. [OIDC in Action] 1. 基于OIDC(OpenID Connect)的SSO

    在[认证授权]系列博客中,分别对OAuth2和OIDC在理论概念方面进行了解释说明,其间虽然我有写过一个完整的示例(https://github.com/linianhui/oidc.example) ...

  2. [OIDC in Action] 2. 基于OIDC(OpenID Connect)的SSO(纯JS客户端)

    在上一篇基于OIDC的SSO的中涉及到了4个Web站点: oidc-server.dev:利用oidc实现的统一认证和授权中心,SSO站点. oidc-client-hybrid.dev:oidc的一 ...

  3. OAuth 2.0、OIDC 原理

    OAuth 目录 OAuth 什么是 OAuth? 为什么是 OAuth? SAML OAuth 和 API OAuth 主要组件 OAuth 作用域 OAuth 参与者 OAuth 令牌 OAuth ...

  4. ASP.NET Core 认证与授权[3]:OAuth & OpenID Connect认证

    在上一章中,我们了解到,Cookie认证是一种本地认证方式,通常认证与授权都在同一个服务中,也可以使用Cookie共享的方式分开部署,但局限性较大,而如今随着微服务的流行,更加偏向于将以前的单体应用拆 ...

  5. ASP.NET Core的身份认证框架IdentityServer4(9)-使用OpenID Connect添加用户认证

    OpenID Connect OpenID Connect 1.0是OAuth 2.0协议之上的一个简单的身份层. 它允许客户端基于授权服务器执行的身份验证来验证最终用户的身份,以及以可互操作和类似R ...

  6. .NET Core IdentityServer4实战 第二章-OpenID Connect添加用户认证

    内容:本文带大家使用IdentityServer4进行使用OpenID Connect添加用户认证 作者:zara(张子浩) 欢迎分享,但需在文章鲜明处留下原文地址. 在这一篇文章中我们希望使用Ope ...

  7. IdentityServer4(9)- 使用OpenID Connect添加用户身份验证(implicit)

    本文为 OpenID Connect 简化模式(implicit) 已更新至.NET Core 2.2 在本快速入门中,我们希望通过 OpenID Connect 协议向我们的 IdentitySer ...

  8. 第11章 使用OpenID Connect添加用户身份验证 - Identity Server 4 中文文档(v1.0.0)

    在本快速入门中,我们希望通过OpenID Connect协议向我们的IdentityServer添加对交互式用户身份验证的支持. 一旦到位,我们将创建一个将使用IdentityServer进行身份验证 ...

  9. IdentityServer4 实现 OpenID Connect 和 OAuth 2.0

    关于 OAuth 2.0 的相关内容,点击查看:ASP.NET WebApi OWIN 实现 OAuth 2.0 OpenID 是一个去中心化的网上身份认证系统.对于支持 OpenID 的网站,用户不 ...

随机推荐

  1. 博客系统typecho的安装与使用

    之前用过wordpress和emlog的博客系统,感觉上wordpress功能强大,插件多,而且也不局限博客网站,就是资源占用比较高,emlog比较简单,资源占用少,就是界面不太喜欢,功能相对也少了些 ...

  2. ubuntu修改键盘映射

    code {margin: 0;padding: 0;font-size: 100%;word-break: normal;background: transparent;border: 0;}ol ...

  3. pandas对Excel文件的读写操作

    1.将Excel数据读为dataframe 1.1 直接读取 df = pd.read_excel('data.xlsx') 1.2 根据sheet索引 xls = pd.ExcelFile('dat ...

  4. markdown实战问题备忘

    问题一:怎么把文档标题放在中间呢? 下面这个能解决问题. 居中: <center>诶嘿</center> 左对齐: <p align="left"&g ...

  5. libevent和libcurl实现http和https服务器 cJSON使用

    前言 libevent和libcurl都是功能强大的开源库:libevent主要实现服务器,包含了select.epoll等高并发的实现:libcurl实现了curl命令的API封装,主要作为客户端. ...

  6. 微信小程序开发07-列表页面怎么做

    接上文:微信小程序开发06-一个业务页面的完成 github地址:https://github.com/yexiaochai/wxdemo 我们首页功能基本完成,我对比了下实际工作中的需求,完成度有7 ...

  7. django-restframework之缓存系统

    django-restframework之缓存系统 一 前言 一 为什么需要缓存 在动态网站中,用户所有的请求,服务器都会去数据库中进行相应的增.删.查.改,渲染模块,执行业务逻辑,最后生成用户看到的 ...

  8. AspNetCoreapi 使用 Docker + Centos 7部署

    好久没有更新文章了,前段时间写了一系列的文章放到桌面了,想着修修改改,后来系统中勒索病毒了还被公司网络安全的抓到是我电脑,后来装系统文章给装丢了.然后好长一段时间没有写了. 今天记录一下AspNetC ...

  9. JS里charCodeAt()和fromCharCode()方法拓展应用:加密与解密

    JS实现客户端的网页加密解密技术,可用作选择性隐蔽展示.当然客户端的加密安全度是不能与服务器相提并论,肯定不能用于密码这类内容的加密,但对于一般级别的内容用作展示已经够了. JS加密与解密的解决方案有 ...

  10. 了解AJAX

    1.如何打开终端的快捷键 Window+R==>CMD==>ipconfig 动态页面:跟后台发生数据交互的页面. 前后台数据交互依赖的一项技术叫 ajax. 1. js的异步操作 (1) ...