OAuth 2.0 authentication strategy for Passport that enables developers to implement OAuth 2.0-based authentication in Node.js applications.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
Full session-based state store implementation that provides CSRF protection by storing state parameters and metadata in the user session.
/**
* Full session-based state store constructor (from lib/state/store)
* @param options - Configuration options
*/
function StateStore(options);
interface SessionStoreOptions {
key: string; // Required: Session key for storing state data
}
/**
* Store state in session
* @param req - HTTP request object with session
* @param state - State value to store
* @param meta - Metadata about the OAuth request
* @param callback - Completion callback
*/
StateStore.prototype.store = function(req, state, meta, callback);
/**
* Verify provided state against stored state
* @param req - HTTP request object with session
* @param providedState - State parameter from OAuth callback
* @param callback - Verification callback
*/
StateStore.prototype.verify = function(req, providedState, callback);Usage Example:
const StateStore = require('passport-oauth2/lib/state/store');
// Create session store
const store = new StateStore({ key: 'oauth2:state' });
// Usage in strategy
const strategy = new OAuth2Strategy({
authorizationURL: 'https://example.com/oauth/authorize',
tokenURL: 'https://example.com/oauth/token',
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
store: store // Use custom state store
}, verifyCallback);Simplified session-based state store that generates random nonces for CSRF protection without additional metadata. This is the implementation from lib/state/session.js.
/**
* Simple session-based state store constructor (from lib/state/session)
* @param options - Configuration options
*/
function SessionStore(options);
/**
* Store random nonce in session
* @param req - HTTP request object with session
* @param callback - Completion callback receiving generated state
*/
SessionStore.prototype.store = function(req, callback);
/**
* Verify provided state against stored nonce
* @param req - HTTP request object with session
* @param providedState - State parameter from OAuth callback
* @param callback - Verification callback
*/
SessionStore.prototype.verify = function(req, providedState, callback);Implementation Details:
uid2Usage Example:
const strategy = new OAuth2Strategy({
authorizationURL: 'https://example.com/oauth/authorize',
tokenURL: 'https://example.com/oauth/token',
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
state: true // Automatically uses SessionStore from lib/state/session
}, verifyCallback);PKCE-enabled session state store that supports Proof Key for Code Exchange along with state management for enhanced security.
/**
* PKCE-enabled session state store constructor
* @param options - Configuration options
*/
function PKCESessionStore(options);
/**
* Store PKCE verifier and state in session
* @param req - HTTP request object with session
* @param verifier - PKCE code verifier
* @param state - State value to store
* @param meta - Metadata about the OAuth request
* @param callback - Completion callback receiving state handle
*/
PKCESessionStore.prototype.store = function(req, verifier, state, meta, callback);
/**
* Verify state and return PKCE code verifier
* @param req - HTTP request object with session
* @param providedState - State parameter from OAuth callback
* @param callback - Verification callback receiving verifier
*/
PKCESessionStore.prototype.verify = function(req, providedState, callback);PKCE Flow:
code_verifier (random 32-byte value, base64url encoded)code_challenge using SHA256 hash of verifier (for S256 method)code_challenge and code_challenge_methodcode_verifier retrieved from sessionUsage Example:
const strategy = new OAuth2Strategy({
authorizationURL: 'https://example.com/oauth/authorize',
tokenURL: 'https://example.com/oauth/token',
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
pkce: 'S256', // Enable PKCE with SHA256
state: true // Automatically uses PKCESessionStore when PKCE enabled
}, verifyCallback);No-operation state store that performs no state validation, effectively disabling CSRF protection.
/**
* No-op state store constructor
* @param options - Configuration options (unused)
*/
function NullStore(options);
/**
* No-op store method
* @param req - HTTP request object
* @param callback - Completion callback
*/
NullStore.prototype.store = function(req, callback);
/**
* No-op verify method that always succeeds
* @param req - HTTP request object
* @param providedState - State parameter (ignored)
* @param callback - Verification callback (always returns true)
*/
NullStore.prototype.verify = function(req, providedState, callback);Security Warning: NullStore provides no CSRF protection and should only be used in development or when state validation is handled externally.
Usage Example:
const strategy = new OAuth2Strategy({
authorizationURL: 'https://example.com/oauth/authorize',
tokenURL: 'https://example.com/oauth/token',
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
// No state option = NullStore used automatically
}, verifyCallback);The OAuth2Strategy automatically selects the appropriate state store based on configuration options:
// State store selection logic in OAuth2Strategy
if (options.store && typeof options.store === 'object') {
this._stateStore = options.store; // Custom store instance
} else if (options.store) {
// Use built-in stores with session key
this._stateStore = options.pkce
? new PKCESessionStore({ key: this._key })
: new StateStore({ key: this._key }); // Full state store from lib/state/store
} else if (options.state) {
// Enable state with appropriate store
this._stateStore = options.pkce
? new PKCESessionStore({ key: this._key })
: new SessionStore({ key: this._key }); // Simple session store from lib/state/session
} else {
// No state protection
if (options.pkce) {
throw new TypeError('OAuth2Strategy requires `state: true` option when PKCE is enabled');
}
this._stateStore = new NullStore();
}You can implement custom state stores by providing an object with store and verify methods:
/**
* Custom state store interface
*/
interface CustomStateStore {
/**
* Store state data
* @param req - HTTP request object
* @param state - State to store (or verifier for PKCE)
* @param meta - OAuth request metadata
* @param callback - Completion callback
*/
store(req: any, state?: string, meta?: any, callback?: Function): void;
/**
* Verify stored state
* @param req - HTTP request object
* @param providedState - State from OAuth callback
* @param callback - Verification callback
*/
verify(req: any, providedState: string, callback: Function): void;
}Custom Redis Store Example:
const redis = require('redis');
const client = redis.createClient();
class RedisStateStore {
constructor(options) {
this.prefix = options.prefix || 'oauth2:state:';
this.ttl = options.ttl || 300; // 5 minutes
}
store(req, state, meta, callback) {
const key = this.prefix + require('uid2')(24);
const data = JSON.stringify({ state, meta, timestamp: Date.now() });
client.setex(key, this.ttl, data, (err) => {
if (err) return callback(err);
callback(null, key);
});
}
verify(req, providedState, callback) {
client.get(providedState, (err, data) => {
if (err) return callback(err);
if (!data) return callback(null, false, { message: 'Invalid state' });
client.del(providedState); // Clean up
try {
const parsed = JSON.parse(data);
callback(null, true, parsed.state);
} catch (e) {
callback(null, false, { message: 'Invalid state format' });
}
});
}
}
// Usage
const strategy = new OAuth2Strategy({
// ... other options
store: new RedisStateStore({ prefix: 'myapp:oauth:' })
}, verifyCallback);Session-based state stores require session middleware to be configured:
const session = require('express-session');
app.use(session({
secret: 'your-session-secret',
resave: false,
saveUninitialized: false,
cookie: { secure: false } // Set to true in production with HTTPS
}));Common Session Errors:
OAuth 2.0 authentication requires session support when using state: Session middleware not configuredUnable to verify authorization request state: Session data missing or corruptedInvalid authorization request state: State parameter mismatchInstall with Tessl CLI
npx tessl i tessl/npm-passport-oauth2