API da Web protegida: verifique escopos e funções do aplicativo

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

  • Aplicativos em nome de usuários que têm os escopos e funções certos.
  • Aplicativos Daemon que têm as funções de aplicativo certas.

Os trechos 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, você deve adicionar o [Authorize] atributo 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
    {
     // ...
    }

Mas essa proteção não é suficiente. Ele 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. Estas alegações, em particular, necessitam de verificação:

  • Os escopos se a API for chamada em nome de um usuário.
  • O aplicativo funciona se a API puder ser chamada a partir de um aplicativo daemon.

Verificar escopos em APIs chamadas em nome de 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, consulte Configuração de código | Token ao 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 [RequiredScope] atributo. Este atributo tem várias substituições. Um que usa os escopos necessários diretamente e outro que leva uma chave para a configuração.

Verificar os escopos em uma ação do controlador com escopos codificados

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

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"
  }
}

Em seguida, faça referência a ele no [RequiredScope] atributo:

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 VerifyUserHasAnyAcceptedScope método de extensão 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 para todo o controlador

Verificar os escopos em um controlador com escopos codificados

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

  • Uso AddMicrosoftIdentityWebApi em Startup.cs, como visto na configuração de código
  • ou, de outra forma, adicione o às políticas de autorização, conforme explicado ScopeAuthorizationRequirement em Políticas de autorização.
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

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 de forma mais global

Definir escopos granulares para sua API da 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, consulte autorização baseada em declaração na documentação do ASP.NET Core.

O que é verificado?

O [RequiredScope] atributo e VerifyUserHasAnyAcceptedScope o método, faz algo como as seguintes etapas:

  • Verifique se há uma declaração com o nome 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 da Web for chamada por um aplicativo daemon, esse aplicativo deverá exigir uma permissão de aplicativo para sua API da 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 do access_as_application aplicativo.

Agora você precisa que sua API verifique se o token recebido contém a roles declaração 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 do 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 [Authorize(Roles = "access_as_application")] atributos no controlador ou uma ação (ou uma página de barbear).

[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 um entre eles que se adapte aos seus respetivos cenários.

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

Verificar funções de aplicativo em APIs chamadas em nome de 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 forem atribuíveis a ambas, a verificação de funções permitirá que os aplicativos entrem como usuários e os usuários entrem como aplicativos. Recomendamos que você declare funções diferentes para usuários e aplicativos para evitar essa confusão.

Se você definiu funções de aplicativo com usuário/grupo, a declaração de funções também pode ser verificada na API junto com os escopos. A lógica de verificação das funções do aplicativo neste cenário permanece a mesma como se a API fosse chamada pelos aplicativos daemon, uma vez que não há diferenciação na declaração de função para usuário/grupo e aplicativo.

Aceitando tokens somente de aplicativo se a API da Web deve ser chamada apenas por aplicativos daemon

Se você quiser que apenas aplicativos daemon chamem sua API da Web, adicione a condição de que o token seja um token somente de aplicativo quando 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 que apenas os aplicativos que entram em um usuário chamem sua API.

Usando autorização baseada em ACL

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

Se você estiver usando Microsoft.Identity.Web no ASP.NET Core, precisará declarar que está usando a autorização baseada em ACL, caso contrário, o Microsoft Identity Web lançará uma exceção quando nem as funções nem os escopos estiverem 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 AllowWebApiToBeAuthorizedByACL propriedade configuration como true in appsettings.json ou programaticamente.

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

Se você definir AllowWebApiToBeAuthorizedByACL para true, esta é sua responsabilidade para garantir o mecanismo ACL.

Próximos passos

  • Saiba mais criando um aplicativo Web ASP.NET Core que inicia sessão nos utilizadores na seguinte série de tutoriais com várias partes

  • Explore exemplos de API da Web da plataforma de identidade da Microsoft