web-dev-qa-db-fra.com

Combiner l'utilisation de l'authentification à la fois pour les pages MVC et pour les pages API Web?

J'ai une application Web MVC 5 et je peux me connecter avec une page Login.cshtml et obtenir un cookie et la connexion fonctionne correctement. Mais, je voudrais faire une connexion avec l'API Web et ensuite (peut-être) définir un cookie afin que je sois connecté pour mes pages MVC ... (ou me connecter avec la connexion MVC et ensuite accéder à l'API Web) mais le L'API Web renvoie un jeton de porteur et non un jeton de cookie ... donc cela ne fonctionne pas. Existe-t-il un moyen de combiner l'utilisation de l'authentification à la fois pour mes pages MVC et pour mes pages API Web?

METTRE À JOUR:

Ce n'est pas vraiment un problème de code, plus un problème conceptuel.

Les pages Web normales de MVC examinent un cookie nommé, par défaut, ".AspNet.ApplicationCookie" pour déterminer l'identité des demandeurs. Ce cookie est généré en appelant ApplicationSignInManager.PasswordSignInAsync.

Les appels WebAPI, d'autre part, examinent les en-têtes de demandes pour un élément nommé Autorisation ... et utilisent cette valeur pour déterminer l'identité des demandeurs. Ceci est renvoyé d'un appel WebAPI vers "/ Token".

Ce sont des valeurs très différentes. Mon site Web doit utiliser les deux pages MVC et les appels WebAPI (pour mettre à jour dynamiquement ces pages) ... et les deux doivent être authentifiés pour effectuer leurs tâches.

La seule méthode à laquelle je peux penser est de s'authentifier deux fois ... une fois avec un appel WebAPI et encore une fois avec le message de connexion. (voir ma réponse ci-dessous).

Cela semble très hacky ... mais je ne comprends pas suffisamment le code d'autorisation pour savoir s'il existe un moyen plus approprié d'accomplir cela.

24
Brian Rice

La meilleure façon d'y parvenir est d'avoir un serveur d'autorisation (une API Web générant un token) et un middleware de consommation de token dans votre projet MVC. IdentityServer devrait aider. Cependant, je l'ai fait comme ceci:

J'ai construit un serveur d'autorisation en utilisant JWT avec l'API Web et l'identité ASP.Net comme expliqué ici .

Une fois que vous avez fait cela, vos API Web startup.cs ressemblera à ceci:

 // Configures cookie auth for web apps and JWT for SPA,Mobile apps
 private void ConfigureOAuthTokenGeneration(IAppBuilder app)
 {
    // Configure the db context, user manager and role manager to use a single instance per request
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

    // Cookie for old school MVC application
    var cookieOptions = new CookieAuthenticationOptions
    {
        AuthenticationMode = AuthenticationMode.Active,
        CookieHttpOnly = true, // JavaScript should use the Bearer
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,                
        LoginPath = new PathString("/api/Account/Login"),
        CookieName = "AuthCookie"
    };
    // Plugin the OAuth bearer JSON Web Token tokens generation and Consumption will be here
    app.UseCookieAuthentication(cookieOptions);

    OAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        //For Dev enviroment only (on production should be AllowInsecureHttp = false)
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/oauth/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
        Provider = new CustomOAuthProvider(),                
        AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["JWTPath"])
    };

    // OAuth 2.0 Bearer Access Token Generation
    app.UseOAuthAuthorizationServer(OAuthServerOptions);
}

Vous pouvez trouver les classes CustomOAuthProvider et CustomJwtFormatici .

J'ai écrit une logique de consommation (c'est-à-dire un middleware) dans toutes mes autres API (serveurs de ressources) que je voulais sécuriser en utilisant le même token. Étant donné que vous souhaitez consommer le jeton généré par l'API Web dans votre projet MVC, après avoir implémenté le serveur d'autorisation, vous devez effectuer les opérations suivantes:

Dans votre application MVC, ajoutez ceci dans startup.cs:

public void Configuration(IAppBuilder app)
{
        ConfigureOAuthTokenConsumption(app);
}

private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
    var issuer = ConfigurationManager.AppSettings["AuthIssuer"];
    string audienceid = ConfigurationManager.AppSettings["AudienceId"];
    byte[] audiencesecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);

    app.UseCookieAuthentication(new CookieAuthenticationOptions { CookieName = "AuthCookie" , AuthenticationType=DefaultAuthenticationTypes.ApplicationCookie });

    //// Api controllers with an [Authorize] attribute will be validated with JWT
    app.UseJwtBearerAuthentication(
        new JwtBearerAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Passive,
            AuthenticationType = "JWT",
            AllowedAudiences = new[] { audienceid },
            IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
            {
                new SymmetricKeyIssuerSecurityTokenProvider(issuer, audiencesecret)                           
            }

        });
}

