2016-07-09 40 views
5

Desidero rispondere con un modello di risposta JSON quando si verificano 401 e 403. Per esempio:Modello di risposta 401 e 403 personalizzato con middleware UseJwtBearerAuthentication

HTTP 401 
{ 
    "message": "Authentication failed. The request must include a valid and non-expired bearer token in the Authorization header." 
} 

Sto usando middleware (come suggerito in this answer) per intercettare 404s e funziona benissimo, ma non è il caso con 401 o 403s. Ecco il middleware:

app.Use(async (context, next) => 
{ 
    await next(); 
    if (context.Response.StatusCode == 401) 
    { 
     context.Response.ContentType = "application/json"; 
     await context.Response.WriteAsync(JsonConvert.SerializeObject(UnauthorizedModel.Create(), SerializerSettings), Encoding.UTF8); 
    } 
}); 

Quando posto sotto app.UseJwtBearerAuthentication(..) in Startup.Configure(..), sembra essere completamente ignorato ed una normale 401 viene restituito.

Quando posto sopra app.UseJwtBearerAuthentication(..) in Startup.Configure(..), allora la seguente eccezione viene generata:

Connection id "0HKT7SUBPLHEM": un'eccezione non gestita è stato gettato dal l'applicazione. System.InvalidOperationException: le intestazioni sono di sola lettura , la risposta è già stata avviata. a Microsoft.AspNetCore.Server.Kestrel.Internal.Http.FrameHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item (String chiave, valore StringValues) a Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_ContentType (String valore) su MyProject.Api.Startup. < b__12_0> d.MoveNext() in Startup.cs

risposta

1

Prima di tutto, l'ordine di middlewares è importante.

Ogni middleware sceglie se passare la richiesta al componente successivo nella pipeline, e può eseguire determinate azioni prima e dopo il componente successivo viene richiamato nella pipeline

UseJwtBearerAuthentication arresta ulteriore esecuzione conduttura se si è verificato un errore.

Ma il tuo approccio non funziona con il middleware JwtBearerAuthentication, come quando si ha un errore non autorizzato, il middleware invia l'intestazione WWWAuthenticate, per cui si ottiene l'eccezione "la risposta è già stata avviata" - guarda nel metodo HandleUnauthorizedAsync. Puoi sovrascrivere questo metodo e implementare la tua logica personalizzata.

Un'altra possibile soluzione (non è sicuro che funzioni) consiste nell'utilizzare il callback HttpContext.Response.OnStarting nel middleware, come viene chiamato prima dell'emissione dell'intestazione. L'aspetto è SO answer

+0

"Sistema.InvalidOperationException: le intestazioni sono di sola lettura, la risposta è già stata avviata "viene generata se posta prima di UseJwtBearerAuthentication. – davenewza

+0

l'aggiornamento di speranza aiuterà – Set

6

Il set era sulla buona strada, ma in realtà non è necessario creare il proprio middleware, poiché è possibile sfruttare il modello di eventi per ignorare la logica di sfida predefinita.

Ecco un esempio che restituirà una risposta 401 contenente l'OAuth2 codice di errore/descrizione come testo normale (si può di JSON naturalmente ritorno o quello che volete):

app.UseJwtBearerAuthentication(new JwtBearerOptions 
{ 
    Authority = "http://localhost:54540/", 
    Audience = "http://localhost:54540/", 
    RequireHttpsMetadata = false, 
    Events = new JwtBearerEvents 
    { 
     OnChallenge = async context => 
     { 
      // Override the response status code. 
      context.Response.StatusCode = 401; 

      // Emit the WWW-Authenticate header. 
      context.Response.Headers.Append(
       HeaderNames.WWWAuthenticate, 
       context.Options.Challenge); 

      if (!string.IsNullOrEmpty(context.Error)) 
      { 
       await context.Response.WriteAsync(context.Error); 
      } 

      if (!string.IsNullOrEmpty(context.ErrorDescription)) 
      { 
       await context.Response.WriteAsync(context.ErrorDescription); 
      } 

      context.HandleResponse(); 
     } 
    } 
}); 

In alternativa, è anche possibile utilizzare la middleware delle pagine di codici di stato, ma per 403 risposte non si hanno suggerimenti sulla politica di autorizzazione che lo ha causato:

app.UseStatusCodePages(async context => 
{ 
    if (context.HttpContext.Request.Path.StartsWithSegments("/api") && 
     (context.HttpContext.Response.StatusCode == 401 || 
     context.HttpContext.Response.StatusCode == 403)) 
    { 
     await context.HttpContext.Response.WriteAsync("Unauthorized request"); 
    } 
}); 
+0

Ah, questo funziona bene per l'autenticazione! Sebbene nessuno di questi eventi sembra attivare un errore di autorizzazione (403). – davenewza

+0

Purtroppo, applicare la stessa logica a 403 risposte sarà molto più complicato (perché in questo caso non viene chiamato l'evento 'Challenge'). Un'opzione potrebbe essere quella di sottoclasse il gestore del portatore JWT come suggerito da @Set e per sovrascrivere 'HandleForbiddenAsync' per fare la cosa giusta, speriamo che sarà migliorato nella prossima versione: https://github.com/aspnet/Security/issues/872 – Pinpoint

+0

Peccato, c'è una buona ragione per cui? app.UseStatusCodePages() 'non verrebbe utilizzato per 403s? – davenewza

Problemi correlati