Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Erstellen Sie eine TypeScript/Node.js Clientbibliothek, die in das Microsoft Entra SDK für AgentID integriert ist, um Token abzurufen und downstream-APIs aufzurufen. Integrieren Sie diesen Client dann in Express.js- oder NestJS-Anwendungen, um authentifizierte API-Anforderungen zu verarbeiten.
Voraussetzungen
- Ein Azure-Konto mit einem aktiven Abonnement. Kostenlos ein Konto erstellen.
- Node.js (Version 14 oder höher) mit npm auf Ihrem Entwicklungscomputer installiert.
- Das Microsoft Entra SDK für AgentID wird in Ihrer Umgebung bereitgestellt und ausgeführt. Anweisungen zur Einrichtung finden Sie im Installationshandbuch .
- Downstream-APIs , die im SDK mit Basis-URLs und erforderlichen Bereichen konfiguriert sind.
- Geeignete Berechtigungen in microsoft Entra ID – Ihr Konto muss über Berechtigungen zum Registrieren von Anwendungen verfügen und API-Berechtigungen erteilen.
Konfiguration
Installieren Sie vor dem Erstellen der Clientbibliothek die erforderlichen Abhängigkeiten zum Erstellen von HTTP-Anforderungen:
npm install node-fetch
npm install --save-dev @types/node-fetch
Clientbibliotheksimplementierung
Erstellen Sie eine wiederverwendbare Clientklasse, die HTTP-Aufrufe an das Microsoft Entra SDK für AgentID umschließt. Diese Klasse behandelt die Tokenweiterleitung, Anforderungskonfiguration und Fehlerbehandlung:
// sidecar-client.ts
import fetch from 'node-fetch';
export interface SidecarConfig {
baseUrl: string;
timeout?: number;
}
export class SidecarClient {
private readonly baseUrl: string;
private readonly timeout: number;
constructor(config: SidecarConfig) {
this.baseUrl = config.baseUrl || process.env.SIDECAR_URL || 'http://localhost:5000';
this.timeout = config.timeout || 10000;
}
async getAuthorizationHeader(
incomingToken: string,
serviceName: string,
options?: {
scopes?: string[];
tenant?: string;
agentIdentity?: string;
agentUsername?: string;
}
): Promise<string> {
const url = new URL(`${this.baseUrl}/AuthorizationHeader/${serviceName}`);
if (options?.scopes) {
options.scopes.forEach(scope =>
url.searchParams.append('optionsOverride.Scopes', scope)
);
}
if (options?.tenant) {
url.searchParams.append('optionsOverride.AcquireTokenOptions.Tenant', options.tenant);
}
if (options?.agentIdentity) {
url.searchParams.append('AgentIdentity', options.agentIdentity);
if (options.agentUsername) {
url.searchParams.append('AgentUsername', options.agentUsername);
}
}
const response = await fetch(url.toString(), {
headers: { 'Authorization': incomingToken },
signal: AbortSignal.timeout(this.timeout)
});
if (!response.ok) {
throw new Error(`SDK error: ${response.statusText}`);
}
const data = await response.json();
return data.authorizationHeader;
}
async callDownstreamApi<T>(
incomingToken: string,
serviceName: string,
relativePath: string,
options?: {
method?: string;
body?: any;
scopes?: string[];
}
): Promise<T> {
const url = new URL(`${this.baseUrl}/DownstreamApi/${serviceName}`);
url.searchParams.append('optionsOverride.RelativePath', relativePath);
if (options?.method && options.method !== 'GET') {
url.searchParams.append('optionsOverride.HttpMethod', options.method);
}
if (options?.scopes) {
options.scopes.forEach(scope =>
url.searchParams.append('optionsOverride.Scopes', scope)
);
}
const fetchOptions: any = {
method: options?.method || 'GET',
headers: { 'Authorization': incomingToken },
signal: AbortSignal.timeout(this.timeout)
};
if (options?.body) {
fetchOptions.headers['Content-Type'] = 'application/json';
fetchOptions.body = JSON.stringify(options.body);
}
const response = await fetch(url.toString(), fetchOptions);
if (!response.ok) {
throw new Error(`SDK error: ${response.statusText}`);
}
const data = await response.json();
if (data.statusCode >= 400) {
throw new Error(`API error ${data.statusCode}: ${data.content}`);
}
return JSON.parse(data.content) as T;
}
}
// Usage
const sidecar = new SidecarClient({ baseUrl: 'http://localhost:5000' });
// Get authorization header
const authHeader = await sidecar.getAuthorizationHeader(token, 'Graph');
// Call API
interface UserProfile {
displayName: string;
mail: string;
userPrincipalName: string;
}
const profile = await sidecar.callDownstreamApi<UserProfile>(
token,
'Graph',
'me'
);
Express.js Integration
Integrieren Sie die Clientbibliothek in eine Express.js-Anwendung, indem Sie Middleware und Routenhandler erstellen, die das eingehende Token extrahieren und nachgeschaltete APIs aufrufen.
import express from 'express';
import { SidecarClient } from './sidecar-client';
const app = express();
app.use(express.json());
const sidecar = new SidecarClient({ baseUrl: process.env.SIDECAR_URL! });
// Middleware to extract token
app.use((req, res, next) => {
const token = req.headers.authorization;
if (!token && !req.path.startsWith('/health')) {
return res.status(401).json({ error: 'No authorization token' });
}
req.userToken = token;
next();
});
// Routes
app.get('/api/profile', async (req, res) => {
try {
const profile = await sidecar.callDownstreamApi(
req.userToken,
'Graph',
'me'
);
res.json(profile);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/api/messages', async (req, res) => {
try {
const messages = await sidecar.callDownstreamApi(
req.userToken,
'Graph',
'me/messages?$top=10'
);
res.json(messages);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(8080, () => {
console.log('Server running on port 8080');
});
NestJS-Integration
Erstellen Sie für NestJS-Anwendungen einen Dienst, der die Clientbibliothek umschließt. Dieser Dienst kann in Controller eingefügt werden, um authentifizierte Anforderungen zu verarbeiten:
import { Injectable } from '@nestjs/common';
import { SidecarClient } from './sidecar-client';
@Injectable()
export class GraphService {
private readonly sidecar: SidecarClient;
constructor() {
this.sidecar = new SidecarClient({
baseUrl: process.env.SIDECAR_URL!
});
}
async getUserProfile(token: string) {
return await this.sidecar.callDownstreamApi(
token,
'Graph',
'me'
);
}
async getUserMessages(token: string, top: number = 10) {
return await this.sidecar.callDownstreamApi(
token,
'Graph',
`me/messages?$top=${top}`
);
}
}
Bewährte Methoden
Wenn Sie das Microsoft Entra SDK für AgentID aus TypeScript verwenden, befolgen Sie die folgenden Methoden, um zuverlässige und verwaltete Anwendungen zu erstellen:
-
Clientinstanz wiederverwenden: Erstellen Sie eine einzelne
SidecarClientInstanz, und verwenden Sie sie in der gesamten Anwendung wieder, anstatt neue Instanzen pro Anforderung zu erstellen. Dadurch wird die Leistung und die Ressourcennutzung verbessert. - Legen Sie geeignete Timeouts fest: Konfigurieren Sie Anforderungstimeouts basierend auf der nachgeschalteten API-Latenz. Dadurch wird verhindert, dass Ihre Anwendung unbegrenzt hängen bleibt, wenn der SDK- oder downstream-Dienst langsam ist.
- Implementieren der Fehlerbehandlung: Fügen Sie eine ordnungsgemäße Fehlerbehandlungs- und Wiederholungslogik hinzu, insbesondere für vorübergehende Fehler. Unterscheiden Sie zwischen Clientfehlern (4xx) und Serverfehlern (5xx), um geeignete Antworten zu ermitteln.
- Verwenden Sie TypeScript-Schnittstellen: Definieren Sie TypeScript-Schnittstellen für API-Antworten, um die Typsicherheit sicherzustellen und Fehler zur Kompilierungszeit anstatt zur Laufzeit abzufangen.
- Aktivieren von Verbindungspooling: Verwenden Sie HTTP-Agents, um die Wiederverwendung der Verbindung über Anforderungen hinweg zu ermöglichen, wodurch der Aufwand reduziert und der Durchsatz verbessert wird.
Andere Sprachhandbücher
Nächste Schritte
Beginnen Sie mit einem Szenario: