Search code examples
asp.net.netasp.net-web-apiauthorizationbasic-authentication

Why is IIdentity getting reset / replaced in ASP.NET Web API?


Using the [Authorize] attribute on an ASP.Net Web API method causes a "401 Unauthorized" response.

I have an Http Module that handles the context.AuthenticateRequest event in which I examine the Authorization header (Basic authorization) of the request, and, if valid, set the System.Threading.Thread.CurrentPrincipal to a new GenericPrincipal containing a new GenericIdentity based on the info in the Authorization header. I also set the HttpContext.Current.User to the same instance of GenericPrincipal.

At this point, the IsAuthenticated property of the IIdentity is true. However, by the time the action method in the controller is invoked, System.Threading.Thread.CurrentPrincipal has been set to a System.Security.Claims.ClaimsPrincipal containing a System.Security.Claims.ClaimsIdentity with IsAuthenticated = false.

So... somewhere in the pipeline between the point where I set the CurrentPrincipal and when it reaches the action method, the CurrentPrincipal and the Identity is getting replaced.

Some of the methods of the API access ASP.Net Identity users (for a related website, the API itself does not use ASP.Net Identity for authentication/authorization), so the API project is set up with all the relevant ASP.Net Identity NuGet packages, etc.

I've used the same Http Module in other API projects that DON'T have all the ASP.Net Identity NuGet packages, etc. and it works like a champ.

I suspect that the ASP.Net Identity configuration is causing my Basic Authentication System.Security.Claims.ClaimsPrincipal to be replaced.

Any help would be much appreciated!

Here's my code:
Http Module - at the end of this method, System.Threading.Thread.CurrentPrincipal and HttpContext.Current.User are correctly set.

public class FsApiHttpAuthentication : IHttpModule, IDisposable {
    public void Init( HttpApplication context ) {
        context.AuthenticateRequest += AuthenticateRequests;
        context.EndRequest += TriggerCredentials;
    }

    private static void AuthenticateRequests( object sender, EventArgs e ) {
        string authHeader = HttpContext.Current.Request.Headers["Authorization"];
        if ( authHeader != null ) {
            System.Net.Http.Headers.AuthenticationHeaderValue authHeaderVal = System.Net.Http.Headers.AuthenticationHeaderValue.Parse(authHeader);
            if ( authHeaderVal.Parameter != null ) {
                byte[] unencoded = Convert.FromBase64String(authHeaderVal.Parameter);
                string userpw = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(unencoded);
                string[] creds = userpw.Split(':');
                CredentialCache.Credential cred = CredentialCache.GetCredential(creds[0], creds[1]);
                if ( cred != null ) {
                    System.Security.Principal.GenericIdentity identity = new System.Security.Principal.GenericIdentity
                    (cred.Username);System.Threading.Thread.CurrentPrincipal = new System.Security.Principal.GenericPrincipal(identity, roles);
                    if ( string.IsNullOrWhiteSpace(cred.RolesList) ) {
                         System.Threading.Thread.CurrentPrincipal = new System.Security.Principal.GenericPrincipal(identity, null);
                    } else {
                        System.Threading.Thread.CurrentPrincipal = new System.Security.Principal.GenericPrincipal(identity, cred.RolesList.Split(','));
                    }

                    HttpContext.Current.User = System.Threading.Thread.CurrentPrincipal;
                }
            }
        }
    }

Api Controller - when the Post action in this controller is reached, System.Threading.Thread.CurrentPrincipal and HttpContext.Current.User have been set to a System.Security.Claims.ClaimsPrincipal containing a System.Security.Claims.ClaimsIdentity with IsAuthenticated = false.

public class ConsumerAccountController : ApiController {
    private ApplicationUserManager _userManager;
    private ApplicationUserManager UserManager {
        get {
            return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
        set {
            _userManager = value;
            }
    }

    public async System.Threading.Tasks.Task<IHttpActionResult> Post( API.FinancialSamaritan.com.ViewModels.UserCredentials creds ) {
        API.FinancialSamaritan.com.ViewModels.CreateUserResult cccur = null;
        try {
            string username = creds.Username;
            string password = creds.Password;

            var user = new API.FinancialSamaritan.com.Models.ApplicationUser {
                UserName = username,
                Email = username,
                SecurityQuestion = creds.SecurityQuestion,
                SecurityAnswer = UserManager.PasswordHasher.HashPassword(creds.SecurityAnswer),
                IsPasswordChangeRequired = true,
                EmailConfirmed = true
            };
            IdentityResult userResult = await UserManager.CreateAsync(user, password);
            ...

Solution

  • The [Authorize] attribute was deriving from System.Web.Http.AuthorizeAttribute instead of System.Web.Mvc.AuthorizeAttribute. I had to fully qualify the namespace of the attribute so that MVC version of [Authorize] would be used. Thanks to @RonBrogan for pointing me in the right direction!