or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdindex.mdstate-management.md

state-management.mddocs/

0

# State Management

1

2

Passport OAuth2 provides multiple state store implementations for CSRF protection and PKCE (Proof Key for Code Exchange) support, enabling secure OAuth 2.0 flows with various storage backends.

3

4

## Capabilities

5

6

### StateStore (Full State Store)

7

8

Full session-based state store implementation that provides CSRF protection by storing state parameters and metadata in the user session.

9

10

```javascript { .api }

11

/**

12

* Full session-based state store constructor (from lib/state/store)

13

* @param options - Configuration options

14

*/

15

function StateStore(options);

16

17

interface SessionStoreOptions {

18

key: string; // Required: Session key for storing state data

19

}

20

21

/**

22

* Store state in session

23

* @param req - HTTP request object with session

24

* @param state - State value to store

25

* @param meta - Metadata about the OAuth request

26

* @param callback - Completion callback

27

*/

28

StateStore.prototype.store = function(req, state, meta, callback);

29

30

/**

31

* Verify provided state against stored state

32

* @param req - HTTP request object with session

33

* @param providedState - State parameter from OAuth callback

34

* @param callback - Verification callback

35

*/

36

StateStore.prototype.verify = function(req, providedState, callback);

37

```

38

39

**Usage Example:**

40

41

```javascript

42

const StateStore = require('passport-oauth2/lib/state/store');

43

44

// Create session store

45

const store = new StateStore({ key: 'oauth2:state' });

46

47

// Usage in strategy

48

const strategy = new OAuth2Strategy({

49

authorizationURL: 'https://example.com/oauth/authorize',

50

tokenURL: 'https://example.com/oauth/token',

51

clientID: 'your-client-id',

52

clientSecret: 'your-client-secret',

53

store: store // Use custom state store

54

}, verifyCallback);

55

```

56

57

### SessionStore (Simple Session Store)

58

59

Simplified session-based state store that generates random nonces for CSRF protection without additional metadata. This is the implementation from `lib/state/session.js`.

60

61

```javascript { .api }

62

/**

63

* Simple session-based state store constructor (from lib/state/session)

64

* @param options - Configuration options

65

*/

66

function SessionStore(options);

67

68

/**

69

* Store random nonce in session

70

* @param req - HTTP request object with session

71

* @param callback - Completion callback receiving generated state

72

*/

73

SessionStore.prototype.store = function(req, callback);

74

75

/**

76

* Verify provided state against stored nonce

77

* @param req - HTTP request object with session

78

* @param providedState - State parameter from OAuth callback

79

* @param callback - Verification callback

80

*/

81

SessionStore.prototype.verify = function(req, providedState, callback);

82

```

83

84

**Implementation Details:**

85

- Generates 24-character random state using `uid2`

86

- Stores state directly in session under configured key

87

- Automatically cleans up session data after verification

88

- Requires Express session middleware

89

90

**Usage Example:**

91

92

```javascript

93

const strategy = new OAuth2Strategy({

94

authorizationURL: 'https://example.com/oauth/authorize',

95

tokenURL: 'https://example.com/oauth/token',

96

clientID: 'your-client-id',

97

clientSecret: 'your-client-secret',

98

state: true // Automatically uses SessionStore from lib/state/session

99

}, verifyCallback);

100

```

101

102

### PKCESessionStore

103

104

PKCE-enabled session state store that supports Proof Key for Code Exchange along with state management for enhanced security.

105

106

```javascript { .api }

107

/**

108

* PKCE-enabled session state store constructor

109

* @param options - Configuration options

110

*/

111

function PKCESessionStore(options);

112

113

/**

114

* Store PKCE verifier and state in session

115

* @param req - HTTP request object with session

116

* @param verifier - PKCE code verifier

117

* @param state - State value to store

118

* @param meta - Metadata about the OAuth request

119

* @param callback - Completion callback receiving state handle

120

*/

121

PKCESessionStore.prototype.store = function(req, verifier, state, meta, callback);

122

123

/**

124

* Verify state and return PKCE code verifier

125

* @param req - HTTP request object with session

126

* @param providedState - State parameter from OAuth callback

127

* @param callback - Verification callback receiving verifier

128

*/

129

PKCESessionStore.prototype.verify = function(req, providedState, callback);

130

```

131

132

**PKCE Flow:**

133

1. Strategy generates `code_verifier` (random 32-byte value, base64url encoded)

134

2. Strategy creates `code_challenge` using SHA256 hash of verifier (for S256 method)

135

3. PKCESessionStore saves verifier with state handle in session

136

4. Authorization request includes `code_challenge` and `code_challenge_method`

137

5. Token request includes `code_verifier` retrieved from session

138

139

**Usage Example:**

140

141

```javascript

142

const strategy = new OAuth2Strategy({

143

authorizationURL: 'https://example.com/oauth/authorize',

144

tokenURL: 'https://example.com/oauth/token',

145

clientID: 'your-client-id',

146

clientSecret: 'your-client-secret',

147

pkce: 'S256', // Enable PKCE with SHA256

148

state: true // Automatically uses PKCESessionStore when PKCE enabled

149

}, verifyCallback);

150

```

