Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my ClaimsIdentity IsAuthenticated always false (for web api Authorize filter)?

In a Web API project I am overriding the normal authentication process to check tokens instead. The code looks something like this:

if ( true ) // validate the token or whatever here
{
    var claims = new List<Claim>();
    claims.Add( new Claim( ClaimTypes.Name, "MyUser" ) );
    claims.Add( new Claim( ClaimTypes.NameIdentifier, "MyUserID" ) );
    claims.Add( new Claim( ClaimTypes.Role, "MyRole" ) );

    var claimsIdentity = new ClaimsIdentity( claims );

    var principal = new ClaimsPrincipal( new[] { claimsIdentity } );
    Thread.CurrentPrincipal = principal;
    HttpContext.Current.User = principal;
}

And then later when I apply the [Authorize] attribute to a controller, it fails to authorize.

Debug code confirms the same behavior:

// ALWAYS FALSE!
if ( HttpContext.Current.User.Identity.IsAuthenticated ) {
    // do something
}

Why does it think the user is not authenticated even though I've constructed a valid ClaimsIdentity and assigned it to the thread?

like image 267
explunit Avatar asked Nov 27 '13 23:11

explunit


People also ask

How do you set ClaimsIdentity IsAuthenticated?

To have IsAuthenticated set to true, you need to specify an authentication type in the ctor: var id = new ClaimsIdentity(claims, “Custom”); I am mentioning this, because I just spent 2 hours looking at code that was giving me authorization errors all over the place.

What is ClaimsIdentity in MVC?

The ClaimsIdentity class is a concrete implementation of a claims-based identity; that is, an identity described by a collection of claims.

What is claim principal?

ClaimsPrincipal exposes a collection of identities, each of which is a ClaimsIdentity. In the common case, this collection, which is accessed through the Identities property, will only have a single element.


2 Answers

While the provided answer has some validity in it, it is not entirely correct. You can't assume that just adding any string will magically work. As stated in one of the comment, this string must match one of the AuthenticationTypes enumeration which in turn must match the one specified in the OWIN authentication/authorization middleware....for example...

public void ConfigureOAuth(IAppBuilder app)
        {
            app.UseCors(CorsOptions.AllowAll);

            OAuthAuthorizationServerOptions serverOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new Microsoft.Owin.PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                AuthenticationType = AuthenticationTypes.Password,
                AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
                Provider = new AppAuthServerProvider()
            };


            app.UseOAuthAuthorizationServer(serverOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
                {
                    AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
                    AuthenticationType = AuthenticationTypes.Password
                });            
        }

However, in the above scenario it wouldn't matter much. But, if you are using more authentication/authorization levels the claims will be associated to the one that matches the same AuthenticationType...another example is when you use cookie authentication...

public void Configuration(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "ApplicationCookie",
                LoginPath = new PathString("/auth/login")
            });
        }

where AuthenticationType describes the name of the cookie, since your app may have obtained other cookies from other providers it is important that you set the AuthenticationType when instantiating the claims in order to associate then to the correct cookie

like image 22
Leo Avatar answered Oct 23 '22 13:10

Leo


The problem is because of a breaking change in .Net 4.5. As explained by this article, simply constructing a claims identity no longer makes it IsAuthenticated return true. Instead, you need to pass some string (doesn't matter what) into the constructor.

So this line in the above code:

var claimsIdentity = new ClaimsIdentity( claims );

Becomes this:

// exact string doesn't matter
var claimsIdentity = new ClaimsIdentity( claims, "CustomApiKeyAuth" );

And the problem is solved. Update: see other answer from Leo. The exact AuthenticationType value may or may not be important depending on what else you have in your auth pipeline.

Update 2: as suggested by Robin van der Knaap in the comments, one of the System.Security.Claims.AuthenticationTypes values might be appropriate.

var claimsIdentity = new ClaimsIdentity( claims, AuthenticationTypes.Password );

// and elsewhere in your application...
if (User.Identity.AuthenticationType == AuthenticationTypes.Password) {
    // ...
}
like image 159
explunit Avatar answered Oct 23 '22 14:10

explunit