Dans votre contrôleur MVC, lorsque vous recevez le jeton, désérialisez-le et générez un cookie à partir du jeton d'accès:

AccessClaims claimsToken = new AccessClaims();
claimsToken = JsonConvert.DeserializeObject<AccessClaims>(response.Content);
claimsToken.Cookie = response.Cookies[0].Value;               
Request.Headers.Add("Authorization", "bearer " + claimsToken.access_token);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync("JWT");
ctx.Authentication.SignOut("JWT");
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);

Générez une clé machine et ajoutez-la dans web.config de votre API Web et de votre site ASP.Net MVC.

Avec cela, un cookie sera créé et le [Authorize] attribut dans le site MVC et l'API Web honorera ce cookie.

PS Je l'ai fait avec une API Web émettant JWT (serveur d'autorisation ou serveur d'authentification et de ressources) et j'ai pu le consommer dans un MVC ASP.Net site Web, site SPA intégré à Angular, API sécurisées intégrées python (serveur de ressources), spring (serveur de ressources) et une Android App.

10
Ravi A.

Ugg ... ce que je devais faire était d'utiliser le formulaire Login.cshtml et de remplacer la soumission ... passer un appel Ajax pour obtenir le jeton du porteur WebApi ... puis faire la soumission du formulaire pour obtenir le cookie MVC réel. Donc, je fais en fait deux demandes de connexion ... une pour le jeton WebApi et l'autre pour le cookie MVC.

Semble assez hacky pour moi ... ce serait bien s'il y avait un moyen de se connecter à MVC en utilisant le jeton du porteur ... ou un appel à WebApi qui me renverrait un cookie que je peux utiliser pour les demandes de page MVC normales .

Si quelqu'un a une meilleure façon, j'aimerais l'entendre.

Voici le code de script que j'ai ajouté à Login.cshtml:

    $(document).ready(function () {
        $('form:first').submit(function (e) {
            e.preventDefault();
            var $form = $(this);
            var formData = $form.serializeObject(); // https://github.com/macek/jquery-serialize-object
            formData.grant_type = "password";
            $.ajax({
                type: "POST",
                url: '@Url.Content("~/Token")',
                dataType: "json",
                data: formData, // seems like the data must be in json format
                success: function (data) {
                    sessionStorage.setItem('token', data.access_token);
                    $form.get(0).submit(); // do the actual page post now
                },
                error: function (textStatus, errorThrown) {
                }
            });
        });
    });
2
Brian Rice

Je suppose que ce que vous essayez de faire, c'est que les pages servies par MVC disposent de javascript qui appelle les méthodes de l'API Web. Si vous utilisez ASP.NET Identity pour gérer l'authentification (ce qui semble être le cas), MVC doit utiliser des jetons OAuth qui peuvent être transmis à l'API Web pour l'authentification).

Voici un extrait d'un code javascript qui fonctionne pour moi dans une situation similaire:

var token = sessionStorage.getItem('access_token');
var headers = {};
if (token) {
    headers.Authorization = 'Bearer ' + token;
}
$.ajax({
    type: <GET/POSt/...>,
    url: <your api>,
    headers: headers
}).done(function (result, textStatus) {
1
DavidS

D'après vos commentaires ci-dessus, d'après ce que je comprends, vous avez un scénario dans lequel vous effectuez une connexion via un navigateur mais devez également invoquer des méthodes web-api à l'aide d'appels ajax.

Les appels du navigateur sont basés sur des cookies de session. Alors que les appels ajax depuis le navigateur auraient le cookie de session dans l'en-tête, ce qui est requis, c'est que l'en-tête d'authentification soit présent pour que l'API Web effectue la validation.

Donc, en cas de connexion réussie, vous devrez également générer un jeton basé sur une API Web, le définir comme un cookie (accessible par javascript), puis pendant les appels ajax, le récupérer dans le cookie et l'inclure comme en-tête dans votre en-tête "Autorisation".

0
Ravindra HV

J'ai un cas similaire avec vous, mais j'utilise une méthode différente pour m'authentifier.

J'ai un site Web et une API, qui sont tous destinés aux utilisateurs de l'intranet. Je n'utilise pas l'identité de l'utilisateur pour passer sur le Web et sur l'API. Au lieu de cela, j'ai créé un compte Web individuel et chaque fois que Web utilisera ce compte spécial pour se connecter à l'API.

Parce que, nous devons également nous assurer que les utilisateurs ne doivent pas se connecter directement à l'API. Ils doivent uniquement se connecter à l'interface utilisateur Web.

J'espère que cela vous aidera.

0
Xin