Nel precedente script abbiamo iniziato a esplorare il supporto al protocollo OAuth fornito da ASP.NET Web API grazie a un apposito middleware OWIN. In particolare ci siamo occupati di un sistema di autenticazione basato su username e password, che è idoneo nel contesto in cui un utente debba fornire le proprie credenziali, magari da un app per un device o una single page web application.
Oggi invece ci occuperemo del caso in cui sia un'applicazione a doversi autenticare presso il nostro servizio ASP.NET Web API. In questo caso, il modo più corretto è sfruttare un grant type di tipo client_credentials, con una richiesta simile alla seguente:
POST /token HTTP/1.1 Host: localhost:15986 Content-Type: application/x-www-form-urlencoded Cache-Control: no-cache grant_type=client_credentials&client_id=testclient&client_secret=testsecret
Come possiamo notare, questa richiesta include esclusivamente i campi client_id e client_secret, che possiamo immaginare come le vere e proprie credenziali dell'app che vogliamo autenticare. La validazione delle credenziali in Web API avviene ancora una volta grazie all'OAuthAuthorizationServerProvider che abbiamo configurato in Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app) { // .. altro codice qui .. OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/Token"), Provider = new ApplicationOAuthProvider(PublicClientId), .... }; // .. altro codice qui .. }
In pratica, ciò che dobbiamo fare è effettuare l'override di due metodi nella classe OAuthAuthorizationServerOptions per definire le modalità con cui:
- valideremo le credenziali dell'applicazione, nel metodo ValidateClientAuthentication;
- assegneremo i claims al principal, quando lo step precedente ha successo, tramite GrantClientCredentials.
Il codice da scrivere è piuttosto semplice:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider { // .. altro codice qui .. public override Task ValidateClientAuthentication( OAuthValidateClientAuthenticationContext context) { string clientId, clientSecret; if (context.TryGetFormCredentials(out clientId, out clientSecret) && clientId == "testclient" && clientSecret == "testsecret") { context.Validated(); } return Task.FromResult<object>(null); } public override Task GrantClientCredentials( OAuthGrantClientCredentialsContext context) { ClaimsIdentity oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType); oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.ClientId)); var properties = new AuthenticationProperties(); var ticket = new AuthenticationTicket(oAuthIdentity, properties); context.Validated(ticket); return Task.FromResult(0); } }
Nell'esempio in alto, la validazione in ValidateClientAuthentication è un banale match di stringhe, ma in un'applicazione reale ovviamente dovremo effettuare una query su una base dati in cui abbiamo memorizzato le app registrate, così da verificare le credenziali in ingresso. Se il test ha successo, dobbiamo invocare il metodo Validated che marca il context come IsValidated consentendo al flusso di proseguire.
Il metodo successivo a essere invocato è GrantClientCredentials, in cui costruiamo il ClaimsIdentity. Nel codice precedente ci siamo limitati a specificare un unico claim, il nome, ma nulla vieta di indicarne di ulteriori, per implementare logiche autorizzative più complesse.
Se entrambi questi metodi hanno successo, il servizio Web API risponderà alla richiesta che abbiamo presentato a inizio script con un token di autorizzazione:
{ "access_token": "2L8KJld3Qj2....", "token_type": "bearer", "expires_in": 1209599, ".issued": "Sun, 21 Feb 2016 12:49:11 GMT", ".expires": "Sun, 06 Mar 2016 12:49:11 GMT" }
Questo token, potrà essere inviato - analogamente allo script precedente - nell'header di una richiesta a uno degli endpoint esposti dal nostro web service, affinché la richiesta possa essere validata e autorizzata:
GET /api/values HTTP/1.1 Host: localhost:15986 Content-Type: application/json Authorization: bearer 2L8KJld3Qj2.... Cache-Control: no-cache
Se a questo punto, utilizzando il debugger, mettiamo un breakpoint in ValuesController, potremo verificare che la proprietà User è effettivamente popolata con i claim che abbiamo rilasciato nello step di autenticazione.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Creare una libreria CSS universale: Clip-path
Usare un KeyedService di default in ASP.NET Core 8
Utilizzare Azure Cosmos DB con i vettori
Sfruttare i KeyedService in un'applicazione Blazor in .NET 8
Eseguire i worklow di GitHub su runner potenziati
Utilizzare politiche di resiliency con Azure Container App
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Creare gruppi di client per Event Grid MQTT
Inference di dati strutturati da testo con Semantic Kernel e ASP.NET Core Web API
Triggerare una pipeline su un altro repository di Azure DevOps
Paginare i risultati con QuickGrid in Blazor
Testare l'invio dei messaggi con Event Hubs Data Explorer