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