2016-06-08 42 views
7

Ho creato una nuova soluzione WebAPI in Visual Studio e sto giocando con il codice per cercare di capire cosa sta succedendo.WebAPI2.0 Richiesta token OWIN con JSON

Ho un'API di test che funziona e funziona con un controller di autorizzazione e un altro controller che implementa tutte le funzionalità effettive.

Il controller (API) tutti i lavori ricevendo JSON e rispondere con JSON, con l'eccezione dei token/request.This deve essere:

Content-Type: application/x-www-form-urlencoded 

altrimenti ottengo solo un errore di nuovo.

La sezione di codice che crea questo endpoint sembra essere questo:

OAuthOptions = new OAuthAuthorizationServerOptions 
{ 
    TokenEndpointPath = new PathString("/Token"), 
    Provider = new ApplicationOAuthProvider(PublicClientId), 
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), 
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), 
    // In production mode set AllowInsecureHttp = false 
    AllowInsecureHttp = false 
}; 

Chiamarlo come questo si traduce in una risposta 200 successo, con un token al portatore:

$("#token_button").click(function() 
{ 
    var username = $("#token_email").val(); 
    var password = $("#token_password").val(); 

    postData("Token", "grant_type=password&username=" + username + "&password=" + password, "application/x-www-form-urlencoded", function (data) 
    { 
     user = data; 
     $("#feedback_display").html(user.access_token); 
    }, function() 
    { 
     user = null; 
    }); 
}); 

Chiamarlo come questo si traduce in una risposta 400:

$("#token_button").click(function() 
{ 
    var username = $("#token_email").val(); 
    var password = $("#token_password").val(); 

    var data = { 
     "grant_type": "password", 
     "username": username, 
     "password": password 
    } 

    postData("Token", JSON.stringify(data), "application/json", function (data) 
    { 
     user = data; 
     $("#feedback_display").html(user.access_token); 
    }, function() 
    { 
     user = null; 
    }); 
}); 

Il corpo di risposta è:

{"error":"unsupported_grant_type"} 

L'unica differenza qui è la codifica utilizzata per trasmettere la richiesta. Ogni dove guardo tutti gli esempi utilizzano la codifica del modulo per richiedere questo token.

Posizionare un punto di interruzione sul codice sotto/api/Account/ExternalLogin, non viene colpito.

C'è un motivo per questo solo accettando la codifica del modulo? e se no, come posso cambiare il controller per accettare JSON?

In alternativa ho appena fatto qualcosa di stupido?

+1

Non c'è bisogno di 'JSON.stringify (dati)' –

+0

Grazie a @gauravbhavsar che funziona ora. Se lo pubblichi come risposta, segnerò è corretto. Immagino che sollevi la domanda perché devo JSON.stringify i dati inviati agli altri endpoint ma non quello? L'ho appena testato e ne ho bisogno. Farò una ricerca e vedrò se non riesco a risolverlo. – Morvael

risposta

11

Il motivo dell'utilizzo di application/x-www-form-urlencoded come Content-Type è semplice: lo OAuth2 specification (RFC 6749) richiede questo tipo di contenuto per le richieste di token.

Qualsiasi altro tipo di contenuto interromperà la compatibilità dei client conformi a OAuth2. Ti consiglio di non cambiare questo comportamento standard.

Nota
Si prega di notare che questo:

postData("Token", data, "application/json", function (data) 
{ 
    //... 
} 

opere solo perché sei non invio di JSON a tutti! Anche se è stata aggiunta l'intestazione Content-Type come Content-Type, il corpo della richiesta viene serializzato come coppie chiave-valore del modulo (la serializzazione oggetto predefinita jQuery nelle chiamate AJAX).

L'implementazione predefinita di OAuthAuthorizationServerMiddleware (più precisamente l'usato internamente OAuthAuthorizationServerHandler) da Microsoft.Owin.Security.OAuth solo ignora l'intestazione Content-Type e tenta di leggere il corpo della richiesta come forma comunque.

+0

Grazie per la risposta completa. Ho verificato che i dati POST non sono JSON quando non si utilizza JSON.stringify(). Seguirò il tuo consiglio e lascerò stare, gracchia solo un po 'che questa chiamata deve essere la forma e il resto JSON. – Morvael

+1

Grazie per il link. Mi ha aiutato molto. a volte ha più senso leggere la definizione del protocollo :) –

1

Non è necessario specificare i dati di passaggio JSON.stringify(data).

+0

Grazie ancora per l'aiuto, tuttavia ora capisco perché questo funziona e non sento di poterlo contrassegnare come la risposta giusta. – Morvael

0

OAuth2 richiede il tipo di contenuto application/x-www-form-urlencoded per le richieste di token.

Eppure, ho pensato a questa soluzione:

// GET api/Account/GetToken 
    [HttpPost] 
    [AllowAnonymous] 
    [Route("GetToken")] 
    public async Task<IHttpActionResult> GetToken(TokenRequest request) 
    { 
     var client = new HttpClient() 
     { 
      BaseAddress = new Uri(Request.RequestUri.GetLeftPart(UriPartial.Authority)) 
     }; 

     var content = new FormUrlEncodedContent(new[] 
     { 
      new KeyValuePair<string, string>("grant_type", "password"), 
      new KeyValuePair<string, string>("username", request.Username), 
      new KeyValuePair<string, string>("password", request.Password) 
     }); 

     var result = await client.PostAsync("/token", content); 
     string resultContent = await result.Content.ReadAsStringAsync(); 
     resultContent = resultContent.Replace(".issued", "issued").Replace(".expires", "expires"); 
     TokenResponse tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(resultContent); 

     return Ok(tokenResponse); 
    } 

Modelli:

public class TokenRequest 
    { 
     public string Username { get; set; } 
     public string Password { get; set; } 
    } 

    public class TokenResponse 
    { 
     public string access_token { get; set; } 
     public string token_type { get; set; } 
     public int expires_in { get; set; } 
     public string userName { get; set; } 
     public DateTime issued { get; set; } 
     public DateTime expires { get; set; } 
     public string error { get; set; } 
     public string error_description { get; set; } 
    } 

Può essere migliorata, ma funziona alla grande.

Problemi correlati