or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assistants.mdauth.mdclient.mdcrons.mdindex.mdreact.mdruns.mdstore.mdthreads.md

auth.mddocs/

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.