por example español ejemplo basada autenticacion post asp.net-mvc-5 oauth-2.0 asp.net-web-api2 asp.net-authorization

post - example - web api ejemplo



MVC POST solicita la pérdida del encabezado de autorización: cómo usar el API Bearer Token una vez recuperado (1)

Después de mucho tiempo trabajando en otros aspectos del proyecto, la implementación de otras características en realidad ha hecho que resolver esto sea mucho más fácil: ahora hay un Manejador de Contenedor de Respuesta como parte de la API, y parte de ese Manejador guarda todos los Encabezados de las Solicitudes entrantes y los agrega a las respuestas salientes. Creo que esto permite que el lado ASP.NET MVC de la aplicación envíe de nuevo el encabezado Autorización después de que se envíe inicialmente la solicitud 200-OK.

Modifiqué mi autenticación para aprovechar Roles, pero intentaré excluir ese código ya que no debería ser relevante aquí:

MVC Startup.cs:

public class Startup { public void Configuration(IAppBuilder app) { ConfigureAuth(app); } /// <summary> /// Configures authentication settings for OAuth. /// </summary> /// <param name="app"></param> private void ConfigureAuth(IAppBuilder app) { app.CreatePerOwinContext(ADUIdentityDbContext.Create); app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, CookieName = "ADU", ExpireTimeSpan = TimeSpan.FromHours(2), LoginPath = new PathString("/Account/Login"), SlidingExpiration = true }); } }

Dónde se usa (AccountController):

private async Task CreateLoginCookie(AuthorizationToken response, User result) { //Create the claims needed to log a user in //(uses UserManager several layers down in the stack) ClaimsIdentity cookieIdent = await _clientSDK.CreateClaimsIdentityForUser(response.AccessToken, result, true).ConfigureAwait(false); if (cookieIdent == null) throw new NullReferenceException("Failed to create claims for cookie."); cookieIdent.AddClaim(new Claim("AuthToken", response.AccessToken)); AuthenticationProperties authProperties = new AuthenticationProperties(); authProperties.AllowRefresh = true; authProperties.IsPersistent = true; authProperties.IssuedUtc = DateTime.Now.ToUniversalTime(); IOwinContext context = HttpContext.GetOwinContext(); AuthenticateResult authContext = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie); if (authContext != null) context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(cookieIdent, authContext.Properties); //Wrapper methods for IOwinContext.Authentication.SignOut()/SignIn() SignOut(); SignIn(authProperties, cookieIdent); }

En mi capa de SDK, creé un método al que llamo desde varios otros métodos que utilizo para llegar a mi API con el fin de configurar la Autorización para cada Solicitud saliente (Me gustaría descubrir cómo convertir esto en un Atributo, pero Me preocuparé por eso más tarde):

private void SetAuthentication() { ClaimsIdentity ident = (ClaimsIdentity)Thread.CurrentPrincipal.Identity; Claim claim; //Both of these methods (Thread.CurrentPrincipal, and ClaimsPrincipal.Current should work, //leaving both in for the sake of example. try { claim = ident.Claims.First(x => x.Type == "AuthToken"); } catch (Exception) { claim = ClaimsPrincipal.Current.Claims.First(x => x.Type == "AuthToken"); } _apiClient.SetBearerAuthentication(claim.Value); }

API Startup.cs

/// <summary> /// Configures the settings used by the framework on application start. Dependency Resolver, OAuth, Routing, and CORS /// are configured. /// </summary> /// <param name="app"></param> public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); config.DependencyResolver = new NinjectResolver(new Bootstrapper().Kernel); WebApiConfig.Register(config); ConfigureOAuth(app); app.UseCors(CorsOptions.AllowAll); app.UseWebApi(config); } /// <summary> /// Configures authentication options for OAuth. /// </summary> /// <param name="app"></param> public void ConfigureOAuth(IAppBuilder app) { app.CreatePerOwinContext(ADUIdentityDbContext.Create); app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/api/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new SimpleAuthorizationServerProvider() }; //token generation app.UseOAuthAuthorizationServer(oAuthServerOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); }

SimpleAuthorizationServerProvider.cs:

/// <summary> /// Creates an access bearer token and applies custom login validation logic to prevent invalid login attempts. /// </summary> /// <param name="context"></param> /// <returns></returns> public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); // Performs any login logic required, such as accessing Active Directory and password validation. User user = await CustomLoginLogic(context).ConfigureAwait(false); //If a use was not found, add an error if one has not been added yet if((user == null) && !context.HasError) SetInvalidGrantError(context); //Break if any errors have been set. if (context.HasError) return; //create a claim for the user ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType); //Add some basic information to the claim that will be used for the token. identity.AddClaim(new Claim("Id", user?.Id)); identity.AddClaim(new Claim("TimeOf", DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString())); //Roles auth SetRoleClaim(user, ref identity); context.Validated(identity); }

Y finalmente, la clave aparente que envuelve todo junto:

public class ResponseWrappingHandler : DelegatingHandler { /// <summary> /// Catches the request before processing is completed and wraps the resulting response in a consistent response wrapper depending on the response returned by the api. /// </summary> /// <param name="request">The request that is being processed.</param> /// <param name="cancellationToken">A cancellation token to cancel the processing of a request.</param> /// <returns></returns> protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpResponseMessage response = await base.SendAsync(request, cancellationToken); //Calls Wrapping methods depending on conditions, //All of the Wrapping methods will make a call to PreserveHeaders() } /// <summary> /// Creates a response based on the provided request with the provided response''s status code and request headers, and the provided response data. /// </summary> /// <param name="request">The original request.</param> /// <param name="response">The reqsponse that was generated.</param> /// <param name="responseData">The data to include in the wrapped response.</param> /// <returns></returns> private static HttpResponseMessage PreserveHeaders(HttpRequestMessage request, HttpResponseMessage response, object responseData) { HttpResponseMessage newResponse = request.CreateResponse(response.StatusCode, responseData); foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers) newResponse.Headers.Add(header.Key, header.Value); return newResponse; }

Con todo eso en su lugar, mi proyecto ahora puede usar la autorización / autenticación sin necesidad de secretos del cliente y tal (que era uno de los objetivos de mi empleador).

Pasé la última semana creando una API para una aplicación MVC existente, y ahora estoy intentando asegurar la API junto con la reelaboración de la seguridad lateral de MVC según sea necesario.

Actualmente, la aplicación MVC está configurada para usar una cookie de aplicación a través de OWIN / OAuth / Identity. Intenté incorporar el token de portador que la API web está configurada para generar cada vez que realiza llamadas a métodos de API restringidos, pero hasta ahora no ha tenido mucho éxito: las solicitudes GET funcionan bien, pero las solicitudes POST están perdiendo el encabezado Autorización cuando las recibe la API.

He creado un SDK Client que está siendo utilizado por la aplicación MVC para realizar las llamadas a la API, y he intentado un total de tres métodos para configurar el encabezado Authorization para cualquier llamada dada a la API, todos los cuales parecen funcionar solo bien para las solicitudes GET, pero no para todas las solicitudes POST que necesito hacer ...

Puedo configurar el encabezado de Solicitud en el controlador MVC:

HttpContext.Request.Headers.Add ("Authorization", "Bearer" + response.AccessToken);

(donde response.AccessToken es el token recuperado previamente de la API)
Puedo configurar el encabezado de Solicitud a través de un método de extensión en el SDK Client:

_apiclient.SetBearerAuthentication (token.AccessToken)

o puedo configurar el encabezado de Solicitud manualmente en el SDK Client:

_apiClient.Authentication = new AuthenticationHeaderValue ("Bearer, accessToken);

(Donde accessToken es el token recuperado previamente, pasado al método Client que se está llamando).

Tengo muy poco para continuar a partir de este punto en cuanto a qué está causando el problema. Lo único que pude obtener hasta ahora es que ASP.NET hace que todas las solicitudes POST envíen primero una solicitud con un encabezado Esperar para una respuesta HTTP 100-Continuar, después de lo cual finalizará la solicitud POST real. Sin embargo, parece que cuando realiza esta segunda solicitud, el encabezado Autorización ya no está presente y, por lo tanto, el atributo Autorizar de la API generará una respuesta 401 no autorizada en lugar de ejecutar realmente el método API.

Entonces, ¿cómo tomo el token de portador que puedo recuperar de la API y lo uso en solicitudes posteriores, incluidas las diversas solicitudes de POST que tendré que hacer?

Más allá de eso, ¿cuál es la mejor manera de almacenar este token en la aplicación MVC? Me gustaría evitar tener que pasar la cadena a cada método en la aplicación que podría necesitarlo, pero también he estado leyendo que almacenarlo en una cookie es una muy mala idea por razones de seguridad.

Algunos puntos adicionales que serán de interés inmediatamente después de superar este problema:

¿El uso de Tokens Portadores OAuth significa que ya no puedo usar ApplicationCookies para la aplicación MVC? ¿Y / o hará que el siguiente código sea inútil en toda la aplicación?

User.Identity.GetUserId ()

Actualmente me veo obligado a comentar mis atributos API [Authorize] para continuar con mi trabajo, lo que obviamente no es ideal, pero me permite continuar con las cosas temporalmente.

Archivos de inicio:

MVC:

public class Startup { public void Configuration(IAppBuilder app) { ConfigureAuth(app); } private void ConfigureAuth(IAppBuilder app) { app.CreatePerOwinContext(ADUIdentityDbContext.Create); app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions { AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), //This should be set to FALSE before we move to production. AllowInsecureHttp = true, ApplicationCanDisplayErrors = true, TokenEndpointPath = new PathString("/api/token"), }); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ExternalBearer, CookieName = "ADU", ExpireTimeSpan = TimeSpan.FromHours(2), LoginPath = new PathString("/Account/Login"), SlidingExpiration = true, }); } }

API

public class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); config.DependencyResolver = new NinjectResolver(new Ninject.Web.Common.Bootstrapper().Kernel); WebApiConfig.Register(config); ConfigureOAuth(app); app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); app.UseWebApi(config); } public void ConfigureOAuth(IAppBuilder app) { app.CreatePerOwinContext(ADUIdentityDbContext.Create); app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/api/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new SimpleAuthorizationServerProvider(), }; //token generation app.UseOAuthAuthorizationServer(oAuthServerOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); } }


public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider { private IUserBusinessLogic _userBusinessLogic; /// <summary> /// Creates the objects necessary to initialize the user business logic field and initializes it, as this cannot be done by dependency injection in this case. /// </summary> public void CreateBusinessLogic() { IUserRepository userRepo = new UserRepository(); IGeneratedExamRepository examRepo = new GeneratedExamRepository(); IGeneratedExamBusinessLogic examBLL = new GeneratedExamBusinessLogic(examRepo); _userBusinessLogic = new UserBusinessLogic(userRepo, examBLL); } public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); //create a claim for the user ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType); identity.AddClaim(new Claim("sub", user.Id)); context.Validated(identity); } }