151

152

### NullStore

153

154

No-operation state store that performs no state validation, effectively disabling CSRF protection.

155

156

```javascript { .api }

157

/**

158

* No-op state store constructor

159

* @param options - Configuration options (unused)

160

*/

161

function NullStore(options);

162

163

/**

164

* No-op store method

165

* @param req - HTTP request object

166

* @param callback - Completion callback

167

*/

168

NullStore.prototype.store = function(req, callback);

169

170

/**

171

* No-op verify method that always succeeds

172

* @param req - HTTP request object

173

* @param providedState - State parameter (ignored)

174

* @param callback - Verification callback (always returns true)

175

*/

176

NullStore.prototype.verify = function(req, providedState, callback);

177

```

178

179

**Security Warning:** NullStore provides no CSRF protection and should only be used in development or when state validation is handled externally.

180

181

**Usage Example:**

182

183

```javascript

184

const strategy = new OAuth2Strategy({

185

authorizationURL: 'https://example.com/oauth/authorize',

186

tokenURL: 'https://example.com/oauth/token',

187

clientID: 'your-client-id',

188

clientSecret: 'your-client-secret',

189

// No state option = NullStore used automatically

190

}, verifyCallback);

191

```

192

193

## State Store Configuration

194

195

The OAuth2Strategy automatically selects the appropriate state store based on configuration options:

196

197

```javascript { .api }

198

// State store selection logic in OAuth2Strategy

199

if (options.store && typeof options.store === 'object') {

200

this._stateStore = options.store; // Custom store instance

201

} else if (options.store) {

202

// Use built-in stores with session key

203

this._stateStore = options.pkce

204

? new PKCESessionStore({ key: this._key })

205

: new StateStore({ key: this._key }); // Full state store from lib/state/store

206

} else if (options.state) {

207

// Enable state with appropriate store

208

this._stateStore = options.pkce

209

? new PKCESessionStore({ key: this._key })

210

: new SessionStore({ key: this._key }); // Simple session store from lib/state/session

211

} else {

212

// No state protection

213

if (options.pkce) {

214

throw new TypeError('OAuth2Strategy requires `state: true` option when PKCE is enabled');

215

}

216

this._stateStore = new NullStore();

217

}

218

```

219

220

## Custom State Store Implementation

221

222

You can implement custom state stores by providing an object with `store` and `verify` methods:

223

224

```javascript { .api }

225

/**

226

* Custom state store interface

227

*/

228

interface CustomStateStore {

229

/**

230

* Store state data

231

* @param req - HTTP request object

232

* @param state - State to store (or verifier for PKCE)

233

* @param meta - OAuth request metadata

234

* @param callback - Completion callback

235

*/

236

store(req: any, state?: string, meta?: any, callback?: Function): void;

237

238

/**

239

* Verify stored state

240

* @param req - HTTP request object

241

* @param providedState - State from OAuth callback

242

* @param callback - Verification callback

243

*/

244

verify(req: any, providedState: string, callback: Function): void;

245

}

246

```

247

248

**Custom Redis Store Example:**

249

250

```javascript

251

const redis = require('redis');

252

const client = redis.createClient();

253

254

class RedisStateStore {

255

constructor(options) {

256

this.prefix = options.prefix || 'oauth2:state:';

257

this.ttl = options.ttl || 300; // 5 minutes

258

}

259

260

store(req, state, meta, callback) {

261

const key = this.prefix + require('uid2')(24);

262

const data = JSON.stringify({ state, meta, timestamp: Date.now() });

263

264

client.setex(key, this.ttl, data, (err) => {

265

if (err) return callback(err);

266

callback(null, key);

267

});

268

}

269

270

verify(req, providedState, callback) {

271

client.get(providedState, (err, data) => {

272

if (err) return callback(err);

273

if (!data) return callback(null, false, { message: 'Invalid state' });

274

275

client.del(providedState); // Clean up

276

277

try {

278

const parsed = JSON.parse(data);

279

callback(null, true, parsed.state);

280

} catch (e) {

281

callback(null, false, { message: 'Invalid state format' });

282

}

283

});

284

}

285

}

286

287

// Usage

288

const strategy = new OAuth2Strategy({

289

// ... other options

290

store: new RedisStateStore({ prefix: 'myapp:oauth:' })

291

}, verifyCallback);

292

```

293

294

## Session Requirements

295

296

Session-based state stores require session middleware to be configured:

297

298

```javascript

299

const session = require('express-session');

300

301

app.use(session({

302

secret: 'your-session-secret',

303

resave: false,

304

saveUninitialized: false,

305

cookie: { secure: false } // Set to true in production with HTTPS

306

}));

307

```

308

309

**Common Session Errors:**

310

- `OAuth 2.0 authentication requires session support when using state`: Session middleware not configured

311

- `Unable to verify authorization request state`: Session data missing or corrupted

312

- `Invalid authorization request state`: State parameter mismatch