API Web protegida: Verificar escopos e funções de aplicativo

Este artigo descreve como adicionar autorização à sua API Web. Essa proteção garante que a API seja chamada apenas por:

  • Aplicativos em nome dos usuários que têm os escopos e funções corretos.
  • Aplicativos daemon que têm as funções de aplicativo corretas.

Os snippets de código neste artigo são extraídos dos seguintes exemplos de código no GitHub:

Para proteger uma API Web ASP.NET ou ASP.NET Core, é preciso adicionar o atributo [Authorize] a um dos seguintes itens:

  • O próprio controlador se você quiser que todas as ações do controlador sejam protegidas
  • A ação do controlador individual para sua API
    [Authorize]
    public class TodoListController : Controller
    {
     // ...
    }

No entanto, essa proteção não é suficiente. Ela garante apenas que ASP.NET e ASP.NET Core validem o token. Sua API precisa verificar se o token usado para chamar a API é solicitado com as declarações esperadas. Essas declarações em particular precisam de verificação:

  • Os escopos se a API for chamada em nome de um usuário.
  • As funções de aplicativo se a API puder ser chamada de um aplicativo daemon.

Verificar escopos em APIs chamadas em nome dos usuários

Se um aplicativo cliente chamar sua API em nome de um usuário, a API precisará solicitar um token de portador que tenha escopos específicos para a API. Para obter mais informações, confira Configuração de código | Token de portador.

No ASP.NET Core, você pode usar Microsoft.Identity.Web para verificar escopos em cada ação do controlador. Você também pode verificá-los no nível do controlador ou para todo o aplicativo.

Verificar os escopos em cada ação do controlador

Você pode verificar os escopos na ação do controlador usando o atributo [RequiredScope]. Esse atributo tem várias substituições. Uma que leva os escopos necessários diretamente e outra que leva uma chave para a configuração.

Verificar os escopos em uma ação do controlador com escopos em código

O trecho de código a seguir mostra o uso do atributo [RequiredScope] com escopos em código.

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    const string scopeRequiredByApi = "access_as_user";

    // GET: api/values
    [HttpGet]
    [RequiredScope(scopeRequiredByApi)]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Verificar os escopos em uma ação do controlador com escopos definidos na configuração

Você também pode declarar esses escopos necessários na configuração e fazer referência à chave de configuração:

Por exemplo, se, em appsettings.json, você tiver a seguinte configuração:

{
 "AzureAd" : {
   // more settings
   "Scopes" : "access_as_user access_as_admin"
  }
}

Faça referência a eles no atributo [RequiredScope]:

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    [RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Verificar escopos condicionalmente

Há casos em que você deseja verificar os escopos condicionalmente. Você pode fazer isso usando o método de extensão VerifyUserHasAnyAcceptedScope no HttpContext.

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
         HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
        // Do the work and return the result.
        // ...
    }
 // ...
}

Verificar os escopos no nível do controlador

Você também pode verificar os escopos do controlador inteiro

Verificar os escopos em um controlador com escopos em código

O trecho de código a seguir mostra o uso do atributo [RequiredScope] com escopos em código no controlador. Para usar o RequiredScopeAttribute, você precisará:

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(scopeRequiredByApi)]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Verificar os escopos em um controlador com escopos definidos na configuração

Assim como na ação, você também pode declarar esses escopos necessários na configuração e fazer referência à chave de configuração:

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}

Verificar os escopos mais globalmente

Definir escopos granulares para a API Web e verificar os escopos em cada ação do controlador é a abordagem recomendada. No entanto, também é possível verificar os escopos no nível do aplicativo ou de um controlador. Para obter detalhes, confira autorização baseada em declaração na documentação do ASP.NET Core.

O que é verificado?

O atributo [RequiredScope] e o método VerifyUserHasAnyAcceptedScope usam um processo semelhante a estas etapas:

  • Verifique se há uma declaração chamada http://schemas.microsoft.com/identity/claims/scope ou scp.
  • Verifique se a declaração tem um valor que contém o escopo esperado pela API.

