or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-security.mdauthorization-flows.mdclient-authentication.mdconfiguration.mdgrant-types.mdindex.mdpassport-integration.mdprotected-resources.mdtoken-management.md

passport-integration.mddocs/

0

# Passport.js Integration

1

2

Complete Passport.js strategy for OpenID Connect authentication with Express.js applications, providing seamless integration with the Passport authentication ecosystem.

3

4

## Capabilities

5

6

### Strategy Class

7

8

Passport.js strategy implementation for OpenID Connect authentication.

9

10

```typescript { .api }

11

/**

12

* OpenID Connect Passport strategy

13

*/

14

class Strategy implements passport.Strategy {

15

/** Strategy name */

16

readonly name: string;

17

18

constructor(options: StrategyOptions, verify: VerifyFunction);

19

constructor(options: StrategyOptionsWithRequest, verify: VerifyFunctionWithRequest);

20

21

authenticate<TOptions extends AuthenticateOptions>(

22

req: express.Request,

23

options: TOptions

24

): void;

25

}

26

```

27

28

**Usage Examples:**

29

30

```typescript

31

import * as client from "openid-client";

32

import { Strategy } from "openid-client/passport";

33

import passport from "passport";

34

import express from "express";

35

36

const app = express();

37

38

// Configure OpenID Connect client

39

const config = await client.discovery(

40

new URL("https://accounts.google.com"),

41

process.env.CLIENT_ID!,

42

process.env.CLIENT_SECRET!

43

);

44

45

// Configure Passport strategy

46

passport.use(

47

"oidc",

48

new Strategy(

49

{

50

config: config,

51

callbackURL: "https://example.com/auth/callback",

52

scope: "openid email profile"

53

},

54

(tokens, done) => {

55

// Verify callback - process tokens

56

const claims = tokens.claims();

57

const user = {

58

id: claims?.sub,

59

email: claims?.email,

60

name: claims?.name,

61

accessToken: tokens.access_token,

62

refreshToken: tokens.refresh_token

63

};

64

65

return done(null, user);

66

}

67

)

68

);

69

70

// Routes

71

app.get("/auth/login", passport.authenticate("oidc"));

72

73

app.get("/auth/callback",

74

passport.authenticate("oidc", { failureRedirect: "/login" }),

75

(req, res) => {

76

res.redirect("/dashboard");

77

}

78

);

79

```

80

81

### Strategy Options

82

83

Configuration options for the Passport strategy.

84

85

```typescript { .api }

86

interface StrategyOptions extends StrategyOptionsBase {

87

/** Do not pass request to verify callback */

88

passReqToCallback?: false;

89

}

90

91

interface StrategyOptionsWithRequest extends StrategyOptionsBase {

92

/** Pass request as first argument to verify callback */

93

passReqToCallback: true;

94

}

95

96

interface StrategyOptionsBase {

97

/** OpenID Client configuration instance */

98

config: Configuration;

99

/** Strategy name (defaults to issuer hostname) */

100

name?: string;

101

/** Session key for storing state (defaults to issuer hostname) */

102

sessionKey?: string;

103

/** DPoP handle retrieval function */

104

DPoP?: getDPoPHandle;

105

/** Callback URL */

106

callbackURL?: URL | string;

107

/** OAuth 2.0 scope */

108

scope?: string;

109

/** Rich Authorization Requests */

110

authorizationDetails?: AuthorizationDetails | AuthorizationDetails[];

111

/** Resource indicators */

112

resource?: string | string[];

113

/** Use PAR (Pushed Authorization Requests) */

114

usePAR?: boolean;

115

/** Use JAR (JWT Authorization Requests) */

116

useJAR?: false | CryptoKey | PrivateKey | [CryptoKey | PrivateKey, ModifyAssertionFunction];

117

}

118

```

119

120

**Advanced Configuration Examples:**

121

122

