繁体   English   中英

单租户应用程序需要通过 OpenId Owin 动态支持来自多租户提供商的 SSO

[英]Single Tenant Application needs to support SSO from a multi-tenant provider dynamically with OpenId Owin

我正在尝试使用 OpenId 对动态授权 URL 进行身份验证。 存在未知数量的不同{n-tenant}.identityProvider.com授权 URL。 因此,我需要能够通过用户访问我的应用程序的n-tenant ,并在用户尝试登录时动态配置UseOpenIdConnectAuthentication

这意味着我在启动时不知道授权 URL 是什么,因为我不知道要注册哪个n-tenant 我只会在租户尝试访问我的应用程序的登录端点后才知道Authrity URL 是什么,因为它在 URL 中有一个n-tenant值。

我尝试使用RedirectToIdentityProvider通知重新配置n.Options.Authority ,但这不起作用。 此外,在启动时省略Authority配置会导致异常。

如果我对特定的n-tenant进行硬编码,则下面的代码可以正常工作。 但是,我无法弄清楚如何动态配置OpenIdConnectAuthenticationOptions以在其权限 URL 中使用动态n-tenant值。

请注意,所有n-tenant的 ClientId 和 ClientSecret 都是相同的。 只有端点需要是动态的。

我正在使用带有 .NET Framework 4.8 的 ASP.NET 表单

在此处输入图像描述

启动.cs

using IdentityModel.Client;
using Microsoft.AspNet.Identity;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Host.SystemWeb;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Security.Claims;

[assembly: OwinStartup(typeof(MyApplication.Startup))]
namespace MyApplication
{
    public class Startup
    {
        private readonly string _clientId = "CLIENT_ID";
        private readonly string _clientSecret = "CLIENT_SECRET";

        private readonly string _redirectUri = "https://myapplication.com/{n-tenant}/oidc-callback";
        private readonly string _authority = "https://identityprovider.com/{n-tenant}/";
        

        public void Configuration(IAppBuilder app)
        {
            JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

            ConfigureAuth(app);
        }

        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions());


            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
            {
                ClientId = _clientId,
                ClientSecret = _clientSecret,
                Authority = _authority,
                RedirectUri = _redirectUri,
                ResponseType = OpenIdConnectResponseType.Code,
                Scope = OpenIdConnectScope.OpenId,
                TokenValidationParameters = new TokenValidationParameters { NameClaimType = "sub" },
                CallbackPath = new PathString("/{n-tenant}/oidc-callback"),
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = async n =>
                    {
                    },
                    AuthorizationCodeReceived = async n =>
                    {
                        using (var client = new HttpClient())
                        {

                            var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
                            {
                                Address = $"{_authority}/connect/token",
                                ClientId = _clientId,
                                ClientSecret = _clientSecret,
                                Code = n.Code,
                                RedirectUri = _redirectUri
                            });

                            if (tokenResponse.IsError)
                            {
                                throw new Exception(tokenResponse.Error);
                            }

                            n.TokenEndpointResponse = new OpenIdConnectMessage(tokenResponse.Raw);
                        }

                    }
                },
                
            });
        }
}

很难发布一个完全可以帮助您解决问题的答案,因为检查它是否有效的唯一方法是重现您可能不可能的整个环境。

很难为这些库找到任何类型的文档,但通常当我处理任何 OAuth 或 OpenIDConnect 流程时,我会使用这些存储库: KatanaIdentityModel

我认为您关于使用RedirectToIdentityProvider的想法是正确的。 这是在将用户重定向到您的身份服务器之前调用的 Katana 代码中的位置。 因此,您可以修改在构建 Url 时使用的参数。 你可以像这样开始你的 lambda:

RedirectToIdentityProvider = async n =>
{
   if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Authentication)
   {
      n.ProtocolMessage.Parameters["redirect_uri"] = //your whole url here

我认为调试这个地方或在某处记录你在那个上下文中的参数也是一个好主意。 因为我相信你应该有一个类似于IssuerAddress的参数或属性。 这应该是您的身份服务器的地址 - 您也可以修改它。

如果您正确设置这些值,不幸的是整个流程仍然无法正常工作。 这是因为在中间件处理程序中使用CallbackPath来识别到达您的应用程序的请求是否是 OAuth 流的一部分。 您可以在此处找到此代码。 否则,您的AuthorizationCodeReceived处理程序将不会被调用。

所以现在的问题是 - 你真的需要单独的CallbackPath 根据您的问题,您说应用程序将设置像"https://myapplication.com/{n-tenant}/oidc-callback"这样的回调,但实际上发生的是中间件获取代码(在用户登录后) 并在反向通道(服务器到服务器请求)中将其交换为 access_token。 在默认流程中,令牌存储在浏览器 cookie 中 - 在您的场景中,您的所有应用程序看起来都共享相同的 cookie(托管在同一个域上)。 所以很难说用户登录后你的预期行为是什么。

如果在您的场景中这种分离是“必须的”,那么我能看到的唯一路径就是使用 Katana 存储库代码并制作您自己的OpenidConnectAuthenticationHandler实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM