0
# Authentication
1
2
The Auth system provides flexible authentication and authorization for LangGraph applications. It supports custom authentication workflows, event handling, and HTTP exception management for secure application development. The system is designed to integrate seamlessly with various authentication providers and custom authorization logic.
3
4
## Core Functionality
5
6
The Auth system supports:
7
8
- **Custom Authentication**: Implement custom authentication logic with flexible callbacks
9
- **Event Handling**: Register callbacks for various authentication and authorization events
10
- **HTTP Exceptions**: Structured error handling with standard HTTP status codes
11
- **User Management**: Type-safe user handling with extensible user models
12
- **Filter Integration**: Authentication filters for fine-grained access control
13
14
## Auth Class API
15
16
```typescript { .api }
17
class Auth<TExtra = {}, TAuthReturn extends BaseAuthReturn = BaseAuthReturn, TUser extends BaseUser = ToUserLike<TAuthReturn>> {
18
/**
19
* Set the authentication callback function
20
* @param cb - Authentication callback that validates and returns user information
21
* @returns Auth instance with updated authentication type
22
*/
23
authenticate<T extends BaseAuthReturn>(cb: AuthenticateCallback<T>): Auth<TExtra, T>;
24
25
/**
26
* Register event callback handlers for authentication events
27
* @param event - Event type to handle
28
* @param callback - Callback function for the event
29
* @returns This Auth instance for method chaining
30
*/
31
on<T extends CallbackEvent>(event: T, callback: OnCallback<T, TUser>): this;
32
}
33
```
34
35
### HTTPException Class
36
37
```typescript { .api }
38
class HTTPException extends Error {
39
/** HTTP status code */
40
status: number;
41
42
/** Error message */
43
message: string;
44
45
/**
46
* Create HTTP exception with status code
47
* @param message - Error message
48
* @param status - HTTP status code
49
*/
50
constructor(message: string, status: number);
51
}
52
```
53
54
## Core Types
55
56
### Authentication Types
57
58
```typescript { .api }
59
interface BaseAuthReturn {
60
/** Unique user identifier */
61
sub: string;
62
/** Additional user data */
63
[key: string]: any;
64
}
65
66
interface BaseUser {
67
/** User identifier */
68
id: string;
69
/** Additional user properties */
70
[key: string]: any;
71
}
72
73
type ToUserLike<T extends BaseAuthReturn> = {
74
id: T["sub"];
75
} & Omit<T, "sub">;
76
77
type AuthenticateCallback<T extends BaseAuthReturn> = (
78
request: AuthRequest
79
) => Promise<T> | T;
80
81
interface AuthRequest {
82
/** Request headers */
83
headers: Record<string, string>;
84
/** Request query parameters */
85
query: Record<string, string>;
86
/** Request body data */
87
body?: any;
88
/** Request method */
89
method: string;
90
/** Request URL */
91
url: string;
92
/** Request cookies */
93
cookies: Record<string, string>;
94
}
95
```
96
97
### Event System
98
99
```typescript { .api }
100
type CallbackEvent =
101
| "beforeAuth"
102
| "afterAuth"
103
| "authError"
104
| "beforeAccess"
105
| "afterAccess"
106
| "accessDenied"
107
| "sessionStart"
108
| "sessionEnd";
109
110
type OnCallback<T extends CallbackEvent, TUser extends BaseUser> = T extends "beforeAuth"
111
? (request: AuthRequest) => Promise<void> | void
112
: T extends "afterAuth"
113
? (user: TUser, request: AuthRequest) => Promise<void> | void
114
: T extends "authError"
115
? (error: Error, request: AuthRequest) => Promise<void> | void
116
: T extends "beforeAccess"
117
? (user: TUser, resource: string, action: string) => Promise<boolean> | boolean
118
: T extends "afterAccess"
119
? (user: TUser, resource: string, action: string, granted: boolean) => Promise<void> | void
120
: T extends "accessDenied"
121
? (user: TUser, resource: string, action: string) => Promise<void> | void
122
: T extends "sessionStart"
123
? (user: TUser, sessionId: string) => Promise<void> | void
124
: T extends "sessionEnd"
125
? (user: TUser, sessionId: string) => Promise<void> | void
126
: never;
127
```
128
129
### Filter Types
130
131
```typescript { .api }
132
interface AuthFilters {
133
/** User ID filter */
134
userId?: string | string[];
135
/** Role-based filter */
136
roles?: string | string[];
137
/** Permission-based filter */
138
permissions?: string | string[];
139
/** Resource access filter */
140
resources?: string | string[];
141
/** Time-based access filter */
142
timeRange?: {
143
start?: string;
144
end?: string;
145
};
146
/** Custom filter predicates */
147
custom?: Record<string, any>;
148
}
149
150
interface AuthEventValueMap {
151
beforeAuth: { request: AuthRequest };
152
afterAuth: { user: BaseUser; request: AuthRequest };
153
authError: { error: Error; request: AuthRequest };
154
beforeAccess: { user: BaseUser; resource: string; action: string };
155
afterAccess: { user: BaseUser; resource: string; action: string; granted: boolean };
156
accessDenied: { user: BaseUser; resource: string; action: string };
157
sessionStart: { user: BaseUser; sessionId: string };
158
sessionEnd: { user: BaseUser; sessionId: string };
159
}
160
```
161
162
## Usage Examples
163
164
### Basic Authentication Setup
165
166
```typescript
167
import { Auth, HTTPException } from "@langchain/langgraph-sdk/auth";
168
169
// Define custom user type
170
interface CustomUser {
171
id: string;
172
email: string;
173
role: string;
174
permissions: string[];
175
organizationId: string;
176
}
177
178
// Create auth instance with type safety
179
const auth = new Auth<{}, { sub: string; email: string; role: string; permissions: string[]; organizationId: string }, CustomUser>();
180
181
// Set authentication callback
182
auth.authenticate(async (request) => {
183
const authHeader = request.headers.authorization;
184
185
if (!authHeader || !authHeader.startsWith('Bearer ')) {
186
throw new HTTPException("Missing or invalid authorization header", 401);
187
}
188
189
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
190
191
try {
192
// Validate token (example with JWT)
193
const decoded = await validateJWT(token);
194
195
// Fetch user data
196
const user = await getUserFromDatabase(decoded.sub);
197
198
if (!user) {
199
throw new HTTPException("User not found", 404);
200
}
201
202
if (!user.active) {
203
throw new HTTPException("User account is deactivated", 403);
204
}
205
206
return {
207
sub: user.id,
208
email: user.email,
209
role: user.role,
210
permissions: user.permissions,
211
organizationId: user.organizationId
212
};
213
214
} catch (error) {
215
if (error instanceof HTTPException) {
216
throw error;
217
}
218
throw new HTTPException("Invalid token", 401);
219
}
220
});
221
222
// Example JWT validation function
223
async function validateJWT(token: string) {
224
// Implementation depends on your JWT library
225
// Return decoded payload or throw error
226
const decoded = jwt.verify(token, process.env.JWT_SECRET!);
227
return decoded as { sub: string; exp: number; iat: number };
228
}
229
230
// Example user database lookup
231
async function getUserFromDatabase(userId: string) {
232
// Your database query logic
233
return {
234
id: userId,
235
email: "user@example.com",
236
role: "user",
237
permissions: ["read", "write"],
238
organizationId: "org_123",
239
active: true
240
};
241
}
242
```
243
244
### Event Handling and Monitoring
245
246
```typescript
247
// Register authentication event handlers
248
auth.on("beforeAuth", async (request) => {
249
console.log(`Authentication attempt from ${request.headers['x-forwarded-for'] || 'unknown'}`);
250
251
// Rate limiting check
252
await checkRateLimit(request.headers['x-forwarded-for']);
253
});
254
255
auth.on("afterAuth", async (user, request) => {
256
console.log(`User ${user.email} authenticated successfully`);
257
258
// Log successful authentication
259
await logAuthEvent({
260
userId: user.id,
261
event: 'login_success',
262
timestamp: new Date().toISOString(),
263
ip: request.headers['x-forwarded-for'],
264
userAgent: request.headers['user-agent']
265
});
266
267
// Update last login timestamp
268
await updateLastLogin(user.id);
269
});
270
271
auth.on("authError", async (error, request) => {
272
console.error(`Authentication failed: ${error.message}`);
273
274
// Log failed authentication attempt
275
await logAuthEvent({
276
event: 'login_failed',
277
error: error.message,
278
timestamp: new Date().toISOString(),
279
ip: request.headers['x-forwarded-for'],
280
userAgent: request.headers['user-agent']
281
});
282
283
// Check for suspicious activity
284
await checkSuspiciousActivity(request.headers['x-forwarded-for']);
285
});
286
287
// Session management
288
auth.on("sessionStart", async (user, sessionId) => {
289
console.log(`Session started for user ${user.email}: ${sessionId}`);
290
291
await createSessionRecord({
292
sessionId,
293
userId: user.id,
294
startTime: new Date().toISOString(),
295
active: true
296
});
297
});
298
299
auth.on("sessionEnd", async (user, sessionId) => {
300
console.log(`Session ended for user ${user.email}: ${sessionId}`);
301
302
await updateSessionRecord(sessionId, {
303
endTime: new Date().toISOString(),
304
active: false
305
});
306
});
307
```
308
309
### Access Control and Authorization
310
311
```typescript
312
// Role-based access control
313
auth.on("beforeAccess", async (user, resource, action) => {
314
console.log(`Access check: ${user.email} wants to ${action} ${resource}`);
315
316
// Admin users have full access
317
if (user.role === 'admin') {
318
return true;
319
}
320
321
// Check resource-specific permissions
322
const hasPermission = await checkResourcePermission(user.id, resource, action);
323
324
if (!hasPermission) {
325
console.warn(`Access denied: ${user.email} cannot ${action} ${resource}`);
326
return false;
327
}
328
329
// Organization-based access control
330
const resourceOrg = await getResourceOrganization(resource);
331
if (resourceOrg && resourceOrg !== user.organizationId) {
332
console.warn(`Cross-org access denied: ${user.email} cannot access ${resource}`);
333
return false;
334
}
335
336
return true;
337
});
338
339
auth.on("afterAccess", async (user, resource, action, granted) => {
340
// Log access attempt
341
await logAccessEvent({
342
userId: user.id,
343
resource,
344
action,
345
granted,
346
timestamp: new Date().toISOString()
347
});
348
349
if (granted) {
350
console.log(`Access granted: ${user.email} can ${action} ${resource}`);
351
}
352
});
353
354
auth.on("accessDenied", async (user, resource, action) => {
355
console.warn(`Access denied: ${user.email} cannot ${action} ${resource}`);
356
357
// Alert on sensitive resource access attempts
358
if (isSensitiveResource(resource)) {
359
await sendSecurityAlert({
360
userId: user.id,
361
resource,
362
action,
363
timestamp: new Date().toISOString(),
364
severity: 'high'
365
});
366
}
367
});
368
369
// Helper functions for access control
370
async function checkResourcePermission(userId: string, resource: string, action: string): Promise<boolean> {
371
// Check database for user permissions on specific resource
372
const permissions = await getUserResourcePermissions(userId, resource);
373
return permissions.includes(action) || permissions.includes('*');
374
}
375
376
async function getResourceOrganization(resource: string): Promise<string | null> {
377
// Get the organization that owns the resource
378
const resourceData = await getResourceMetadata(resource);
379
return resourceData?.organizationId || null;
380
}
381
382
function isSensitiveResource(resource: string): boolean {
383
const sensitivePatterns = ['/admin/', '/financial/', '/personal-data/'];
384
return sensitivePatterns.some(pattern => resource.includes(pattern));
385
}
386
```
387
388
### Multi-Provider Authentication
389
390
```typescript
391
// Support multiple authentication providers
392
interface AuthProvider {
393
name: string;
394
validate: (request: AuthRequest) => Promise<BaseAuthReturn>;
395
}
396
397
const providers: AuthProvider[] = [
398
{
399
name: 'jwt',
400
validate: async (request) => {
401
const token = extractBearerToken(request);
402
const decoded = await validateJWT(token);
403
return await getUserFromJWT(decoded);
404
}
405
},
406
{
407
name: 'api_key',
408
validate: async (request) => {
409
const apiKey = request.headers['x-api-key'];
410
if (!apiKey) throw new HTTPException("API key required", 401);
411
return await getUserFromApiKey(apiKey);
412
}
413
},
414
{
415
name: 'session',
416
validate: async (request) => {
417
const sessionId = request.cookies['session_id'];
418
if (!sessionId) throw new HTTPException("Session required", 401);
419
return await getUserFromSession(sessionId);
420
}
421
}
422
];
423
424
// Multi-provider authentication
425
auth.authenticate(async (request) => {
426
let lastError: Error | null = null;
427
428
// Try each provider in order
429
for (const provider of providers) {
430
try {
431
console.log(`Trying authentication with provider: ${provider.name}`);
432
const result = await provider.validate(request);
433
console.log(`Authentication successful with provider: ${provider.name}`);
434
return result;
435
} catch (error) {
436
console.log(`Provider ${provider.name} failed: ${error.message}`);
437
lastError = error;
438
continue;
439
}
440
}
441
442
// All providers failed
443
throw lastError || new HTTPException("Authentication failed", 401);
444
});
445
446
// Provider-specific implementations
447
function extractBearerToken(request: AuthRequest): string {
448
const authHeader = request.headers.authorization;
449
if (!authHeader?.startsWith('Bearer ')) {
450
throw new HTTPException("Bearer token required", 401);
451
}
452
return authHeader.substring(7);
453
}
454
455
async function getUserFromApiKey(apiKey: string) {
456
const keyData = await validateApiKey(apiKey);
457
if (!keyData) {
458
throw new HTTPException("Invalid API key", 401);
459
}
460
return {
461
sub: keyData.userId,
462
email: keyData.email,
463
role: keyData.role,
464
permissions: keyData.permissions,
465
organizationId: keyData.organizationId
466
};
467
}
468
469
async function getUserFromSession(sessionId: string) {
470
const session = await getActiveSession(sessionId);
471
if (!session) {
472
throw new HTTPException("Invalid or expired session", 401);
473
}
474
return {
475
sub: session.userId,
476
email: session.userEmail,
477
role: session.userRole,
478
permissions: session.permissions,
479
organizationId: session.organizationId
480
};
481
}
482
```
483
484
### Error Handling and Security
485
486
```typescript
487
// Comprehensive error handling
488
class AuthService {
489
private auth: Auth;
490
private securityLogger: SecurityLogger;
491
492
constructor() {
493
this.auth = new Auth();
494
this.securityLogger = new SecurityLogger();
495
this.setupAuth();
496
}
497
498
private setupAuth() {
499
this.auth.authenticate(async (request) => {
500
try {
501
// Pre-authentication security checks
502
await this.performSecurityChecks(request);
503
504
// Main authentication logic
505
const authResult = await this.performAuthentication(request);
506
507
// Post-authentication validation
508
await this.validateAuthResult(authResult);
509
510
return authResult;
511
512
} catch (error) {
513
await this.handleAuthError(error, request);
514
throw error;
515
}
516
});
517
518
// Error recovery and monitoring
519
this.auth.on("authError", async (error, request) => {
520
await this.securityLogger.logAuthFailure({
521
error: error.message,
522
ip: request.headers['x-forwarded-for'],
523
userAgent: request.headers['user-agent'],
524
timestamp: new Date().toISOString(),
525
endpoint: request.url
526
});
527
528
// Implement progressive delays for repeated failures
529
await this.implementRateLimit(request.headers['x-forwarded-for']);
530
});
531
}
532
533
private async performSecurityChecks(request: AuthRequest) {
534
// IP whitelist/blacklist check
535
const clientIP = request.headers['x-forwarded-for'];
536
if (await this.isBlacklistedIP(clientIP)) {
537
throw new HTTPException("Access denied", 403);
538
}
539
540
// Request validation
541
if (!this.isValidAuthRequest(request)) {
542
throw new HTTPException("Invalid request format", 400);
543
}
544
545
// Rate limiting
546
if (await this.isRateLimited(clientIP)) {
547
throw new HTTPException("Too many requests", 429);
548
}
549
}
550
551
private async performAuthentication(request: AuthRequest): Promise<BaseAuthReturn> {
552
// Determine authentication method
553
if (request.headers.authorization?.startsWith('Bearer ')) {
554
return await this.authenticateJWT(request);
555
} else if (request.headers['x-api-key']) {
556
return await this.authenticateApiKey(request);
557
} else if (request.cookies['session_id']) {
558
return await this.authenticateSession(request);
559
} else {
560
throw new HTTPException("No valid authentication method provided", 401);
561
}
562
}
563
564
private async handleAuthError(error: Error, request: AuthRequest) {
565
if (error instanceof HTTPException) {
566
// Log specific HTTP exceptions
567
await this.securityLogger.logHTTPException(error, request);
568
569
// Check for attack patterns
570
if (this.isLikelyAttack(error, request)) {
571
await this.triggerSecurityAlert(error, request);
572
}
573
} else {
574
// Log unexpected errors
575
await this.securityLogger.logUnexpectedError(error, request);
576
}
577
}
578
579
private isLikelyAttack(error: HTTPException, request: AuthRequest): boolean {
580
// Heuristics for detecting potential attacks
581
const suspiciousPatterns = [
582
/sql/i,
583
/script/i,
584
/<script>/i,
585
/union.*select/i,
586
/etc\/passwd/i
587
];
588
589
const requestContent = JSON.stringify(request);
590
return suspiciousPatterns.some(pattern => pattern.test(requestContent));
591
}
592
593
private async triggerSecurityAlert(error: Error, request: AuthRequest) {
594
await this.securityLogger.triggerAlert({
595
type: 'potential_attack',
596
error: error.message,
597
request: {
598
ip: request.headers['x-forwarded-for'],
599
userAgent: request.headers['user-agent'],
600
url: request.url,
601
method: request.method
602
},
603
timestamp: new Date().toISOString(),
604
severity: 'high'
605
});
606
}
607
}
608
609
// Security logging utility
610
class SecurityLogger {
611
async logAuthFailure(event: any) {
612
// Log to security monitoring system
613
console.error("Auth failure:", event);
614
}
615
616
async logHTTPException(error: HTTPException, request: AuthRequest) {
617
console.warn(`HTTP ${error.status}: ${error.message}`, {
618
ip: request.headers['x-forwarded-for'],
619
url: request.url
620
});
621
}
622
623
async triggerAlert(alert: any) {
624
// Send to security team
625
console.error("SECURITY ALERT:", alert);
626
}
627
}
628
```
629
630
### Integration with LangGraph Client
631
632
```typescript
633
// Use auth with LangGraph Client
634
import { Client } from "@langchain/langgraph-sdk";
635
import { Auth } from "@langchain/langgraph-sdk/auth";
636
637
// Create authenticated client
638
const auth = new Auth();
639
auth.authenticate(async (request) => {
640
// Your authentication logic
641
return await authenticateUser(request);
642
});
643
644
// Custom client with auth integration
645
class AuthenticatedLangGraphClient extends Client {
646
private auth: Auth;
647
648
constructor(config: ClientConfig & { auth: Auth }) {
649
const { auth, ...clientConfig } = config;
650
651
super({
652
...clientConfig,
653
onRequest: async (url, init) => {
654
// Add authentication to requests
655
const authRequest = this.convertToAuthRequest(url, init);
656
const user = await auth.authenticate(authRequest);
657
658
// Add auth headers
659
return {
660
...init,
661
headers: {
662
...init.headers,
663
'Authorization': `Bearer ${user.sub}`,
664
'X-User-Role': user.role
665
}
666
};
667
}
668
});
669
670
this.auth = auth;
671
}
672
673
private convertToAuthRequest(url: URL, init: RequestInit): AuthRequest {
674
return {
675
url: url.toString(),
676
method: init.method || 'GET',
677
headers: (init.headers as Record<string, string>) || {},
678
query: Object.fromEntries(url.searchParams),
679
cookies: {}, // Extract from headers if needed
680
body: init.body
681
};
682
}
683
}
684
685
// Usage
686
const authenticatedClient = new AuthenticatedLangGraphClient({
687
apiUrl: "https://api.langgraph.example.com",
688
apiKey: "your-api-key",
689
auth: auth
690
});
691
```
692
693
The Auth system provides comprehensive authentication and authorization capabilities with flexible event handling, robust error management, and seamless integration with LangGraph applications, ensuring secure and auditable access control.