```typescript

123

import * as client from "openid-client";

124

import { Strategy } from "openid-client/passport";

125

126

// Strategy with PAR (Pushed Authorization Requests)

127

passport.use(new Strategy(

128

{

129

config: config,

130

callbackURL: "https://example.com/auth/callback",

131

scope: "openid email profile",

132

usePAR: true // Use PAR for enhanced security

133

},

134

(tokens, done) => {

135

// Process tokens

136

return done(null, { user: tokens.claims()?.sub });

137

}

138

));

139

140

// Strategy with JAR (JWT Authorization Requests)

141

const jarKeyPair = await crypto.subtle.generateKey(

142

{ name: "ECDSA", namedCurve: "P-256" },

143

true,

144

["sign"]

145

);

146

147

passport.use(new Strategy(

148

{

149

config: config,

150

callbackURL: "https://example.com/auth/callback",

151

scope: "openid email profile",

152

useJAR: jarKeyPair.privateKey // Sign authorization requests

153

},

154

(tokens, done) => {

155

return done(null, { user: tokens.claims()?.sub });

156

}

157

));

158

159

// Strategy with DPoP support

160

passport.use(new Strategy(

161

{

162

config: config,

163

callbackURL: "https://example.com/auth/callback",

164

scope: "openid email profile",

165

DPoP: async (req) => {

166

// Return DPoP handle based on request/session

167

const dpopKeyPair = await getDPoPKeyPairFromSession(req);

168

if (dpopKeyPair) {

169

return client.getDPoPHandle(config, dpopKeyPair);

170

}

171

return undefined;

172

}

173

},

174

(tokens, done) => {

175

return done(null, { user: tokens.claims()?.sub });

176

}

177

));

178

```

179

180

### Verify Functions

181

182

Callback functions for processing authentication results.

183

184

```typescript { .api }

185

/**

186

* Standard verify function

187

* @param tokens - Token response from authorization server with helpers

188

* @param verified - Passport verification callback

189

*/

190

type VerifyFunction = (

191

tokens: TokenEndpointResponse & TokenEndpointResponseHelpers,

192

verified: passport.AuthenticateCallback

193

) => void;

194

195

/**

196

* Verify function with request object

197

* @param req - Express request object

198

* @param tokens - Token response from authorization server with helpers

199

* @param verified - Passport verification callback

200

*/

201

type VerifyFunctionWithRequest = (

202

req: express.Request,

203

tokens: TokenEndpointResponse & TokenEndpointResponseHelpers,

204

verified: passport.AuthenticateCallback

205

) => void;

206

```

207

208

**Verify Function Examples:**

209

210

```typescript

211

import * as client from "openid-client";

212

import { Strategy } from "openid-client/passport";

213

214

// Standard verify function

215

const verifyUser: VerifyFunction = (tokens, done) => {

216

const claims = tokens.claims();

217

218

if (!claims) {

219

return done(new Error("No ID token claims"));

220

}

221

222

// Find or create user in database

223

User.findOrCreate({

224

where: { sub: claims.sub },

225

defaults: {

226

email: claims.email,

227

name: claims.name,

228

picture: claims.picture

229

}

230

}).then(([user, created]) => {

231

// Store tokens for API access

232

user.accessToken = tokens.access_token;

233

user.refreshToken = tokens.refresh_token;

234

235

return done(null, user);

236

}).catch(err => {

237

return done(err);

238

});

239

};

240

241

// Verify function with request access

242

const verifyUserWithRequest: VerifyFunctionWithRequest = (req, tokens, done) => {

243

const claims = tokens.claims();

244

245

// Access request information

246

const userAgent = req.get("User-Agent");

247

const ipAddress = req.ip;

248

249

// Log authentication

250

console.log(`User ${claims?.sub} authenticated from ${ipAddress}`);

251

252

// Store additional session information

253

req.session.tokenExpiresAt = Date.now() + (tokens.expires_in || 3600) * 1000;

254

255

return done(null, {

256

id: claims?.sub,

257

email: claims?.email,

258

name: claims?.name,

259

lastLogin: new Date(),

260

userAgent,

261

ipAddress

262

});

263

};

264

265

// Use with strategy

266

passport.use(new Strategy(

267

{

268

config: config,

269

callbackURL: "https://example.com/auth/callback",

270

passReqToCallback: true

271

},

272

verifyUserWithRequest

273

));

274

```

275

276

### Authentication Options

277

278

Options for authentication requests.

279

280

```typescript { .api }

281

interface AuthenticateOptions extends passport.AuthenticateOptions {

282

/** Resource indicators (request-specific) */

283

resource?: string | string[];

284

/** Login hint for authorization request */

285

loginHint?: string;

286

/** ID Token hint for authorization request */

287

idTokenHint?: string;

288

/** Rich Authorization Requests (request-specific) */

289

authorizationDetails?: AuthorizationDetails | AuthorizationDetails[];

290

/** OpenID Connect prompt parameter */

291

prompt?: string;

292

/** OAuth 2.0 scope (request-specific) */

293

scope?: string | string[];

294

/** Callback URL (request-specific) */

295

callbackURL?: URL | string;

296

}

297

```

298

299

**Authentication Examples:**

300

301

```typescript

302

import express from "express";

303

import passport from "passport";

304

305

const app = express();

306

307

// Basic authentication

308

app.get("/auth/login", passport.authenticate("oidc"));

309

310

// Authentication with specific scope

311

app.get("/auth/admin", passport.authenticate("oidc", {

312

scope: "openid email profile admin:read admin:write"

313

}));

314

315

// Authentication with resource indicator

316

app.get("/auth/api", passport.authenticate("oidc", {

317

resource: "https://api.example.com"

318

}));

319

320

// Authentication with login hint

321

app.get("/auth/user/:email", (req, res, next) => {

322

passport.authenticate("oidc", {

323

loginHint: req.params.email

324

})(req, res, next);

325

});

326

327

// Force re-authentication

328

app.get("/auth/reauth", passport.authenticate("oidc", {

329

prompt: "login"

330

}));

331

332

// Request consent

333

app.get("/auth/consent", passport.authenticate("oidc", {

334

prompt: "consent"

335

}));

336

```

337

338

### Strategy Method Overrides

339

340

Extensible methods for customizing strategy behavior.

341

342

```typescript { .api }

343

/**

344

* Return additional authorization request parameters

345

* @param req - Express request object

346

* @param options - Authentication options

347

* @returns Additional parameters or undefined

348

*/

349

authorizationRequestParams<TOptions extends AuthenticateOptions>(

350

req: express.Request,

351

options: TOptions

352

): URLSearchParams | Record<string, string> | undefined;

353

354

/**

355

* Return additional token endpoint parameters

356

* @param req - Express request object

357

* @param options - Authentication options

358

* @returns Additional parameters or undefined

359

*/

360

authorizationCodeGrantParameters<TOptions extends AuthenticateOptions>(

361

req: express.Request,

362

options: TOptions

363

): URLSearchParams | Record<string, string> | undefined;

364

365

/**

366

* Determine current request URL

367

* @param req - Express request object

368

* @returns Current URL for callback processing

369

*/

370

currentUrl(req: express.Request): URL;

371

372

/**

373

* Determine whether to initiate authorization request

374

* @param req - Express request object

375

* @param currentUrl - Current request URL

376

* @param options - Authentication options

377

* @returns True to initiate authorization, false to process callback

378

*/

379

shouldInitiateAuthRequest<TOptions extends AuthenticateOptions>(

380

req: express.Request,

381

currentUrl: URL,

382

options: TOptions

383

): boolean;

384

```

385

386

**Custom Strategy Example:**

387

388

```typescript

389

import * as client from "openid-client";

390

import { Strategy } from "openid-client/passport";

391

392

class CustomOIDCStrategy extends Strategy {

393

// Add custom authorization parameters

394

authorizationRequestParams(req: express.Request, options: any) {

395

const params: Record<string, string> = {};

396

397

// Add custom parameters based on request

398

if (req.query.locale) {

399

params.ui_locales = req.query.locale as string;

400

}

401

402

if (req.headers["x-tenant-id"]) {

403

params.tenant_hint = req.headers["x-tenant-id"] as string;

404

}

405

406

return params;

407

}

408

409

// Add custom token endpoint parameters

410

authorizationCodeGrantParameters(req: express.Request, options: any) {

411

const params: Record<string, string> = {};

412

413

// Add tenant information to token request

414

if (req.headers["x-tenant-id"]) {

415

params.tenant_id = req.headers["x-tenant-id"] as string;

416

}

417

418

return params;

419

}

420

421

// Custom URL handling for proxies

422

currentUrl(req: express.Request): URL {

423

// Handle proxy headers

424

const protocol = req.headers["x-forwarded-proto"] || req.protocol;

425

const host = req.headers["x-forwarded-host"] || req.get("host");

426

const url = req.originalUrl || req.url;

427

428

return new URL(`${protocol}://${host}${url}`);

429

}

430

431

// Custom logic for determining auth vs callback

432

shouldInitiateAuthRequest(req: express.Request, currentUrl: URL, options: any): boolean {

433

// Custom logic - check for specific callback indicator

434

return !currentUrl.searchParams.has("code") &&

435

!currentUrl.searchParams.has("error") &&

436

req.method === "GET";

437

}

438

}

439

440

// Use custom strategy

441

passport.use("custom-oidc", new CustomOIDCStrategy(

442

{

443

config: config,

444

callbackURL: "https://example.com/auth/callback"

445

},

446

(tokens, done) => {

447

return done(null, { user: tokens.claims()?.sub });

448

}

449

));

450

```

451

452

## Complete Express.js Application Example

453

454

```typescript

455

import express from "express";

456

import session from "express-session";

457

import passport from "passport";

458

import * as client from "openid-client";

459

import { Strategy } from "openid-client/passport";

460

461

const app = express();

462

463

// Session configuration

464

app.use(session({

465

secret: process.env.SESSION_SECRET!,

466

resave: false,

467

saveUninitialized: false,

468

cookie: { secure: process.env.NODE_ENV === "production" }

469

}));

470

471

// Passport middleware

472

app.use(passport.initialize());

473

app.use(passport.session());

474

475

// Serialize/deserialize user

476

passport.serializeUser((user: any, done) => {

477

done(null, user.id);

478

});

479

480

passport.deserializeUser(async (id: string, done) => {

481

try {

482

const user = await User.findById(id);

483

done(null, user);

484

} catch (error) {

485

done(error);

486

}

487

});

488

489

// Configure OpenID Connect

490

async function setupAuth() {

491

const config = await client.discovery(

492

new URL(process.env.OIDC_ISSUER!),

493

process.env.CLIENT_ID!,

494

process.env.CLIENT_SECRET!

495

);

496

497

passport.use("oidc", new Strategy(

498

{

499

config: config,

500

callbackURL: process.env.CALLBACK_URL!,

501

scope: "openid email profile"

502

},

503

async (tokens, done) => {

504

try {

505

const claims = tokens.claims();

506

507

const [user] = await User.findOrCreate({

508

where: { sub: claims?.sub },

509

defaults: {

510

email: claims?.email,

511

name: claims?.name,

512

picture: claims?.picture

513

}

514

});

515

516

// Store tokens for API access

517

await user.update({

518

accessToken: tokens.access_token,

519

refreshToken: tokens.refresh_token,

520

tokenExpiresAt: new Date(Date.now() + (tokens.expires_in || 3600) * 1000)

521

});

522

523

return done(null, user);

524

} catch (error) {

525

return done(error);

526

}

527

}

528

));

529

}

530

531

// Authentication routes

532

app.get("/login", passport.authenticate("oidc"));

533

app.get("/auth/callback",

534

passport.authenticate("oidc", { failureRedirect: "/login?error=1" }),

535

(req, res) => {

536

res.redirect("/dashboard");

537

}

538

);

539

540

app.get("/logout", (req, res) => {

541

req.logout(() => {

542

res.redirect("/");

543

});

544

});

545

546

// Protected routes

547

function requireAuth(req: express.Request, res: express.Response, next: express.NextFunction) {

548

if (req.isAuthenticated()) {

549

return next();

550

}

551

res.redirect("/login");

552

}

553

554

app.get("/dashboard", requireAuth, (req, res) => {

555

res.json({ user: req.user });

556

});

557

558

// Start server

559

setupAuth().then(() => {

560

app.listen(3000, () => {

561

console.log("Server running on http://localhost:3000");

562

});

563

});

564

```

565

566

## DPoP Integration Helper

567

568

```typescript { .api }

569

/**

570

* Function to retrieve DPoP handle for requests

571

* @param req - Express request object

572

* @returns DPoP handle or undefined

573

*/

574

type getDPoPHandle = (

575

req: express.Request

576

) => Promise<DPoPHandle | undefined> | DPoPHandle | undefined;

577

```

578

579

**DPoP Session Management:**

580

581

```typescript

582

import * as client from "openid-client";

583

584

// Store DPoP key pair in session

585

const dpopHelper: getDPoPHandle = async (req) => {

586

if (!req.session.dpopKeyPair) {

587

// Generate new key pair for session

588

req.session.dpopKeyPair = await client.randomDPoPKeyPair();

589

}

590

591

return client.getDPoPHandle(config, req.session.dpopKeyPair);

592

};

593

594

passport.use(new Strategy(

595

{

596

config: config,

597

callbackURL: "https://example.com/auth/callback",

598

DPoP: dpopHelper

599

},

600

(tokens, done) => {

601

// Tokens will be sender-constrained if server supports DPoP

602

return done(null, { user: tokens.claims()?.sub });

603

}

604

));

605

```