Password Change Token Invalidation
Status: ❌ Not Implemented Priority: 🟡 MEDIUM Estimated Time: 12-16 hours Risk Level: MEDIUM Impact: Security event detection and session invalidation
Problem Statement
When users change their password at the Identity Provider (IdP), existing access and refresh tokens remain valid until they naturally expire. This creates a security gap where compromised credentials continue to work even after the user has taken remediation action.
Current Behavior:
- User changes password at IdP
- Existing access/refresh tokens stored in Seed remain valid
- Sessions continue to work until token TTL expires (default: 1 hour access, 24 hours refresh)
- No mechanism to detect or respond to security events
Security Impact:
- Stolen tokens remain valid after password change
- Account compromise window extends beyond password reset
- Users cannot force-logout all sessions after security incident
- No real-time response to IdP security events
Current Mitigation
Seed currently uses short-lived tokens as a partial mitigation:
- Access tokens expire after 1 hour (configurable via
TOKEN_TTL_SECONDS) - Automatic token refresh requires valid refresh token
- Session TTL limits maximum session lifetime (24 hours default)
This reduces the exposure window but doesn't eliminate it.
Proposed Solutions
Option 1: Periodic Token Validation (Recommended for MVP)
Approach: Periodically validate stored tokens against IdP token introspection endpoint.
Implementation:
// src/services/token-validation.ts
import { config } from "../config/index.js";
import { getTokenStore } from "./token-store.js";
import { logger } from "./logger.js";
/**
* Validate token with IdP introspection endpoint
*/
async function introspectToken(accessToken: string): Promise<boolean> {
const response = await fetch(config.oidc.introspectionUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
token: accessToken,
client_id: config.oidc.clientId,
client_secret: config.oidc.clientSecret,
}),
});
const data = await response.json();
return data.active === true;
}
/**
* Background job to validate stored tokens
*/
export async function validateStoredTokens(): Promise<void> {
const tokenStore = getTokenStore();
const allTokens = await tokenStore.getAllUsers();
for (const userId of allTokens) {
const tokens = await tokenStore.get(userId);
if (!tokens) continue;
const isValid = await introspectToken(tokens.access_token);
if (!isValid) {
logger.warn("Token invalidated by IdP, removing from store", { userId });
await tokenStore.delete(userId);
}
}
}
// Run validation every 5 minutes
setInterval(validateStoredTokens, 5 * 60 * 1000);Pros:
- Works with any RFC 7662 compliant IdP
- No changes required to IdP configuration
- Gradual detection of invalid tokens
Cons:
- 5-minute lag between password change and detection
- Adds load to IdP introspection endpoint
- Requires IdP to support token introspection
Estimated Effort: 6-8 hours
Option 2: Webhook Integration (Complete Solution)
Approach: Receive real-time security events from IdP via webhooks.
Implementation:
// src/routes/idp-webhook.ts
import { Router, type Request, type Response } from "express";
import { getTokenStore } from "../services/token-store.js";
import { removeAllUserSessions } from "../mcp/mcp.js";
import { logger } from "../services/logger.js";
export const idpWebhookRouter = Router();
interface SecurityEvent {
event_type: "password_change" | "account_locked" | "mfa_reset";
user_id: string;
timestamp: string;
}
/**
* IdP Security Event Webhook
* POST /idp/events
*/
idpWebhookRouter.post("/events", async (req: Request, res: Response) => {
// Validate webhook signature (IdP-specific)
const signature = req.headers["x-idp-signature"] as string;
if (!verifyWebhookSignature(req.body, signature)) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = req.body as SecurityEvent;
logger.info("Received IdP security event", {
eventType: event.event_type,
userId: event.user_id,
});
// Invalidate all tokens and sessions for user
const tokenStore = getTokenStore();
await tokenStore.delete(event.user_id);
await removeAllUserSessions(event.user_id);
logger.warn("Invalidated all tokens and sessions due to security event", {
eventType: event.event_type,
userId: event.user_id,
});
res.status(200).json({ status: "processed" });
});Pros:
- Real-time invalidation (< 1 second)
- No polling overhead
- Comprehensive security event handling
Cons:
- Requires IdP webhook support
- Additional endpoint to secure
- IdP-specific webhook format
Estimated Effort: 12-16 hours (includes IdP configuration, signature validation, testing)
Option 3: Token Introspection on Each Request (Not Recommended)
Approach: Validate every token with IdP on each authenticated request.
Pros:
- Immediate detection of invalid tokens
Cons:
- High latency impact (adds 50-200ms per request)
- Significant load on IdP
- Defeats purpose of JWT validation
- May violate IdP rate limits
Estimated Effort: 4-6 hours
Recommendation: ❌ Not recommended due to performance impact
Recommended Implementation Path
Phase 1: Documentation (Immediate)
- Document current behavior as "acceptable risk" with short-lived tokens
- Add security best practices guide for token TTL configuration
- Recommend IdP-side session management
Phase 2: Token Introspection Endpoint (6-8 hours)
- Implement RFC 7662 token introspection endpoint
- Add periodic validation background job (5-minute interval)
- Monitor validation failures and invalidation rate
Phase 3: Webhook Support (12-16 hours, when IdP supports it)
- Add IdP webhook endpoint for security events
- Implement signature validation
- Add comprehensive event handling (password change, account lock, MFA reset)
Configuration
# Token validation settings
TOKEN_VALIDATION_ENABLED=true
TOKEN_VALIDATION_INTERVAL_MS=300000 # 5 minutes
# IdP introspection endpoint
OIDC_INTROSPECTION_URL=https://idp.example.com/oauth/introspect
# Webhook settings (Phase 3)
IDP_WEBHOOK_ENABLED=false
IDP_WEBHOOK_SECRET=your-webhook-secretAcceptance Criteria
Phase 1 (Documentation):
- [ ] Document current behavior in security guide
- [ ] Add token TTL configuration best practices
- [ ] Clarify acceptable risk window
Phase 2 (Token Introspection):
- [ ] Implement RFC 7662 introspection endpoint
- [ ] Add background validation job
- [ ] Prometheus metrics for validation results
- [ ] Test with IdP token revocation
- [ ] Handle validation failures gracefully
Phase 3 (Webhooks):
- [ ] Add webhook endpoint at
/idp/events - [ ] Implement signature verification
- [ ] Handle password change events
- [ ] Handle account lock events
- [ ] Comprehensive test coverage
- [ ] Documentation for IdP configuration
Related Enhancements
- OAuth Token Introspection - RFC 7662 introspection endpoint
- Session Management API - User-facing session control
- Token Revocation - Manual token revocation