Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Zweryfikuj przychodzące tokeny uwierzytelniające, przekazując je do punktu końcowego /Validate zestawu Microsoft Entra SDK dla AgentID, a następnie wyodrębnij zwrócone oświadczenia w celu podejmowania decyzji autoryzacyjnych. W tym przewodniku przedstawiono sposób implementowania warstwy pośredniczącej do weryfikacji tokenu i podejmowania decyzji dotyczących uprawnień na podstawie zakresów lub ról.
Wymagania wstępne
- Konto Azure z aktywną subskrypcją. Utwórz konto bezpłatnie.
- Zestaw Microsoft Entra SDK dla identyfikatora AgentID wdrożony i uruchomiony z dostępem do sieci z aplikacji. Aby uzyskać instrukcje dotyczące instalacji, zobacz Przewodnik instalacji.
-
Zarejestrowana aplikacja w usłudze Microsoft Entra ID — zarejestruj nową aplikację w centrum administracyjnym firmy Microsoft Entra, skonfigurowaną tylko dla kont w tym katalogu organizacyjnym. Aby uzyskać więcej informacji, zobacz Rejestrowanie aplikacji . Zapisz następujące wartości na stronie Przegląd aplikacji:
- Identyfikator aplikacji (klienta)
- Identyfikator katalogu (klienta)
- Skonfiguruj identyfikator URI aplikacji w sekcji Udostępnienie interfejsu API (używany jako widownia do weryfikacji tokenów)
- Tokeny dostępu typu Bearer od uwierzytelnionych klientów — aplikacja musi odbierać tokeny z aplikacji klienckich za pośrednictwem przepływów protokołu OAuth 2.0.
- Odpowiednie uprawnienia w usłudze Microsoft Entra ID — Twoje konto musi mieć uprawnienia do rejestrowania aplikacji i konfigurowania ustawień uwierzytelniania.
Konfiguracja
Aby zweryfikować tokeny dla interfejsu API, skonfiguruj zestaw Microsoft Entra SDK for AgentID przy użyciu danych o dzierżawie 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
Poniższa implementacja pokazuje, jak utworzyć oprogramowanie pośredniczące weryfikacji tokenu zintegrowane z zestawem Microsoft Entra SDK dla identyfikatora AgentID przy użyciu języka TypeScript lub JavaScript. To oprogramowanie pośredniczące sprawdza każde przychodzące żądanie prawidłowego tokenu elementu nośnego i wyodrębnia oświadczenia do użycia w programach obsługi tras:
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;
}
Poniższy fragment kodu przedstawia sposób używania funkcji w oprogramowaniu pośredniczącym Express.js w celu ochrony punktów końcowych interfejsu 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
Poniższy fragment kodu języka Python używa dekoratorów platformy Flask do zawijania procedur obsługi tras przy użyciu weryfikacji tokenu. Ten dekorator wyodrębnia token typu bearer z nagłówka autoryzacji, weryfikuje go za pomocą Microsoft Entra SDK dla AgentID i udostępnia atrybuty na trasie:
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
Poniższa implementacja języka Go demonstruje walidację tokenu przy użyciu standardowego wzorca procedury obsługi HTTP. To podejście oprogramowania pośredniczącego wyodrębnia tokeny elementu nośnego z nagłówka autoryzacji, weryfikuje je za pomocą zestawu Microsoft Entra SDK dla identyfikatora AgentID i przechowuje informacje o użytkowniku w nagłówkach żądań do użycia w programach obsługi podrzędnej:
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#
Poniższa implementacja języka C# demonstruje walidację tokenu przy użyciu oprogramowania pośredniczącego ASP.NET Core. Takie podejście używa iniekcji zależności w celu uzyskania dostępu do usługi weryfikacji tokenu, wyodrębnia tokeny typu bearer z nagłówka Authorization, weryfikuje je za pomocą Microsoft Entra SDK for AgentID i przechowuje żądania użytkowników w obiekcie HttpContext do użycia w kontrolerach.
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 }
});
}
}
Wyodrębnianie określonych roszczeń
Po zweryfikowaniu tokenu możesz wyodrębnić oświadczenia, aby podejmować decyzje dotyczące autoryzacji w aplikacji. Punkt /Validate końcowy zwraca obiekt oświadczeń z następującymi informacjami:
{
"protocol": "Bearer",
"claims": {
"oid": "user-object-id",
"upn": "user@contoso.com",
"tid": "tenant-id",
"scp": "User.Read Mail.Read",
"roles": ["Admin"]
}
}
Typowe oświadczenia obejmują:
-
oid: identyfikator obiektu (unikatowy identyfikator użytkownika) w dzierżawie Microsoft Entra ID -
upn: główna nazwa użytkownika (zazwyczaj format poczty e-mail) -
tid: identyfikator dzierżawcy, do którego należy użytkownik -
scp: Delegowane zakresy, które użytkownik przyznał aplikacji -
roles: Role aplikacji przypisane do użytkownika
W poniższych przykładach pokazano, jak wyodrębnić określone oświadczenia z odpowiedzi weryfikacji:
Tożsamość użytkownika:
// 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
Zakresy i role:
// 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
}
Wzorce autoryzacji
Po zweryfikowaniu tokenów można wymusić autoryzację na podstawie delegowanych zakresów (uprawnień przyznanych przez użytkownika) lub ról aplikacji (przypisanych przez administratora dzierżawy). Wybierz wzorzec zgodny z modelem autoryzacji:
Autoryzacja oparta na zakresie
Przed udzieleniem dostępu sprawdź, czy token użytkownika zawiera wymagane zakresy:
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' });
});
Autoryzacja oparta na rolach
Sprawdź, czy użytkownik ma wymagane role aplikacji:
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' });
});
Obsługa błędów
Weryfikacja tokenu może zakończyć się niepowodzeniem z kilku powodów: token może być wygasły, nieprawidłowy lub brakuje wymaganych zakresów. Zaimplementuj obsługę błędów, która rozróżnia różne scenariusze awarii, aby odpowiednio reagować:
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;
}
}
Typowe błędy walidacji
| Error | Przyczyna | Rozwiązanie |
|---|---|---|
| 401 Brak autoryzacji | Nieprawidłowy lub wygasły token | Żądanie nowego tokenu od klienta |
| 403 Zabronione | Brak wymaganych zakresów | Aktualizowanie konfiguracji zakresu lub żądania tokenu |
| 400 Nieprawidłowe żądanie | Źle sformułowany nagłówek autoryzacji | Sprawdź format nagłówka: ****** |
Struktura odpowiedzi
Punkt /Validate końcowy zwraca:
{
"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"]
}
}
Najlepsze praktyki
- Sprawdzanie poprawności na wczesnym etapie: sprawdzanie poprawności tokenów w bramie interfejsu API lub w punkcie wejścia
- Sprawdź zakresy: zawsze sprawdź, czy token ma wymagane zakresy dla operacji
- Niepowodzenia logowania: niepowodzenia weryfikacji dziennika na potrzeby monitorowania zabezpieczeń
- Obsługa błędów: podaj jasne komunikaty o błędach na potrzeby debugowania
- Użyj oprogramowania pośredniczącego: zaimplementuj walidację jako oprogramowanie pośredniczące w celu zapewnienia spójności
- Bezpieczny zestaw SDK: upewnij się, że zestaw SDK jest dostępny tylko z poziomu aplikacji
Dalsze kroki
Po zweryfikowaniu tokenów może być konieczne: