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