Stephane Ehret
Stephane Ehret

Reputation: 350

ASP.Net: Mixed authentication Azure AD / Forms

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

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
                {...})

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

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

Therefore in my Web.config I commented out:

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

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

Just calling

 FormsAuthentication.SetAuthCookie(email, false);

is not enough: I'm still getting a redirection to Azure AD Signin page as soon as I call a controller with

[System.Web.Mvc.Authorize]

Plus I'm getting error messages because of

 @Html.AntiForgeryToken()

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!

Upvotes: 2

Views: 1028

Answers (1)

Stephane Ehret
Stephane Ehret

Reputation: 350

Here is the answer for the Startup:

    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
                                        }
                }
        );
    }

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);

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();
    }

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

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

Upvotes: 1

Related Questions