Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Neste artigo, implementa a autorização nas APIs web ASP.NET Core usando Microsoft.Identity.Web. Irá validar os escopos (permissões delegadas) e as permissões da aplicação (permissões da aplicação) para controlar o acesso a recursos protegidos. Os exemplos utilizam o Microsoft Entra ID como fornecedor de identidade.
Compreender os conceitos de autorização
Esta secção aborda as principais diferenças entre autenticação e autorização e descreve o que o Microsoft.Identity.Web valida nos tokens de acesso.
Autenticação vs autorização
| Conceito | Purpose | Result |
|---|---|---|
| Authentication | Verificar identidade | 401 Não autorizado em caso de falha |
| Authorization | Verificar permissões | 403 Proibido se insuficiente |
O que é validado
Quando uma API web recebe um token de acesso, Microsoft.Identity.Web valida:
- Assinatura de token - É de uma autoridade de confiança?
- Audiência de tokens - Destina-se a esta API?
- Expiração do token - Ainda é válido?
- Scopes/Roles - A aplicação cliente e o sujeito (utilizador) têm as permissões corretas?
Este guia foca-se no #4 - validação de escopos e permissões de aplicações.
Escopos (permissões delegadas)
Os scopes aplicam-se quando um utilizador delega permissão a uma aplicação para agir em seu nome (por exemplo, uma API web chamada em nome de um utilizador iniciado sessão).
| Detail | Valor |
|---|---|
| Reivindicação do token |
scp ou scope (aplicação cliente); roles (utilizador) |
| Exemplos de valores |
"access_as_user", "User.Read", "Files.ReadWrite" |
Permissões de aplicação (permissões de aplicação)
As permissões da aplicação aplicam-se quando uma aplicação chama a API web como ela própria, sem contexto de utilizador, como um daemon ou serviço em segundo plano usando credenciais do cliente.
| Detail | Valor |
|---|---|
| Reivindicação do token | roles |
| Exemplos de valores |
"Mail.Read.All", "User.Read.All" |
Validar escopos com o RequiredScope
O RequiredScope atributo verifica se o token de acesso contém pelo menos um dos escopos especificados. Use este atributo quando a sua API serve apenas pedidos delegados pelo utilizador.
Configurar validação do escopo
Siga estes passos para permitir a validação do âmbito na sua API.
1. Ative a autorização na sua API:
Adicione serviços de autenticação e autorização ao seu pipeline de aplicação:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(); // Required for authorization
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization(); // Must be after UseAuthentication
app.MapControllers();
app.Run();
2. Proteger controladores ou ações:
Aplique os atributos [Authorize] e [RequiredScope] ao seu controlador ou a ações individuais:
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Only accessible if token has "access_as_user" scope
return Ok(new[] { "Todo 1", "Todo 2" });
}
}
Aplicar padrões de âmbito
Escolha o padrão que melhor se adapte à forma como gere os âmbitos na sua aplicação.
Padrão 1: Escopos codificados diretamente
Use este padrão quando os escopos estiverem fixos e conhecidos no momento do desenvolvimento.
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
// All actions require "access_as_user" scope
}
Para aceitar qualquer um de múltiplos âmbitos, liste-os como parâmetros:
[Authorize]
[RequiredScope("read", "write", "admin")]
public class TodoListController : ControllerBase
{
// Token must have "read" OR "write" OR "admin"
}
Padrão 2: Escopos a partir da configuração
Use este padrão quando os osciloscópios devem ser configuráveis por ambiente. Defina os escopos no seu ficheiro de configuração:
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user read write"
}
}
Refere a chave de configuração no teu controlador:
[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : ControllerBase
{
// Scopes read from configuration
}
Esta abordagem permite-lhe alterar os escopos sem ter de recompilar.
Padrão 3: Escopos de nível de ação
Use este padrão quando diferentes ações exigirem permissões distintas. Aplicar [RequiredScope] a métodos de ação individuais:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[RequiredScope("read")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[RequiredScope("write")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
// Only tokens with "write" scope can create
return CreatedAtAction(nameof(GetTodos), todo);
}
[HttpDelete("{id}")]
[RequiredScope("admin")]
public IActionResult DeleteTodo(int id)
{
// Only tokens with "admin" scope can delete
return NoContent();
}
}
Compreender o fluxo de validação
Quando chega um pedido, o middleware processa-o pela seguinte ordem:
- O middleware de autenticação ASP.NET Core valida o token
-
RequiredScopeverificações de atributos para as declaraçõesscpouscope - Se o token contiver pelo menos um escopo correspondente, o pedido avança.
- Se não for encontrado um âmbito correspondente, a API devolve uma resposta 403 Proibida.
O exemplo seguinte mostra uma resposta típica de erro:
{
"error": "insufficient_scope",
"error_description": "The token does not have the required scope 'access_as_user'."
}
Validar permissões da aplicação com RequiredScopeOrAppPermission
O RequiredScopeOrAppPermission atributo valida ou os escopos (delegados) ou as permissões da aplicação (aplicação). Use este atributo quando a sua API servir tanto as aplicações delegadas pelo utilizador como as aplicações daemon/serviço a partir do mesmo endpoint.
Se a sua API só servir pedidos delegados pelo utilizador, use RequiredScope em vez disso.
Configurar o âmbito ou validação de permissões da aplicação
Aplique o atributo para aceitar qualquer um dos tipos de token:
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScopeOrAppPermission(
AcceptedScope = new[] { "access_as_user" },
AcceptedAppPermission = new[] { "TodoList.ReadWrite.All" }
)]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Accessible with EITHER:
// - User-delegated token with "access_as_user" scope, OR
// - App-only token with "TodoList.ReadWrite.All" app permission
return Ok(todos);
}
}
Configurar permissões da aplicação a partir das definições
Armazene os escopos e permissões da aplicação na configuração para os alterar sem recompilar.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user",
"AppPermissions": "TodoList.ReadWrite.All TodoList.Admin"
}
}
Refira-se às chaves de configuração no seu controlador.
[Authorize]
[RequiredScopeOrAppPermission(
RequiredScopesConfigurationKey = "AzureAd:Scopes",
RequiredAppPermissionsConfigurationKey = "AzureAd:AppPermissions"
)]
public class TodoListController : ControllerBase
{
// Scopes and app permissions from configuration
}
Compare as diferenças nas reivindicações de tokens
A tabela seguinte mostra como as reivindicações diferem entre tokens delegados pelo utilizador e tokens apenas de aplicação:
| Tipo de token | Afirmação | Valor de Exemplo |
|---|---|---|
| Delegado pelo utilizador |
scp ou scope |
"access_as_user User.Read" |
| Apenas para app | roles |
["TodoList.ReadWrite.All"] |
O exemplo seguinte mostra um token delegado pelo utilizador:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"scp": "access_as_user",
"sub": "user-object-id",
...
}
O exemplo seguinte mostra um token exclusivo da aplicação:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"roles": ["TodoList.ReadWrite.All"],
"sub": "app-object-id",
...
}
Criar políticas de autorização
Para cenários de autorização complexos, utilize políticas de autorização ASP.NET Core. As políticas permitem centralizar regras, combinar múltiplos requisitos e escrever lógica de autorização testável.
| Benefit | Description |
|---|---|
| Lógica centralizada | Define regras de autorização uma vez, reutiliza em todo o lado |
| Componível | Combinar múltiplos requisitos (escopos + declarações + lógica personalizada) |
| Testável | Testar a lógica de autorização com testes unitários tornou-se mais fácil |
| Flexível | Requisitos personalizados para além da validação do âmbito |
Padrão 1: Definir uma política com o ExigeScope
Defina políticas nomeadas que exijam escopos específicos e depois referencia-as nos seus controladores:
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("TodoReadPolicy", policyBuilder =>
{
policyBuilder.RequireScope("read", "access_as_user");
});
options.AddPolicy("TodoWritePolicy", policyBuilder =>
{
policyBuilder.RequireScope("write", "admin");
});
});
var app = builder.Build();
Aplicar as políticas às ações do controlador:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[Authorize(Policy = "TodoReadPolicy")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[Authorize(Policy = "TodoWritePolicy")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
return CreatedAtAction(nameof(GetTodos), todo);
}
}
Padrão 2: Definir uma política com Requisito de Autorização de Âmbito
Use ScopeAuthorizationRequirement para requisitos de âmbito mais explícitos:
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CustomPolicy", policyBuilder =>
{
policyBuilder.AddRequirements(
new ScopeAuthorizationRequirement(new[] { "access_as_user" })
);
});
});
Padrão 3: Definir uma política padrão
Defina uma política padrão que se aplique automaticamente a todos os [Authorize] atributos:
builder.Services.AddAuthorization(options =>
{
var defaultPolicy = new AuthorizationPolicyBuilder()
.RequireScope("access_as_user")
.Build();
options.DefaultPolicy = defaultPolicy;
});
Cada [Authorize] atributo agora requer o access_as_user âmbito:
[Authorize] // Automatically requires "access_as_user" scope
public class TodoListController : ControllerBase
{
// All actions protected by default policy
}
Padrão 4: Combinar múltiplos requisitos
Combine o âmbito, função e requisitos de autenticação numa única política:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policyBuilder =>
{
policyBuilder.RequireScope("admin");
policyBuilder.RequireRole("Admin"); // Also check role claim
policyBuilder.RequireAuthenticatedUser();
});
});
Padrão 5: Construir uma política a partir da configuração
Carregar escopos a partir da configuração para manter as políticas específicas ao ambiente:
var requiredScopes = builder.Configuration["AzureAd:Scopes"]?.Split(' ');
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiAccessPolicy", policyBuilder =>
{
if (requiredScopes != null)
{
policyBuilder.RequireScope(requiredScopes);
}
});
});
Filtrar pedidos por inquilino
Restringa o acesso à API a tokens provenientes de inquilinos específicos do Microsoft Entra. Isto é útil quando a sua API multi-inquilino só deve aceitar pedidos de clientes arrendatários aprovados.
Restringir o acesso aos inquilinos permitidos
Defina uma apólice que verifique a reivindicação do ID do inquilino contra uma lista de permissões:
builder.Services.AddAuthorization(options =>
{
string[] allowedTenants =
{
"14c2f153-90a7-4689-9db7-9543bf084dad", // Contoso tenant
"af8cc1a0-d2aa-4ca7-b829-00d361edb652", // Fabrikam tenant
"979f4440-75dc-4664-b2e1-2cafa0ac67d1" // Northwind tenant
};
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
// Apply to all endpoints by default
options.DefaultPolicy = options.GetPolicy("AllowedTenantsOnly");
});
Configurar filtragem de inquilinos a partir das definições
A Store permitiu o armazenamento de IDs dos locatários na configuração para gerenciá-los sem alterações no código.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "your-api-client-id",
"AllowedTenants": [
"14c2f153-90a7-4689-9db7-9543bf084dad",
"af8cc1a0-d2aa-4ca7-b829-00d361edb652"
]
}
}
Leia a lista de inquilinos e crie a política no início:
var allowedTenants = builder.Configuration.GetSection("AzureAd:AllowedTenants")
.Get<string[]>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants ?? Array.Empty<string>()
);
});
});
Combinar escopos com filtragem de locatários
Crie uma apólice que exija tanto um âmbito válido como um inquilino aprovado:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("SecureApiAccess", policyBuilder =>
{
// Require specific scope
policyBuilder.RequireScope("access_as_user");
// AND require specific tenant
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
});
Siga as melhores práticas
Aplicar estas recomendações para construir uma lógica de autorização segura e sustentável.
Coisas a Fazer
1. Sempre emparelhar [Authorize] com validação de escopo:
[Authorize] // Authentication
[RequiredScope("access_as_user")] // Authorization
public class MyController : ControllerBase { }
2. Utilizar a configuração para escopos específicos do ambiente:
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
3. Aplicar o privilégio mínimo:
[HttpGet]
[RequiredScope("read")] // Only read permission needed
[HttpPost]
[RequiredScope("write")] // Write permission for modifications
4. Utilizar políticas para autorizações complexas:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
{
policy.RequireScope("admin");
policy.RequireClaim("department", "IT");
});
});
5. Permitir respostas detalhadas a erros no desenvolvimento:
if (builder.Environment.IsDevelopment())
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
}
Coisas a não fazer
1. Não salte [Authorize] ao usar RequiredScope:
// Wrong - RequiredScope won't work without [Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
// Correct
[Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
2. Não codifique os IDs de cliente em produção:
// Wrong
policyBuilder.RequireClaim("tid", "14c2f153-90a7-4689-9db7-9543bf084dad");
// Better - use configuration
var tenants = Configuration.GetSection("AllowedTenants").Get<string[]>();
policyBuilder.RequireClaim("tid", tenants);
3. Não confunda âmbitos com papéis:
// Wrong - This checks roles claim, not scopes
[RequiredScope("Admin")] // "Admin" is typically a role, not a scope
// Correct
[RequiredScope("access_as_user")] // Scope
[Authorize(Roles = "Admin")] // Role
4. Não exponha informações sensíveis de escopo em mensagens de erro de produção:
Configure níveis de registo apropriados e o tratamento de erros para ambientes de produção.
Soluções para problemas de autorização
Use as seguintes orientações para diagnosticar problemas comuns de autorização.
403 Forbidden - falta de escopo
Erro: A API devolve 403 mesmo com um token válido.
Diagnóstico:
- Decifrar o token em jwt.ms.
- Verifica a
scpou ascopereclamação. - Verifica se o valor corresponde ao teu
RequiredScopeatributo.
Solution:
- Certifique-se de que a aplicação cliente solicita o âmbito correto ao adquirir o token.
- Verifique se o âmbito está exposto no registo da aplicação API na Microsoft Entra.
- Conceda consentimento administrativo se necessário.
RequiredScope não funciona
Sintoma: O atributo parece ser ignorado.
Verifique:
- Adicionaste o
[Authorize]atributo? - Depois de
app.UseAuthorization(), é chamadoapp.UseAuthentication()? - Está o/a
services.AddAuthorization()registado/a?
Chave de configuração não encontrada
Erro: A validação do escopo falha silenciosamente.
Verifique:
{
"AzureAd": {
"Scopes": "access_as_user" // Matches RequiredScopesConfigurationKey
}
}
Garantir que o caminho de configuração corresponda exatamente.