ASP.Net:混合身份验证Azure AD/表单

ASP.Net:混合身份验证Azure AD/表单

问题描述:

我有一个使用表单身份验证的旧版 Asp.Net/MVC/Razor WebApp.

I've got a legacy Asp.Net/MVC/Razor WebApp that uses Forms authentication.

现在,由于某些用户拥有一个Azure AD帐户,我添加了一个特殊的AD登录按钮以及使之正常运行的常用代码

Now, because some users have an Azure AD account, I added a special AD SignIn button plus the usual code to make it work

app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {...})

使用按钮登录后,我进入了URL:

After the sign in using the button, I was getting in the following in the URL:

https://localhost:44361/Account/Index?ReturnUrl=%2fAccount%2fSignIn

因此,在我的Web.config中,我注释掉了:

Therefore in my Web.config I commented out:

<!--<authentication mode="Forms">
  <forms loginUrl="~/Account/Index" timeout="2880" cookieless="UseDeviceProfile" />
</authentication>-->

在此阶段, Azure AD身份验证可以正常工作!但是这样做,我破坏了原始的 Forms 身份验证:-(

At this stage Azure AD authentication works fine! But doing so, I broke the original Forms authentication :-(

只需打电话

 FormsAuthentication.SetAuthCookie(email, false);

还不够:我使用

[System.Web.Mvc.Authorize]

此外,由于以下原因,我收到了错误消息

Plus I'm getting error messages because of

 @Html.AntiForgeryToken()

类型的声明'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'或者'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider'在提供的ClaimsIdentity上不存在.启用防伪令牌支持以及基于声明的身份验证,请验证配置的声明提供程序会在它生成的ClaimsIdentity实例.如果已配置声明提供者而是使用其他声明类型作为唯一标识符,可以通过设置static属性进行配置AntiForgeryConfig.UniqueClaimTypeIdentifier.

A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.

有人可以告诉我如何结合两种身份验证方法吗?谢谢!

Can someone please tell me how to combine both authentication methods? Thank you!

以下是启动的答案:

    public void ConfigureAuth(IAppBuilder app)
    {
        PublicClientId = "self";

        app.UseCookieAuthentication(new CookieAuthenticationOptions
                                    {
                                            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                                            LoginPath = new PathString("/Account/Index/"),
                                            CookieSecure = CookieSecureOption.Always
                                    });

        app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
                                 {
                                         TokenEndpointPath = new PathString("/Token"),
                                         Provider = new ApplicationOAuthProvider(new StatelessRepository(new DataAccessHelper()), PublicClientId),
                                         RefreshTokenProvider = new AuthenticationTokenProvider
                                                                {
                                                                        OnCreate = CreateRefreshToken,
                                                                        OnReceive = RecieveRefreshToken
                                                                },
                                         AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                                         AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
                                         AllowInsecureHttp = true
                                 });

        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                        ClientId = clientId,
                        Authority = authority,
                        RedirectUri = redirectUri,
                        PostLogoutRedirectUri = redirectUri,
                        Scope = OpenIdConnectScope.OpenIdProfile,
                        ResponseType = OpenIdConnectResponseType.CodeIdToken,
                        TokenValidationParameters = new TokenValidationParameters
                                                    {
                                                            ValidateIssuer = true,
                                                            ValidIssuer = $"https://login.microsoftonline.com/{tenant}/v2.0",
                                                            RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
                                                            NameClaimType = "name",
                                                    },
                        Notifications = new OpenIdConnectAuthenticationNotifications
                                        {
                                                AuthenticationFailed = OnAuthenticationFailed,
                                                SecurityTokenValidated = OnAuthenticationSuccessded
                                        }
                }
        );
    }

在Web.config中

Here in the Web.config

<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.8" />
<httpRuntime targetFramework="4.8" />
</system.web>
      <system.webServer>
        <modules>
          <remove name="FormsAuthentication" />
        </modules>
      </system.webServer>

最后,在验证用户凭据之后,在控制器中的此处:

And finally here in the controller, after validating the user credentials:

List<Claim> claims = new List<Claim>
                             {
                                     new Claim(ClaimTypes.Name, partnerUser.Email),
                                     new Claim(ClaimTypes.Email, partnerUser.Email),
                                     new Claim(ClaimTypes.NameIdentifier, partnerUser.Email)
                             };
        ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims,
                DefaultAuthenticationTypes.ApplicationCookie);

        Request.GetOwinContext().Authentication.SignIn(claimsIdentity);

加上SignOut方法:

Plus a SignOut method:

    public void SignOut()
    {
        IAuthenticationManager authenticationManager = HttpContext.GetOwinContext().Authentication;

        foreach (ClaimsIdentity claimsIdentity in authenticationManager.User.Identities)
        {
            switch (claimsIdentity.AuthenticationType)
            {
                case DefaultAuthenticationTypes.ApplicationCookie:
                    authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
                    break;

                case CookieAuthenticationDefaults.AuthenticationType:
                    authenticationManager.SignOut(
                            OpenIdConnectAuthenticationDefaults.AuthenticationType,
                            CookieAuthenticationDefaults.AuthenticationType);
                    break;
            }
        }

        Session.Abandon();
        Session.RemoveAll();
    }

最后,这是Global.asax的一些东西:

And finally, here is something for the Global.asax:

    protected void Application_Start()
    {
        AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
        ...           
    }