如何同时使用内部表单身份验证和 Azure AD 身份验证

How to use both internal Forms authenticationas well as Azure AD authentication

在我的网络应用程序中,我默认实现了表单身份验证。但对于内部用户,我使用 AD 进行身份验证。 我有一个通过 LDAP 公开的本地 AD,我只用它来验证内部用户。

现在我们正计划迁移到 Azure。所以我们已经迁移到 azure AD。

A​​zure AD 的问题是我无法再使用页面内的表单来验证用户身份。我必须将用户重定向到 azure 的身份验证页面 (OWIN OpenIDConnect)。有没有办法让我在我的本地应用程序中拥有 username/password,然后将它们发送到任何 azure api/service 来验证它?

另一个问题是 - 现在我无法在我的 web.config 中使用默认的表单身份验证,或者它永远不会重定向到 azure 的身份验证页面。

有人可以帮忙吗?

注意:它是一个多用户的单租户 WEB 应用程序

问题可以通过使用 OWIN CookieAuthentication 和 ASP.NET identity 来解决,使用 OpenIdConnect 为 AzureAD 处理本地身份验证身份验证。

"The other problem is - Now I cannot use forms authentication as default in my web.config or it never redirects to the azure's authentication page."

如果您未在 web.config 中设置 <authentication mode="none" />,OWIN 无法从 IIS 接管您的应用程序的 Authentication/Authorization 进程。

在您的 Startup class 中,您需要同时设置身份和 OpenIdConnect 管道。

身份

如果您使用个人帐户生成新的 MVC 项目进行身份验证,除了 Account 控制器之外,您还可以看到 Startup class(下面的示例)的自动生成代码其中有 Login 用于直接身份验证和 ExternalLogin (第 3 方)的操作。您还可以通过查看 ExternalLoginExternalLoginCallback 中的操作,了解如何将外部登录提供程序(在您的情况下为 AzureAD)映射到本地用户帐户,即您的应用程序本地身份存储中的帐户。 =18=]控制器。

有关设置本地身份验证最低要求的教程。
Adding-minimal-OWIN-Identity-Authentication-to-an-Existing-ASPNET-MVC-Application

OpenIdConnect

设置 OpenIdConnect 时,如果将 Notification RedirectToIdentityProvider(如下所示)设置为 domain_hint 参数设置为域部分下的已验证域,该域在您的 Azure Active Directory(Azure 管理门户)中具有单点登录,如果已在加入域的计算机上进行身份验证,用户应跳过提示页面。

domain_hint - This parameter is not part of the OpenID Connect standard. Azure AD introduced it to allow you to specify which IdP you want users to authenticate with. Say that your app trusts a federated Azure AD tenant. With the default request, users would first see the Azure AD authentication pages and be redirected to the federated ADFS only after they type their username in the text box. If you send the domain_hint parameter set to the federated domain, the Azure AD page is skipped, and the request goes straight to the ADFS associated with the tenant. If the user is accessing your app from an Intranet, and is thus already authenticated with ADFS, this can actually enable a seamless single sign-on experience.

Modern Authentication with Azure Active Directory for Web Applications p.118


位于您的租户下方的域选项卡


还有其他参数,例如 prompt,可用于控制身份验证流程。

http://openid.net/specs/openid-connect-core-1_0.html

Startup.Auth

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {

        // Configure the db context, user manager and signin manager to use a single instance per request
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
        
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<SystemUserManager, SystemUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });
        
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {

                ClientId = clientId,
                Authority = Authority,
                PostLogoutRedirectUri = postLogoutRedirectUri,

                // Used to skip login prompt for trusted tenant
                
                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    RedirectToIdentityProvider = async (context) =>
                    {
                        context.ProtocolMessage.DomainHint = "someDomainName";
                    },
                }
            });
        }
    }

当未经身份验证的用户尝试访问受保护的代码时,他们将被定向到 Azure 登录页面。处理此问题的一种方法是创建自定义属性并将它们定向到所需的登录页面。

public class ExampleAuthorize : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return httpContext.User.IsInRole(this.Roles);
    }


    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
        {
            {"action", "Login"},{"controller", "Account"}
        });
    }
}

**更新评论**

Resource Owner Password Credentials Grant (i.e., username and password) flow can be used directly as an authorization grant however it should only be used where entirely necessary (see accepted answer) How to accept user credentials programmatically. Example project Authenticating to Azure AD non-interactively using a username & password

如果它只是一个自定义的 AzureAD 登录页面以实现一致的外观,那么添加 AzureAD custom branding 是一个选项。

未测试 -

您可以尝试在 RedirectToIdentityProvider 请求中设置用户名和密码参数。根据 OpenIDConnect Spec 设置 prompt="none" 应该告诉服务器“不显示任何身份验证或同意用户界面页面”(参见代码和注释)。

中间件设置 -

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {

        ClientId = clientId,
        Authority = "https://login.windows.net/yourcompany.onmicrosoft.com",
        PostLogoutRedirectUri = postLogoutRedirectUri,                   
        Notifications = new OpenIdConnectAuthenticationNotifications()
        {                        
            RedirectToIdentityProvider = async (context) =>
            {
                context.ProtocolMessage.Username = context.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary["username"];
                context.ProtocolMessage.Password = context.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary["password"];
                
                // Its my understanding that setting prompt="none" should  tell the server not to display any authentication or consent user interface pages however this has not worked for me                 
                // context.ProtocolMessage.Prompt = "none";
            },
        }
    });
    

在您的登录操作中 -

public void Login(string username, string password, string redirectUri, string loginProvider)
{
    AuthenticationProperties properties = new AuthenticationProperties();       
    properties.RedirectUri = RedirectUri;
    properties.Dictionary.Add("username", username);
    properties.Dictionary.Add("password", password);

    context.HttpContext.GetOwinContext().Authentication.Challenge(properties, loginProvider);
}

除了 cID 的解决方案之外,还可以查看用于存储用户的 Azure B2C: https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-overview/

对于 B2C,您有一个 OWIN/OpenID 连接数据库充满了用户,但您必须将现有的表单数据库迁移到那里。

该解决方案的第二部分是预览MSAL库,它结合了AD和B2C的登录

经过大量研究,我得出结论:

1) 在 Web 应用程序中,无法像使用本地 AD ( LDAP)。这不是限制,但可以被视为安全实现。

2) 最多可以使用 RedirectToIdentityProvider 和 domain_hint.

跳过 azure 门户页面

3) ADAL 可以将用户凭据传递给 Azure AD 进行身份验证 仅适用于本机客户端客户端希望允许用户访问另一个资源(网络 api、服务等)

4) 要在基于表单的身份验证之上使用 azure AD,您必须在 web.config 中将身份验证模式设置为 none 并 创建一个新的授权过滤器以重定向未经授权的用户访问基于表单的登录页面。或者您可以使用下面URL中的答案: Redirect user to custom login page when using Azure AD