web-dev-qa-db-fra.com

Le pipeline d'authentification OWIN et comment utiliser correctement le middleware Katana?

J'ai récemment commencé à regarder le nouveau cadre d'identité ASP.Net et le middleware Katana, il y a une quantité surprenante de code et de documentation, mais je vois ce qui semble être beaucoup d'informations contradictoires, ce qui, je suppose, est un résultat de la fréquence croissante des mises à jour du code.

Je cherche à utiliser l'authentification WsFederation contre un service ADFS 2 interne, mais la façon dont fonctionne le pipeline d'authentification OWIN me laisse un peu perplexe et j'espère que quelqu'un pourra en proposer définitif informations.

Plus précisément, je suis intéressé par l'ordre dans lequel le middleware doit être connecté et les modules requis dans divers scénarios, je voudrais me débarrasser de tout ce qui n'a pas besoin d'être là et en même temps m'assurer que le processus est aussi sûr que possible.

Par exemple, il semblerait que UseWsFederationAuthentication devrait être utilisé en conjonction avec UseCookieAuthentication, mais je ne sais pas quel serait le AuthenticationType correct ( this post suggère que ce n'est qu'une chaîne d'identifiant, mais sa valeur est-elle significative?) ou même si nous devons encore utiliser SetDefaultSignInAsAuthenticationType.

J'ai également remarqué ce fil sur le forum de discussion du projet Katana, où Tratcher mentionne une erreur courante, mais n'est pas très précis quant à la partie du code qui est en erreur.

Personnellement, j'utilise maintenant ce qui suit (avec un gestionnaire de jeton SAML personnalisé pour lire la chaîne de jeton dans un document XML valide), cela fonctionne pour moi, mais est-ce optimal?

var appURI = ConfigurationManager.AppSettings["app:URI"];
var fedPassiveTokenEndpoint = ConfigurationManager.AppSettings["wsFederation:PassiveTokenEndpoint"];
var fedIssuerURI = ConfigurationManager.AppSettings["wsFederation:IssuerURI"];
var fedCertificateThumbprint = ConfigurationManager.AppSettings["wsFederation:CertificateThumbprint"];

var audienceRestriction = new AudienceRestriction(AudienceUriMode.Always);

audienceRestriction.AllowedAudienceUris.Add(new Uri(appURI));

var issuerRegistry = new ConfigurationBasedIssuerNameRegistry();

issuerRegistry.AddTrustedIssuer(fedCertificateThumbprint, fedIssuerURI);

app.UseCookieAuthentication(
    new CookieAuthenticationOptions
    {
        AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType // "Federation"
    }
);

app.UseWsFederationAuthentication(
    new WsFederationAuthenticationOptions
    {
        Wtrealm = appURI,
        SignOutWreply = appURI,
        Configuration = new WsFederationConfiguration
        {
            TokenEndpoint = fedPassiveTokenEndpoint
        },
        TokenValidationParameters = new TokenValidationParameters
        {
            AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
        },
        SecurityTokenHandlers = new SecurityTokenHandlerCollection
        {                        
            new SamlSecurityTokenHandlerEx
            {
                CertificateValidator = X509CertificateValidator.None,
                Configuration = new SecurityTokenHandlerConfiguration
                {
                    AudienceRestriction = audienceRestriction,
                    IssuerNameRegistry = issuerRegistry
                }
            }
        }
    }
);

Merci beaucoup pour tout ce que vous pouvez offrir pour dissiper cette confusion pour moi.

34
Tom Tregenna

Comme l'a dit @Tratcher, le paramètre AuthenticationType est utilisé par Microsoft.Owin.Security Comme clé pour effectuer des recherches d'instances de middleware d'authentification.

Le code ci-dessous utilisera la méthode d'assistance simple suivante pour exiger que toutes les demandes soient authentifiées. En pratique, vous êtes plus susceptible d'utiliser un attribut [Authorize] Sur les contrôleurs sensibles, mais je voulais un exemple qui ne repose sur aucun framework:

private static void AuthenticateAllRequests(IAppBuilder app, params string[] authenticationTypes)
{
    app.Use((context, continuation) =>
    {
        if (context.Authentication.User != null &&
            context.Authentication.User.Identity != null &&
            context.Authentication.User.Identity.IsAuthenticated)
        {
            return continuation();
        }
        else
        {
            context.Authentication.Challenge(authenticationTypes);
            return Task.Delay(0);
        }
    });
}

L'appel context.Authentication.Challenge(authenticationTypes) lancera un défi d'authentification à partir de chacun des types d'authentification fournis. Nous allons simplement fournir celui, notre type d'authentification WS-Federation.

Code correct

Donc, d'abord, voici un exemple de la configuration de démarrage Owin "optimale" pour un site qui utilise simplement WS-Federation, comme vous êtes:

public void Configuration(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

    app.UseCookieAuthentication(new CookieAuthenticationOptions());

    app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
    {
        AuthenticationType = "WS-Fed Auth (Primary)",
        Wtrealm = ConfigurationManager.AppSettings["app:URI"],
        MetadataAddress = ConfigurationManager.AppSettings["wsFederation:MetadataEndpoint"]
    });

    AuthenticateAllRequests(app, "WS-Fed Auth (Primary)");

    app.UseWelcomePage();
}

