Autenticazione di un'applicazione su ASP.NET Web API con token bearer

di Marco De Sanctis, in ASP.NET Web API,

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

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi