2016-01-08 13 views
10

Situazione: Ho un progetto Web API 2 che funge da server di autorizzazione (/ token endpoint) e un server di risorse. Sto usando il modello che esce dalla scatola con ASP.Net Web API meno qualsiasi riferimento MVC. Lo Start.Auth è configurato come di seguito:Come memorizzare i token bearer quando MVC e Web API si trovano in diversi progetti

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

     } 

Il MVC 5 client (progetto diverso) utilizza l'applicazione Web API per l'autorizzazione e dati. Di seguito è riportato il codice per recuperare il token portatore in caso di negozio Nome utente/password:

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

Problema

ho potuto recuperare il portatore token e quindi aggiungerlo alla intestazione di autorizzazione per le chiamate successive. Penso che sarebbe ok in caso di un'Angular App o di una SPA. Ma penso che ci dovrebbe essere qualcosa in MVC che lo gestisce per me, come lo memorizza automaticamente in un cookie e invia il cookie alle richieste successive. Ho cercato in giro parecchio e ci sono post che suggeriscono questo (Registering Web API 2 external logins from multiple API clients with OWIN Identity) ma non sono stato in grado di capire cosa fare dopo aver ottenuto un token.

Devo aggiungere qualcosa nell'app MVC Startup.Auth?

Idealmente, ho bisogno della funzionalità che l'AccountController nel modello ASP.Net (MVC + Web API) fornisce di casella (accessi, registro, accessi esterni, password dimenticata ecc. Ecc.) Ma con MVC e Web API in diversi progetti.

C'è un modello o un repository git che ha questo codice piastra?

Grazie in anticipo!

Aggiornamento Incorporando suggerimenti @FrancisDucharme, sotto è il codice per 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); 
     } 

Ma non riesco ancora a ottenere quel Cookie o a capire cosa fare dopo.

Domande ribadendo:

  1. Quale sarebbe il modo corretto per autenticare, autorizzare e chiamare i metodi Web API (Auth e server di risorse) da un client MVC?
  2. C'è codice o modello di codice per AccountController che esegue l'impianto idraulico di base (Login, registro - interno/esterno, password dimenticata, ecc.)?
+0

Se la propria API Web restituisce l'hash del token nei cookie di risposta, il client restituirà questo cookie per tutte le richieste successive, presupponendo che il browser client abbia i cookie abilitati. –

+0

@FrancisDucharme potresti elaborare questo processo per favore. Sto usando l'endpoint token standard e la configurazione che esce dal modello di API web. –

+0

Il tuo problema principale è che vuoi che il client MVC aggiunga sempre automaticamente l'intestazione 'Authorization: Bearer ', giusto? –

risposta

1

Si potrebbe fare in modo che la classe di avvio restituisca un cookie di risposta che il client restituirà in seguito a tutte le richieste successive, ecco un esempio. Lo farei nello 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 di avvio:

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

    } 
} 

Questo presuppone che il cliente ha attivato i cookie, naturalmente.

Quindi, modify your MVC headers per aggiungere l'intestazione Autorizzazione a tutte le richieste come tali.

Nel ActionFilterAttribute, recuperare il valore del cookie (Token) e aggiungere l'intestazione.

+0

Grazie a @FrancisDucharme per la spiegazione dettagliata. Sono una specie di nuovo su oAuth. Consentitemi di assimilare tutte queste informazioni e tornerò una volta che avrò funzionato sul mio setup :) –

+0

Ho aggiornato la domanda con i vostri suggerimenti. Non sono in grado di ottenere quel cookie nel browser, forse perché sto usando HttpClient (per favore scusate la mia comprensione limitata del concetto). Pensi che questa sia la strada giusta da fare? Ho aggiornato le domande per comprendere il modo corretto di implementare l'intera stretta di mano. –

+0

@AmanvirSinghMundra Mi dispiace, non ho molta esperienza con il lato client ASP MVC. Cosa significa esattamente "cliente"? Accedi alla rete di Chrome se ripristini il cookie nelle intestazioni di risposta durante il POST a token. –

Problemi correlati