Notez l'utilisation de la "WS-Fed Auth (Primary)"AuthenticationType pour identifier de manière unique l'instance de middleware WS-Federation que nous avons configurée. Cela signifie que vous pourriez, par exemple, utiliser une "WS-Fed Auth (Secondary)" avec un serveur WS-Federation distinct comme solution de rechange, si vous aviez cette exigence.

Cette configuration fera ce qui suit:

  1. Tout d'abord, dites au pipeline de sécurité Owin que, par défaut, nous voulons authentifier les demandes avec la valeur par défaut CookeAuthentication AthenticationType. (C'est juste une chaîne constante sur cette classe CookieAuthenticationDefaults, et c'est la valeur par défaut utilisée par la propriété CookieAuthenticationOptions.AuthenticationType.)
  2. Ensuite, enregistrez une instance de middleware d'authentification de cookie avec toutes les options par défaut, afin qu'elle corresponde à la clé AuthenticationType que nous avons définie comme valeur par défaut à l'étape 1.
  3. Ensuite, enregistrez une instance de middleware d'authentification WS-Federation avec les options que nous définissons dans le fichier Web.config, et avec une valeur AuthenticationType personnalisée pour pouvoir y faire référence plus tard.
  4. Une fois tous les enregistrements du middleware d'authentification effectués, nous demandons au pipeline d'authentifier toutes les demandes (via notre méthode d'assistance personnalisée qui appelle le Microsoft.Owin.Security méthodes pour lancer des contestations à toute demande non authentifiée)
  5. Enfin, si l'utilisateur a été authentifié, affichez la page d'accueil!

Mauvais code

Il y a donc deux façons de vous tromper ici.

Ne pas fournir de type d'authentification par défaut

Pour expérimenter, j'ai essayé de le faire, et vous verrez tout de suite quel est le problème:

public void Configuration(IAppBuilder app)
{
    var x = app.GetDefaultSignInAsAuthenticationType();

    app.SetDefaultSignInAsAuthenticationType(x);
}

Ce premier appel vous donnera l'exception que vous avez mentionnée dans votre premier commentaire:

"Une valeur par défaut pour SignInAsAuthenticationType est introuvable dans les propriétés IAppBuilder. Cela peut se produire si votre middleware d'authentification est ajouté dans le mauvais ordre, ou s'il en manque un."

Droite - parce que par défaut le pipeline Microsoft.Owin.Security N'assume rien sur le middleware que vous allez utiliser (c'est-à-dire que Microsoft.Owin.Security.Cookies N'est même pas connu pour être présent), donc il ne le fait pas ' Je ne sais pas quelle devrait être la valeur par défaut.

Utilisation du mauvais type d'authentification par défaut

Cela m'a coûté beaucoup de temps aujourd'hui car je ne savais pas vraiment ce que je faisais:

public void Configuration(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType("WS-Fed AAD Auth");

    // ... remainder of configuration
}

Donc, cela va continuer d'essayer d'authentifier l'appelant avec WS-Federation à chaque appel. Ce n'est pas que c'est super cher, c'est que le WS- Le middleware de la fédération émettra un défi à chaque demande. Vous ne pourrez donc jamais entrer et vous verrez beaucoup d'URL de connexion passer devant vous . : P

Possibilités

Donc, ce qui est génial d'avoir toute cette flexibilité dans le pipeline, c'est que vous pouvez faire des choses vraiment cool. Par exemple, j'ai un domaine avec deux applications Web différentes à l'intérieur, fonctionnant sous différents sous-chemins comme: example.com/foo Et example.com/bar. Vous pouvez utiliser la fonctionnalité de mappage d'Owin (comme dans app.Map(...)) pour configurer un pipeline d'authentification totalement différent pour chacune de ces applications. Dans mon cas, l'un utilise WS-Federation, tandis que l'autre utilise des certificats clients. Essayer de faire cela dans le cadre monolithique System.Web Serait horrible. : P

49
Lars Kemmann