web-dev-qa-db-fra.com

Comment stocker des jetons de support lorsque MVC et l'API Web sont dans des projets différents

Situation: J'ai un projet Web API 2 qui joue le rôle de serveur d'autorisation (/ noeud final) et de serveur de ressources. J'utilise le modèle fourni avec l'API Web ASP.Net, moins une référence MVC. Le Start.Auth est configuré comme suit:

public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context and user manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Configure the application for OAuth based flow
            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AuthorizeEndpointPath = new PathString("/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                // In production mode set AllowInsecureHttp = false
                AllowInsecureHttp = true
            };

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);

            var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
            {
                AppId = ConfigurationManager.AppSettings["Test_Facebook_AppId"],
                AppSecret = ConfigurationManager.AppSettings["Test_Facebook_AppSecret"],
                //SendAppSecretProof = true,
                Provider = new FacebookAuthenticationProvider
                {
                    OnAuthenticated = (context) =>
                    {
                        context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
                        return Task.FromResult(0);
                    }
                }
            };

            facebookAuthenticationOptions.Scope.Add("email user_about_me user_location");
            app.UseFacebookAuthentication(facebookAuthenticationOptions);

        }

Le client MVC 5 (projet différent) utilise l’application API Web pour l’autorisation et les données. Vous trouverez ci-dessous le code permettant de récupérer le jeton du porteur dans le cas du magasin de nom d'utilisateur/mot de passe:

[HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            model.ExternalProviders = await GetExternalLogins(returnUrl);
            return View(model);
        }

        var client = Client.GetClient();

        var response = await client.PostAsync("Token", 
            new StringContent(string.Format("grant_type=password&username={0}&password={1}", model.Email, model.Password), Encoding.UTF8));

        if (response.IsSuccessStatusCode)
        {
            return RedirectToLocal(returnUrl);
        }
        return View();
    }

Problème

Je pourrais récupérer le jeton de support, puis l'ajouter à l'en-tête d'autorisation pour les appels suivants. Je pense que cela irait dans le cas d'une Angular App ou d'un SPA. Mais je pense qu’il devrait y avoir quelque chose dans MVC qui le gère pour moi, comme le stocker automatiquement dans un cookie et l’envoyer lors de demandes ultérieures. J'ai parcouru pas mal de recherches et il y a des publications qui suggèrent cela ( Enregistrement de connexions externes à l'API Web 2 à partir de plusieurs clients API avec OWIN Identity ), mais je n'ai pas été en mesure de déterminer quoi faire après avoir reçu un jeton.

Dois-je ajouter quelque chose dans l'application MVC Startup.Auth?

Idéalement, j'ai besoin des fonctionnalités que le AccountController dans un modèle ASP.Net (API MVC + Web) offre sans configuration (connexions, enregistrement, connexions externes, mot de passe oublié, etc.), mais avec les API MVC et Web dans différents projets. .

Existe-t-il un modèle ou un dépôt git portant ce code de plaque de chaudière? 

Merci d'avance!

Update Incorporant les suggestions de @FrancisDucharme, vous trouverez ci-dessous le code de GrantResourceOwnerCredentials ().

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

            ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);

            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }

            ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
               OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
                CookieAuthenticationDefaults.AuthenticationType);

            AuthenticationProperties properties = CreateProperties(user.UserName);
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);

            //Add a response cookie...
            context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));


            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
        }

Mais je n'arrive toujours pas à obtenir ce cookie ou à savoir quoi faire ensuite.

Questions récapitulatives:

  1. Quelle serait la manière correcte d'authentifier, d'autoriser et d'appeler des méthodes d'API Web (serveur Auth et Resource) à partir d'un client MVC?
  2. Existe-t-il un code standard ou un modèle pour AccountController qui effectue la plomberie de base (connexion, enregistrement - interne/externe, mot de passe oublié, etc.)? 
10
Amanvir Mundra

Vous pouvez demander à votre classe de démarrage de renvoyer un cookie de réponse que le client renverra ensuite pour toutes les demandes suivantes. En voici un exemple. Je le ferais dans GrantResourceOwnerCredentials.

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {                          

        //your authentication logic here, if it fails, do this...
        //context.SetError("invalid_grant", "The user name or password is incorrect.");
        //return;

         var identity = new ClaimsIdentity(context.Options.AuthenticationType);
         identity.AddClaim(new Claim("sub", context.UserName));
         identity.AddClaim(new Claim("role", "user"));

         AuthenticationTicket ticket = new AuthenticationTicket(identity);

        //Add a response cookie...
        context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));

        context.Validated(ticket);

}

La classe de démarrage:

public partial class Startup
{

    public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

    public Startup()
    {
        OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
    }

    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

        ConfigureOAuth(app);
        //I use CORS in my projects....
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);

        WebApiConfig.Register(config);

    }

    public void ConfigureOAuth(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true, //I have this here for testing purpose, production should always only accept HTTPS encrypted traffic.
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = new AuthorizationServerProvider()
        };

        app.UseOAuthAuthorizationServer(OAuthServerOptions);
        app.UseOAuthBearerAuthentication(OAuthBearerOptions);

    }
}

Cela suppose que le client a bien entendu activé les cookies.

Ensuite, modifiez vos en-têtes MVC pour ajouter l’en-tête Authorization à toutes les demandes proprement dites.

Dans la ActionFilterAttribute, récupérez votre valeur de cookie (Token) et ajoutez l'en-tête.

1
Francis Ducharme