[OIDC in Action] 3. 基于OIDC(OpenID Connect)的SSO(添加Github OAuth 2.0的支持)
在上上一篇基于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):
- oauth2.github.aspnetcore这个项目,它基于aspnetcore2实现了Github OAuth 2.0认证。
- oidc-server.test站点,对应的是web.oidc.server.ids4这个项目,引用了上面的这个项目。
- 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内部是怎么实现的呢?这里有两件事情需要处理:
- oidc-server.test要能够识别oidc客户端传递过来的这个参数,如果参数有效,则使用参数指定的OP进行登录,如果没有指定,则采用默认的登录方式(本地的用户和密码体系)。参数是 acr_values(Authentication Context Class Reference values),它是oidc协议规定的一个参数,Ids4实现了对这个参数的支持。
- 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项目中。我们暂且先不关注其内部是如何实现的,这里有两个重要的信息。
- “github”,这是方法的第1个参数,指定了Github作为aspnetcore这个框架种支持的一种认证方式的唯一标识符,也就是一个scheme名字。
- 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等等平台,代码也许不一样,但是核心流程是一样的:
- 即先使用github登录,获取到认证用户的信息。
- 然后利用这些信息链接到自有账号体系,最终使用自有的账号体系完成认证。
- 扩展登录的信息可以根据需要放到发放给客户端的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的支持)的更多相关文章
- [OIDC in Action] 1. 基于OIDC(OpenID Connect)的SSO
在[认证授权]系列博客中,分别对OAuth2和OIDC在理论概念方面进行了解释说明,其间虽然我有写过一个完整的示例(https://github.com/linianhui/oidc.example) ...
- [OIDC in Action] 2. 基于OIDC(OpenID Connect)的SSO(纯JS客户端)
在上一篇基于OIDC的SSO的中涉及到了4个Web站点: oidc-server.dev:利用oidc实现的统一认证和授权中心,SSO站点. oidc-client-hybrid.dev:oidc的一 ...
- OAuth 2.0、OIDC 原理
OAuth 目录 OAuth 什么是 OAuth? 为什么是 OAuth? SAML OAuth 和 API OAuth 主要组件 OAuth 作用域 OAuth 参与者 OAuth 令牌 OAuth ...
- ASP.NET Core 认证与授权[3]:OAuth & OpenID Connect认证
在上一章中,我们了解到,Cookie认证是一种本地认证方式,通常认证与授权都在同一个服务中,也可以使用Cookie共享的方式分开部署,但局限性较大,而如今随着微服务的流行,更加偏向于将以前的单体应用拆 ...
- ASP.NET Core的身份认证框架IdentityServer4(9)-使用OpenID Connect添加用户认证
OpenID Connect OpenID Connect 1.0是OAuth 2.0协议之上的一个简单的身份层. 它允许客户端基于授权服务器执行的身份验证来验证最终用户的身份,以及以可互操作和类似R ...
- .NET Core IdentityServer4实战 第二章-OpenID Connect添加用户认证
内容:本文带大家使用IdentityServer4进行使用OpenID Connect添加用户认证 作者:zara(张子浩) 欢迎分享,但需在文章鲜明处留下原文地址. 在这一篇文章中我们希望使用Ope ...
- IdentityServer4(9)- 使用OpenID Connect添加用户身份验证(implicit)
本文为 OpenID Connect 简化模式(implicit) 已更新至.NET Core 2.2 在本快速入门中,我们希望通过 OpenID Connect 协议向我们的 IdentitySer ...
- 第11章 使用OpenID Connect添加用户身份验证 - Identity Server 4 中文文档(v1.0.0)
在本快速入门中,我们希望通过OpenID Connect协议向我们的IdentityServer添加对交互式用户身份验证的支持. 一旦到位,我们将创建一个将使用IdentityServer进行身份验证 ...
- IdentityServer4 实现 OpenID Connect 和 OAuth 2.0
关于 OAuth 2.0 的相关内容,点击查看:ASP.NET WebApi OWIN 实现 OAuth 2.0 OpenID 是一个去中心化的网上身份认证系统.对于支持 OpenID 的网站,用户不 ...
随机推荐
- emscripten、 WebAssembly,传递字符串给c函数
下面看具体的实例. 下面的代码是一个C函数,实现简单的字符串拼接,然后返回拼接的字符串. #include <stdio.h> #include <string> char* ...
- Linux 桌面玩家指南:06. 优雅地使用命令行及 Bash 脚本编程语言中的美学与哲学
特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...
- 机器学习基石笔记:01 The Learning Problem
原文地址:https://www.jianshu.com/p/bd7cb6c78e5e 什么时候适合用机器学习算法? 存在某种规则/模式,能够使性能提升,比如准确率: 这种规则难以程序化定义,人难以给 ...
- 面向对象(__str__和__repr__方法)
#Author : Kelvin #Date : 2019/1/21 16:19 class App: def __init__(self,name): self.name=name # def __ ...
- 完美解决phpstudy安装后mysql无法启动(无需删除原数据库,无需更改任何配置,无需更改端口)直接共存
PHPstudy与原Mysql兼容解决 一.前言 今天学习php,当然是要先安装好运行环境了,phpstyudy是一个运行php的集成环境, 一键安装对新手很友好,与时作为一个新手,便跟着教程安装了p ...
- 强化学习(二)马尔科夫决策过程(MDP)
在强化学习(一)模型基础中,我们讲到了强化学习模型的8个基本要素.但是仅凭这些要素还是无法使用强化学习来帮助我们解决问题的, 在讲到模型训练前,模型的简化也很重要,这一篇主要就是讲如何利用马尔科夫决策 ...
- [翻译]Mock 在 Python 中的使用介绍
目录 Mock 在 Python 中的使用介绍 原文链接与说明 恐惧系统调用 一个简单的删除函数 使用 Mock 重构 潜在陷阱 向 'rm' 中加入验证 将文件删除作为服务 方法 1:模拟实例的方法 ...
- Windows环境下安装配置Mosquitto服务及入门操作介绍
关键字:在windows安装mosquitto,在mosquitto中配置日志,在mosquitto中配置用户账号密码 关于Mosquitto配置的资料网上还是有几篇的,但是看来看去,基本上都是基于L ...
- [转]AI+RPA 融合更智能
本文转自:https://www.jianshu.com/p/cf25b3dfc0f0 前面已经分析过多次RPA的本质,可以参考 [脱下外衣],看看RPA机器人到底是什么? 哪些AI相关应用技 ...
- AngularJs with Webpackv1 升級到 Webpack4
本篇記錄一下升級的血淚過程 請注意升級前請先創一個新目錄將升級應用與舊應用隔離 1. 需要將相關的套件做統一升級的動作,已確認需要升級所有舊的loaders 其它應用的套件可先不做升級的動作 (如果編 ...