Warum ist meine ClaimsIdentity IsAuthenticated immer falsch (für den Web-API-Autorisierungsfilter)?

73

In einem Web-API-Projekt überschreibe ich den normalen Authentifizierungsprozess, um stattdessen Token zu überprüfen. Der Code sieht ungefähr so ​​aus:

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

Und später, wenn ich das [Authorize]Attribut auf einen Controller anwende, kann es nicht autorisiert werden.

Debug-Code bestätigt das gleiche Verhalten:

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

Warum wird der Benutzer nicht authentifiziert, obwohl ich eine gültige ClaimsIdentity erstellt und dem Thread zugewiesen habe?

explunit
quelle

Antworten:

136

Das Problem liegt an einer Änderung in .Net 4.5. Wie in diesem Artikel erläutert, wird IsAuthenticated durch einfaches Erstellen einer Anspruchsidentität nicht mehr als wahr zurückgegeben. Stattdessen müssen Sie eine Zeichenfolge (egal was) an den Konstruktor übergeben.

Also diese Zeile im obigen Code:

var claimsIdentity = new ClaimsIdentity( claims );

Wird dies:

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

Und das Problem ist gelöst. Update: siehe andere Antwort von Leo. Der genaue Wert für AuthenticationType kann wichtig sein oder auch nicht, je nachdem, was Sie sonst noch in Ihrer Authentifizierungspipeline haben.

Update 2: Wie von Robin van der Knaap in den Kommentaren vorgeschlagen, könnte einer der System.Security.Claims.AuthenticationTypesWerte angemessen sein.

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

// and elsewhere in your application...
if (User.Identity.AuthenticationType == AuthenticationTypes.Password) {
    // ...
}
explunit
quelle
9
Obwohl Sie eine beliebige Zeichenfolge hinzufügen können, sollte dies laut MSDN normalerweise einer der in der AuthenticationTypes-Klasse definierten Werte sein. msdn.microsoft.com/en-us/library/…
Robin van der Knaap
1
Beispiel: var ClaimsIdentity = new ClaimsIdentity (Claims, AuthenticationTypes.Password);
Robin van der Knaap
3
Der Wert der Zeichenfolge wird in User.Identity.AuthenticationType
Robin van der Knaap
Wow das ist wirklich dunkel! Vielen Dank, dass Sie dies hier teilen! Ich saß über eine Stunde fest.
David Edwards
15

Die bereitgestellte Antwort hat zwar eine gewisse Gültigkeit, ist jedoch nicht ganz richtig. Sie können nicht davon ausgehen, dass das Hinzufügen einer Zeichenfolge auf magische Weise funktioniert. Wie in einem der Kommentare angegeben, muss diese Zeichenfolge mit einer der AuthenticationTypesAufzählungen übereinstimmen, die wiederum mit der in der OWIN-Authentifizierungs- / Autorisierungs-Middleware angegebenen übereinstimmen muss. Zum Beispiel ...

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

Im obigen Szenario würde es jedoch nicht viel ausmachen. Wenn Sie jedoch mehr Authentifizierungs- / Autorisierungsstufen verwenden, werden die Ansprüche mit denen verknüpft, die mit denselben übereinstimmen. Ein AuthenticationTypeweiteres Beispiel ist die Verwendung der Cookie-Authentifizierung.

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

Wo AuthenticationTypebeschreibt den Namen des Cookies, da Ihre App möglicherweise andere Cookies von anderen Anbietern erhalten hat, ist es wichtig, dass Sie diese bei der AuthenticationTypeInstanziierung der Ansprüche festlegen , um sie dann dem richtigen Cookie zuzuordnen

Löwe
quelle
3
In .NET Core können Sie Konstanten verwenden AuthenticationType, z . B. CookieAuthenticationDefaults.AuthenticationScheme oder JwtBearerDefaults.AuthenticationScheme .
Alex Klaus
2
Beachten Sie, dass ClaimsIdentitySie beim Erstellen den Schemanamen als AuthenticationType(like new ClaimsIdentity(claims, AuthenticationScheme)) übergeben müssen. Andernfalls wird die IsAuthenticatedFlagge auf der Identität sein false. Ich bin in den letzten 2 Jahren zweimal auf dasselbe nicht intuitive Problem gestoßen, und diese Antwort hat mir wieder geholfen. Ich kann diese Antwort leider nicht zweimal positiv bewerten.
Alex Klaus