Verificar funções de aplicativo em APIs chamadas por aplicativos daemon

Se sua API Web for chamada por um aplicativo daemon, esse aplicativo deverá solicitar uma permissão de aplicativo para sua API Web. Conforme mostrado em Expondo permissões de aplicativo (funções de aplicativo), sua API expõe essas permissões. Um exemplo é a função de aplicativo access_as_application.

Agora você precisa fazer com que sua API verifique se o token recebido contém a declaração roles e se essa declaração tem o valor esperado. O código de verificação é semelhante ao código que verifica as permissões delegadas, exceto que a ação do controlador testa funções em vez de escopos:

O trecho de código a seguir mostra como verificar a função de aplicativo:

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : ApiController
{
    public IEnumerable<TodoItem> Get()
    {
        HttpContext.ValidateAppRole("access_as_application");
        // ...
    }

Em vez disso, você pode usar os atributos [Authorize(Roles = "access_as_application")] no controlador ou uma ação (ou uma página razor).

[Authorize(Roles = "access_as_application")]
MyController : ApiController
{
    // ...
}

A autorização baseada em função no ASP.NET Core lista várias abordagens para implementar a autorização baseada em função. Os desenvolvedores podem escolher uma entre elas, que se adaptam aos respectivos cenários.

Para obter exemplos de funcionamento, confira o tutorial incremental do aplicativo Web sobre autorização por funções e grupos.

Verificar funções de aplicativo em APIs chamadas em nome dos usuários

Os usuários também podem usar declarações de funções em padrões de atribuição de usuário, conforme mostrado em Como adicionar funções de aplicativo em seu aplicativo e recebê-las no token. Se as funções puderem ser atribuídas a ambas, a verificação de funções permitirá que os aplicativos entrem como usuários e usuários entrem como aplicativos. Recomendamos que você declare diferentes funções para usuários e aplicativos para evitar essa confusão.

Se você tiver definido funções de aplicativo com usuário/grupo, a declaração de funções também poderá ser verificada na API junto com os escopos. A lógica de verificação das funções de aplicativo nesse cenário permanecerá a mesma se a API for chamada pelos aplicativos daemon, pois não há nenhuma diferenciação na declaração de função para usuário/grupo e aplicativo.

Aceitar tokens somente de aplicativo caso a API Web deva ser chamada somente por aplicativos daemon

Se você quiser que apenas aplicativos daemon chamem sua API Web, adicione a condição de que o token é um token somente de aplicativo ao validar a função do aplicativo.

string oid = ClaimsPrincipal.Current.FindFirst("oid")?.Value;
string sub = ClaimsPrincipal.Current.FindFirst("sub")?.Value;
bool isAppOnly = oid != null && sub != null && oid == sub;

A verificação da condição inversa permite somente aplicativos que se conectam a um usuário para chamar sua API.

Como usar uma autorização baseada em ACL

Como alternativa à autorização baseada em funções de aplicativo, você pode proteger sua API Web com um padrão de autorização baseado em ACL (Lista de Controle de Acesso) para controlar tokens sem a declaração roles.

Se estiver usando o Microsoft.Identity.Web no ASP.NET Core, você precisará declarar que está usando a autorização baseada em ACL; caso contrário, o Microsoft Identity Web lançará uma exceção quando nenhuma função ou escopo estiver nas Declarações fornecidas:

System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.

Para evitar essa exceção, defina a propriedade de configuração AllowWebApiToBeAuthorizedByACL como true em appsettings.json ou programaticamente.

{
 "AzureAD"
 {
  // other properties
  "AllowWebApiToBeAuthorizedByACL" : true,
  // other properties
 }
}

Se você definir AllowWebApiToBeAuthorizedByACL como true, será sua responsabilidade garantir o mecanismo de ACL.

Próximas etapas

  • Saiba mais criando um aplicativo Web ASP.NET Core que conecta usuários na série de tutoriais de várias partes a seguir

  • Explorar os exemplos de API Web da plataforma de identidade da Microsoft