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.
Valide os tokens portadores recebidos encaminhando-os para o SDK de Microsoft Entra para o endpoint /Validate do AgentID, e depois extraia as reivindicações devolvidas para tomar decisões de autorização. Este guia mostra como implementar middleware de validação de token e tomar decisões de autorização com base em escopos ou funções.
Pré-requisitos
- Uma conta no Azure com uma subscrição ativa. Crie uma conta gratuitamente.
- Microsoft Entra SDK para AgentID implementado e a correr com acesso à rede através da sua aplicação. Consulte o Guia de Instalação para obter instruções de configuração.
-
Aplicação registada em Microsoft Entra ID - Registar uma nova aplicação no centro de administração Microsoft Entra, configurada apenas para Contas neste diretório organizacional. Consulte Registar uma candidatura para obter mais detalhes. Registre os seguintes valores na página Visão geral do aplicativo:
- ID da aplicação (cliente)
- ID do diretório (inquilino)
- Configurar um URI de ID de Aplicação na secção Expor uma API (utilizado como destinatário para validação de token)
- Tokens de portador de clientes autenticados - O seu aplicativo deve receber tokens de aplicações cliente por meio de fluxos OAuth 2.0.
- Permissões apropriadas em Microsoft Entra ID - A sua conta deve ter permissões para registar aplicações e configurar as definições de autenticação.
Configuração
Para validar tokens para a sua API, configure o Microsoft Entra SDK para o Agent ID com a informação do seu tenant do Microsoft Entra ID.
env:
- name: AzureAd__Instance
value: "https://login.microsoftonline.com/"
- name: AzureAd__TenantId
value: "your-tenant-id"
- name: AzureAd__ClientId
value: "your-api-client-id"
- name: AzureAd__Audience
value: "api://your-api-id"
TypeScript/Node.js
A implementação seguinte mostra como criar um middleware de validação de token que se integra com o Microsoft Entra SDK para ID de Agente, usando TypeScript ou JavaScript. Esse middleware verifica cada solicitação de entrada para um token de portador válido e extrai declarações para uso em seus manipuladores de rota:
import fetch from 'node-fetch';
interface ValidateResponse {
protocol: string;
token: string;
claims: {
aud: string;
iss: string;
oid: string;
sub: string;
tid: string;
upn?: string;
scp?: string;
roles?: string[];
[key: string]: any;
};
}
async function validateToken(authorizationHeader: string): Promise<ValidateResponse> {
const sidecarUrl = process.env.SIDECAR_URL || 'http://localhost:5000';
const response = await fetch(`${sidecarUrl}/Validate`, {
headers: {
'Authorization': authorizationHeader
}
});
if (!response.ok) {
throw new Error(`Token validation failed: ${response.statusText}`);
}
return await response.json() as ValidateResponse;
}
O trecho a seguir demonstra como usar a função validateToken em um middleware Express.js para proteger endpoints da API.
// Express.js middleware example
import express from 'express';
const app = express();
// Token validation middleware
async function requireAuth(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ error: 'No authorization token provided' });
}
try {
const validation = await validateToken(authHeader);
// Attach claims to request object
req.user = {
id: validation.claims.oid,
upn: validation.claims.upn,
tenantId: validation.claims.tid,
scopes: validation.claims.scp?.split(' ') || [],
roles: validation.claims.roles || [],
claims: validation.claims
};
next();
} catch (error) {
console.error('Token validation failed:', error);
return res.status(401).json({ error: 'Invalid token' });
}
}
// Protected endpoint
app.get('/api/protected', requireAuth, (req, res) => {
res.json({
message: 'Access granted',
user: {
id: req.user.id,
upn: req.user.upn
}
});
});
// Scope-based authorization
app.get('/api/admin', requireAuth, (req, res) => {
if (!req.user.roles.includes('Admin')) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
res.json({ message: 'Admin access granted' });
});
app.listen(8080);
Python
O seguinte excerto em Python utiliza decoradores Flask para envolver manipuladores de rotas com validação de token. Este decorador extrai o token de portador do cabeçalho de Autorização, valida-o com o SDK Microsoft Entra para AgentID e disponibiliza as declarações na sua rota:
import os
import requests
from flask import Flask, request, jsonify
from functools import wraps
app = Flask(__name__)
def validate_token(authorization_header: str) -> dict:
"""Validate token using the SDK."""
sidecar_url = os.getenv('SIDECAR_URL', 'http://localhost:5000')
response = requests.get(
f"{sidecar_url}/Validate",
headers={'Authorization': authorization_header}
)
if not response.ok:
raise Exception(f"Token validation failed: {response.text}")
return response.json()
# Token validation decorator
def require_auth(f):
@wraps(f)
def decorated_function(*args, **kwargs):
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify({'error': 'No authorization token provided'}), 401
try:
validation = validate_token(auth_header)
# Attach user info to Flask's g object
from flask import g
g.user = {
'id': validation['claims']['oid'],
'upn': validation['claims'].get('upn'),
'tenant_id': validation['claims']['tid'],
'scopes': validation['claims'].get('scp', '').split(' '),
'roles': validation['claims'].get('roles', []),
'claims': validation['claims']
}
return f(*args, **kwargs)
except Exception as e:
print(f"Token validation failed: {e}")
return jsonify({'error': 'Invalid token'}), 401
return decorated_function
# Protected endpoint
@app.route('/api/protected')
@require_auth
def protected():
from flask import g
return jsonify({
'message': 'Access granted',
'user': {
'id': g.user['id'],
'upn': g.user['upn']
}
})
# Role-based authorization
@app.route('/api/admin')
@require_auth
def admin():
from flask import g
if 'Admin' not in g.user['roles']:
return jsonify({'error': 'Insufficient permissions'}), 403
return jsonify({'message': 'Admin access granted'})
if __name__ == '__main__':
app.run(port=8080)
Go
A implementação Go a seguir demonstra a validação de token usando o padrão de manipulador HTTP padrão. Esta abordagem middleware extrai tokens portadores do cabeçalho Authorization, valida-os com o Microsoft Entra SDK for AgentID e armazena informações do utilizador em cabeçalhos de pedido para uso em handlers a jusante:
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
type ValidateResponse struct {
Protocol string `json:"protocol"`
Token string `json:"token"`
Claims map[string]interface{} `json:"claims"`
}
type User struct {
ID string
UPN string
TenantID string
Scopes []string
Roles []string
Claims map[string]interface{}
}
func validateToken(authHeader string) (*ValidateResponse, error) {
sidecarURL := os.Getenv("SIDECAR_URL")
if sidecarURL == "" {
sidecarURL = "http://localhost:5000"
}
req, err := http.NewRequest("GET", fmt.Sprintf("%s/Validate", sidecarURL), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", authHeader)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("token validation failed: %s", resp.Status)
}
var validation ValidateResponse
if err := json.NewDecoder(resp.Body).Decode(&validation); err != nil {
return nil, err
}
return &validation, nil
}
// Middleware for token validation
func requireAuth(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "No authorization token provided", http.StatusUnauthorized)
return
}
validation, err := validateToken(authHeader)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Extract user information from claims
user := &User{
ID: validation.Claims["oid"].(string),
TenantID: validation.Claims["tid"].(string),
Claims: validation.Claims,
}
if upn, ok := validation.Claims["upn"].(string); ok {
user.UPN = upn
}
if scp, ok := validation.Claims["scp"].(string); ok {
user.Scopes = strings.Split(scp, " ")
}
if roles, ok := validation.Claims["roles"].([]interface{}); ok {
for _, role := range roles {
user.Roles = append(user.Roles, role.(string))
}
}
// Store user in context (simplified - use context.Context in production)
r.Header.Set("X-User-ID", user.ID)
r.Header.Set("X-User-UPN", user.UPN)
next(w, r)
}
}
func protectedHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"message": "Access granted",
"user": map[string]string{
"id": r.Header.Get("X-User-ID"),
"upn": r.Header.Get("X-User-UPN"),
},
})
}
func main() {
http.HandleFunc("/api/protected", requireAuth(protectedHandler))
fmt.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
C#
A seguinte implementação em C# demonstra validação de tokens usando middleware ASP.NET Core. Esta abordagem utiliza injeção de dependência para aceder ao serviço de validação de tokens, extrai tokens bearer do cabeçalho Authorization, valida-os com o Microsoft Entra SDK para AgentID e armazena as declarações dos utilizadores no HttpContext para uso em controladores:
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
public class ValidateResponse
{
public string Protocol { get; set; }
public string Token { get; set; }
public JsonElement Claims { get; set; }
}
public class TokenValidationService
{
private readonly HttpClient _httpClient;
private readonly string _sidecarUrl;
public TokenValidationService(IHttpClientFactory httpClientFactory, IConfiguration config)
{
_httpClient = httpClientFactory.CreateClient();
_sidecarUrl = config["SIDECAR_URL"] ?? "http://localhost:5000";
}
public async Task<ValidateResponse> ValidateTokenAsync(string authorizationHeader)
{
var request = new HttpRequestMessage(HttpMethod.Get, $"{_sidecarUrl}/Validate");
request.Headers.Add("Authorization", authorizationHeader);
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ValidateResponse>();
}
}
// Middleware example
public class TokenValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenValidationService _validationService;
public TokenValidationMiddleware(RequestDelegate next, TokenValidationService validationService)
{
_next = next;
_validationService = validationService;
}
public async Task InvokeAsync(HttpContext context)
{
var authHeader = context.Request.Headers["Authorization"].ToString();
if (string.IsNullOrEmpty(authHeader))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(new { error = "No authorization token" });
return;
}
try
{
var validation = await _validationService.ValidateTokenAsync(authHeader);
// Store claims in HttpContext.Items for use in controllers
context.Items["UserClaims"] = validation.Claims;
context.Items["UserId"] = validation.Claims.GetProperty("oid").GetString();
await _next(context);
}
catch (Exception ex)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(new { error = "Invalid token" });
}
}
}
// Controller example
[ApiController]
[Route("api")]
public class ProtectedController : ControllerBase
{
[HttpGet("protected")]
public IActionResult GetProtected()
{
var userId = HttpContext.Items["UserId"] as string;
return Ok(new
{
message = "Access granted",
user = new { id = userId }
});
}
}
Extração de alegações específicas
Depois de validar um token, você pode extrair as declarações para tomar decisões de autorização em seu aplicativo. O /Validate endpoint retorna um objeto de claims com as seguintes informações:
{
"protocol": "Bearer",
"claims": {
"oid": "user-object-id",
"upn": "user@contoso.com",
"tid": "tenant-id",
"scp": "User.Read Mail.Read",
"roles": ["Admin"]
}
}
As alegações comuns incluem:
-
oid: Identificador de objeto (ID de utilizador único) no seu Microsoft Entra ID tenant -
upn: Nome principal do usuário (normalmente formato de e-mail) -
tid: ID do locatário a que o utilizador pertence -
scp: Escopos delegados que o usuário concedeu ao seu aplicativo -
roles: Funções de aplicativo atribuídas ao usuário
Os exemplos a seguir mostram como extrair declarações específicas da resposta de validação:
Identidade do usuário:
// Extract user identity
const userId = validation.claims.oid; // Object ID
const userPrincipalName = validation.claims.upn; // User Principal Name
const tenantId = validation.claims.tid; // Tenant ID
Escopos e funções:
// Extract scopes (delegated permissions)
const scopes = validation.claims.scp?.split(' ') || [];
// Check for specific scope
if (scopes.includes('User.Read')) {
// Allow access
}
// Extract roles (application permissions)
const roles = validation.claims.roles || [];
// Check for specific role
if (roles.includes('Admin')) {
// Allow admin access
}
Padrões de autorização
Depois de validar tokens, você pode impor a autorização com base em escopos delegados (permissões concedidas pelo usuário) ou funções de aplicativo (atribuídas pelo administrador do locatário). Escolha o padrão que corresponde ao seu modelo de autorização:
Autorização baseada no âmbito
Verifique se o token de usuário inclui os escopos necessários antes de conceder acesso:
function requireScopes(requiredScopes: string[]) {
return async (req, res, next) => {
const validation = await validateToken(req.headers.authorization);
const userScopes = validation.claims.scp?.split(' ') || [];
const hasAllScopes = requiredScopes.every(s => userScopes.includes(s));
if (!hasAllScopes) {
return res.status(403).json({ error: 'Insufficient scopes' });
}
next();
};
}
app.get('/api/mail', requireScopes(['Mail.Read']), (req, res) => {
res.json({ message: 'Mail access granted' });
});
Autorização baseada em funções
Verifique se o usuário tem funções de aplicativo necessárias:
function requireRoles(requiredRoles: string[]) {
return async (req, res, next) => {
const validation = await validateToken(req.headers.authorization);
const userRoles = validation.claims.roles || [];
const hasRole = requiredRoles.some(r => userRoles.includes(r));
if (!hasRole) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
app.delete('/api/resource', requireRoles(['Admin']), (req, res) => {
res.json({ message: 'Resource deleted' });
});
Tratamento de erros
A validação do token pode falhar por vários motivos: o token pode estar expirado, inválido ou faltando escopos necessários. Implemente o tratamento de erros que distingue entre diferentes cenários de falha para que você possa responder adequadamente:
async function validateTokenSafely(authHeader: string): Promise<ValidateResponse | null> {
try {
return await validateToken(authHeader);
} catch (error) {
if (error.message.includes('401')) {
console.error('Token is invalid or expired');
} else if (error.message.includes('403')) {
console.error('Token missing required scopes');
} else {
console.error('Token validation error:', error.message);
}
return null;
}
}
Erros comuns de validação
| Erro | Motivo | Solução |
|---|---|---|
| 401 Não autorizado | Token inválido ou expirado | Solicitar novo token do cliente |
| 403 Proibido | Escopos obrigatórios em falta | Atualizar configuração de escopo ou solicitação de token |
| 400 Pedido Inválido | Cabeçalho de autorização malformado | Verifique o formato do cabeçalho: ****** |
Estrutura de resposta
O /Validate endpoint retorna:
{
"protocol": "Bearer",
"token": "******",
"claims": {
"aud": "api://your-api-id",
"iss": "https://sts.windows.net/tenant-id/",
"iat": 1234567890,
"nbf": 1234567890,
"exp": 1234571490,
"oid": "user-object-id",
"sub": "subject",
"tid": "tenant-id",
"upn": "user@contoso.com",
"scp": "User.Read Mail.Read",
"roles": ["Admin"]
}
}
Melhores práticas
- Validar antecipadamente: valide tokens no gateway da API ou no ponto de entrada
- Verificar escopos: Assegure-se de que o token tem os escopos necessários para a operação
- Falhas de log: falhas de validação de log para monitoramento de segurança
- Manipular erros: Fornecer mensagens de erro claras para depuração
- Usar middleware: implementar a validação como middleware para consistência
- SDK seguro: verifique se o SDK só está acessível a partir do seu aplicativo