有条件地使用ADFS进行登录

问题描述 投票:2回答:1

我需要为我们的一个客户集成ADFS。

我们正在使用OWIN和OAuth Bearer令牌以及JWT Bearer Auth。所有这一切都运作良好。输入用户名(电子邮件)和密码,对我们的数据库用户存储进行身份验证,获取持有者令牌。

当输入的用户名在特定域(客户端)时,我们只需要询问客户端的ADFS服务器进行身份验证。 (我们不需要问密码;登录已被修改为首先询问电子邮件,检查它是否是客户端域,然后如果没有,则询问密码 - 看看Box.com是如何做到的,这非常相似。 )

我们目前根本不使用AD并且没有工厂这样做,这意味着所有在网上点缀的“联邦”示例都不会以任何方式帮助我们,因为他们假设您已经在使用ADFS或Azure AD,而我们不是。

我们的移动应用程序正在使用ADAL 1.0库,并且运行良好(它还需要#2才能工作)

1)我无法弄清楚如何将用户的浏览器重定向到客户的ADFS登录页面。一切都使用库,所有这些库都是在编译时配置的 - 而我不知道哪个客户端的ADFS要发送给用户,直到该用户在运行时实际启动登录过程。此外,这不是使用Azure AD,这是一个本地服务器 - 所以我不能使用Microsoft网关。

2)回调需要解析用户的令牌,确保它对我们有意义,通过所有签名检查,然后我们发出我们自己的承载令牌 - 我们不能使用客户端的令牌。这应该相对容易,但我不知道如何告诉ADFS端点回调URL是什么。

是的,我知道这些库的编写原因是为了让它更难搞砸。我没有太多选择 - 这些库想要接管整个过程,但我想有条件地调用它们。

更多信息:

客户端ADFS是3.0

我有能力使用OWIN的身份验证,如果按下,可能会在编译时设置客户端。

asp.net asp.net-mvc authentication adfs
1个回答
0
投票

好。我相信我会为此得到一些废话,但是这里有。

我们最终有条件地使用OAuth2 - 检查用户的电子邮件地址,查看域名,并加载其单点登录体验的配置信息。

我无法找到OWIN的任何OAuth2中间件,我可以有条件地调用(即使我使用字幕和被动模式配置ADAL库也不起作用),所以我最终使用适当的参数手动重定向到IDP。我们的移动开发人员正在使用ADAL 1.0和Xamarin,因此我能够从该工作流程(https://adfs.example.net/oauth2/authorize)获取OAuth2 URL并在那里重定向。

要解决第1步:我创建了一个OAuth2回调端点(必须使用ADFS注册!我有一个查询字符串参数,它提示要加载哪个SSO配置,但根本不能改变 - 它必须始终相同那一个IdP),并设置一个方法来交换令牌的“代码”查询字符串参数。这是一个相当简单的HTTPS调用,但您必须自己验证令牌(这是我的要求第2步所需)并且有一堆代码来挖掘电子邮件地址/用户名声明。您必须设置授权类型以进行传输,并准备处理不同的声明命名空间。请记住通过您的回调网址 - OAuth2标准要求您再次传递,不更改 - 或者您的代码无效。

在使用优秀的System.IdentityModel.Tokens.Jwt库验证令牌是有效令牌之后(您将需要在Stack Overflow上引用有关如何以PEM格式加载X509证书的其他问题,因为我们希望以ascii-armored PEM格式存储IDP的pubkey数据库 - 获取它的代码在这个时候被认为是一个很好的东西),并且是针对预期的受众而且发行者是你期望的那个(如果有人试图弄乱域提示)并且签名匹配(捕获大多数这些早期的情况),并且它与用户输入的电子邮件地址相匹配(这是OWIN'External Cookie'中间件的作用,当您可以使用它时):

然后,只有这样你才能发出一个令牌。

我必须在我们的启动代码中将引用(我们使用Autofac)存储到JWT保护库中:

builder.RegisterInstance(customFormat).As<ISecureDataFormat<AuthenticationTicket>>();

ISecureDataFormat是用于保护/取消保护JWT密钥的注册事项。我无法找到一种方法来利用OWIN身份验证对象来找到在某处找到此实例作为属性的方法,因此我必须在IoC模块中存储对它的引用。

我们的登录代码(自定义身份验证提供程序)如下所示:

        var cookiesIdentity = await _userManager.CreateIdentityAsync(user, CookieAuthenticationDefaults.AuthenticationType);
        Request.GetOwinContext().Authentication.SignIn(new AuthenticationProperties { IsPersistent = true }, cookiesIdentity);

        // login
        var properties = new AuthenticationProperties(new Dictionary<string, string>
        {
            { "userName", globalUser.UserName },
            { "userId", globalUser.Id.ToString() }
        })
        {
            IsPersistent = true,
            IssuedUtc = DateTime.UtcNow,
            ExpiresUtc = DateTime.UtcNow.AddDays(28),
        };

        // jwt
        var oAuthIdentity = await _userManager.CreateIdentityAsync(user, "JWT");
        oAuthIdentity.AddClaim(new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()));
        oAuthIdentity.AddClaim(new Claim(JwtRegisteredClaimNames.Email, user.Email));
        oAuthIdentity.AddClaim(new Claim(JwtRegisteredClaimNames.GivenName, user.Contact.ContactFirstName + " " + user.Contact.ContactLastName));

        Microsoft.Owin.Security.AuthenticationTicket at = new Microsoft.Owin.Security.AuthenticationTicket(oAuthIdentity, properties);
        string jwt = Startup.Resolve<ISecureDataFormat<AuthenticationTicket>>().Protect(at);

字符串'jwt'是实际的原始身份验证票证,我们将其存储在本地存储中以对API进行身份验证。 (是的,我们的应用程序很奇怪。您登录MVC页面,但页面使用JWT令牌调用API,就像移动应用程序一样。它不是SPA,但它的行为类似于一个。)

要解决第2步:Microsoft ADAL库不返回OAuth2进程中的授权代码,而是返回ADFS中的实际令牌。我建立了另一个端点,在第1步解决方案的中间启动进程,并调用相同的方法来验证令牌,提取用户名/电子邮件,并返回持票令牌。 ADAL库已经验证登录表单上的用户名输入与令牌中的声明匹配,但服务器需要在将令牌交换为JWT承载令牌之前验证令牌是否合法。

我相信这可能会容易得多,但说实话,OAuth2并不难以手工完成 - 但只有在你被迫的情况下才能做到这一点。 OWIN中间件经过了充分的测试,涵盖了下个月我需要花费的极端情况 - 它实际上并不支持跳过很多步骤。

© www.soinside.com 2019 - 2024. All rights reserved.