Repository navigation
Session Management & Security Middleware
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
This document explains session management and security middleware in PersonalLearningPro. It focuses on how authentication middleware enforces session-based access, how sessions are validated across HTTP and WebSocket connections, and how secure cookie settings are applied. It also covers session lifecycle management, automatic cleanup, CORS policy configuration, CSRF protection, and secure logout procedures. Guidance is provided for middleware chain implementation, session persistence strategies, security headers configuration, session hijacking prevention, brute force attack mitigation, and secure logout.
The server initializes Express, registers routes, attaches WebSocket servers, and configures session storage. Authentication and authorization checks are performed via middleware and route handlers. Sessions are persisted in-memory and cleaned periodically.
graph TB
A["Express App<br/>server/index.ts"] --> B["Session Middleware<br/>express-session"]
A --> C["Route Registration<br/>server/routes.ts"]
A --> D["WebSocket Servers<br/>server/chat-ws.ts"]
B --> E["MemoryStore (express-session)<br/>server/storage.ts"]
C --> F["Auth Routes (/api/auth/*)<br/>login, register, logout"]
C --> G["Protected Routes<br/>require session"]
D --> H["Session Validation (WS)<br/>server/chat-ws.ts"]
Diagram sources
- server/index.ts
- server/routes.ts
- server/storage.ts
- server/chat-ws.ts
Section sources
- server/index.ts
- server/routes.ts
- server/storage.ts
- server/chat-ws.ts
- Session configuration and cookie policy
- Secret, resave/saveUninitialized, secure flag, and maxAge are set during session initialization.
- Production requires SESSION_SECRET to be defined.
- Authentication middleware (
authenticateToken)- Verifies the server-issued JWT token (stored in the
ACCESS_COOKIEcookie orAuthorizationheader) againstJWT_SECRET(never calls Firebase Admin on the hot path). - Falls back to session-based verification if
req.session.userIdis present. - On success, sets
req.session.userId,req.session.role, and(req as any).user = { id, role, email, status, emailVerified }. - Dynamically fetches workspace information via
pgFindFirstWorkspaceMembership(userId)and injects(req as any).workspace,(req as any).workspaceRole, and(req as any).permissionscontext. - Registration validates email format before creating a user.
- Verifies the server-issued JWT token (stored in the
- Route-level session usage
- Login and registration set
req.session.userIdandreq.session.role. - Logout destroys the session.
- Login and registration set
- WebSocket session validation
- Extracts connect.sid, decodes it, and resolves userId from session store.
- Session store
- MemoryStore with periodic pruning (every 24 hours).
Section sources
- server/index.ts
- server/middleware.ts
- server/routes.ts
- server/routes.ts
- server/chat-ws.ts
- server/storage.ts
The system uses express-session with a MemoryStore for session persistence. Authentication is enforced at both HTTP and WebSocket layers. The session cookie is secure in production and lasts 24 hours.
sequenceDiagram
participant Client as "Client"
participant App as "Express App"
participant Session as "express-session"
participant Store as "MemoryStore"
participant Routes as "Routes"
participant WS as "WebSocket Server"
Client->>App : "POST /api/auth/login"
App->>Session : "Initialize session"
App->>Store : "Save {userId, role}"
App-->>Client : "{userWithoutPassword}"
Client->>App : "GET /api/users/me"
App->>Session : "Read req.session.userId"
App->>Routes : "Authenticated route"
Routes-->>Client : "{userWithoutPassword}"
Client->>WS : "Connect /ws/chat"
WS->>Session : "Resolve session by connect.sid"
Session->>Store : "Lookup session"
Store-->>WS : "{userId, role}"
WS-->>Client : "Connected"
Diagram sources
- server/index.ts
- server/routes.ts
- server/routes.ts
- server/chat-ws.ts
- server/storage.ts
- Secret: Uses SESSION_SECRET if available; otherwise falls back to a default in development.
- Secure cookie: Enabled in production.
- Max age: 24 hours.
- Store: MemoryStore instance.
- Environment requirement: SESSION_SECRET is mandatory in production.
flowchart TD
Start(["Server Start"]) --> CheckSecret["Check SESSION_SECRET in prod"]
CheckSecret --> |Missing| ThrowErr["Throw error"]
CheckSecret --> |Present| InitSession["Initialize express-session"]
InitSession --> SetCookie["Configure cookie.secure and maxAge"]
SetCookie --> BindStore["Bind MemoryStore"]
BindStore --> Ready(["Ready"])
Diagram sources
- server/index.ts
Section sources
- server/index.ts
- isAuthenticated: Blocks requests without req.session.userId.
- hasRole: Higher-order function returning middleware that checks req.session.userRole against allowed roles.
flowchart TD
Req(["Incoming Request"]) --> CheckAuth["isAuthenticated?"]
CheckAuth --> |No| Reply401["Reply 401 Not Authenticated"]
CheckAuth --> |Yes| CheckRole["hasRole(roles)?"]
CheckRole --> |No| Reply403["Reply 403 Forbidden"]
CheckRole --> |Yes| Next["Call next()"]
Diagram sources
- server/middleware.ts
Section sources
- server/middleware.ts
- POST /api/auth/register
- Validates input, checks uniqueness, creates user, sets req.session.userId and role, returns sanitized user.
- POST /api/auth/login
- Validates credentials, sets req.session.userId and role, returns sanitized user.
- POST /api/auth/logout
- Destroys session and replies success.
- GET /api/users/me
- Requires session; fetches user and returns sanitized data.
sequenceDiagram
participant Client as "Client"
participant Routes as "Routes"
participant Session as "Session"
participant Store as "MemoryStore"
Client->>Routes : "POST /api/auth/register"
Routes->>Routes : "Validate input"
Routes->>Routes : "Check uniqueness"
Routes->>Routes : "Create user"
Routes->>Session : "Set {userId, role}"
Routes-->>Client : "201 {userWithoutPassword}"
Client->>Routes : "POST /api/auth/login"
Routes->>Routes : "Verify credentials"
Routes->>Session : "Set {userId, role}"
Routes-->>Client : "200 {userWithoutPassword}"
Client->>Routes : "POST /api/auth/logout"
Routes->>Session : "Destroy session"
Routes-->>Client : "200 Logged out"
Diagram sources
- server/routes.ts
- server/routes.ts
Section sources
- server/routes.ts
- server/routes.ts
- Extracts connect.sid from the cookie header.
- Decodes the session ID and strips the signature prefix.
- Resolves session via sessionStore.get and validates presence of userId.
- On success, registers client metadata and enables rate limiting.
sequenceDiagram
participant WS as "WebSocket Server"
participant Req as "Upgrade Request"
participant Store as "MemoryStore"
WS->>Req : "Read headers.cookie"
Req-->>WS : "connect.sid=..."
WS->>WS : "Decode and strip signature"
WS->>Store : "get(sid)"
Store-->>WS : "{userId, role}"
WS-->>WS : "Validate userId"
WS-->>WS : "Register client metadata"
Diagram sources
- server/chat-ws.ts
Section sources
- server/chat-ws.ts
- MemoryStore prunes expired entries every 24 hours.
- Sessions persist for 24 hours (maxAge).
- Logout destroys the session immediately.
flowchart TD
Create["Session Created"] --> Active["Active (up to 24h)"]
Active --> Prune["Pruned by MemoryStore (daily)"]
Active --> Destroy["Logout destroys session"]
Destroy --> End(["Inactive"])
Prune --> End
Diagram sources
- server/storage.ts
- server/index.ts
- server/routes.ts
Section sources
- server/storage.ts
- server/index.ts
- server/routes.ts
- Secure cookie: Enabled in production via NODE_ENV.
- SameSite: Implicitly managed by express-session defaults.
- Domain/Path: Not explicitly configured; defaults apply.
- Recommendation: Explicitly set cookie.sameSite and cookie.domain/path for stricter controls.
Section sources
- server/index.ts
- No explicit CORS middleware is configured in the server initialization.
- If cross-origin requests are needed, configure a CORS library (e.g., cors) early in the middleware chain.
Section sources
- server/index.ts
- No CSRF middleware is currently configured.
- Recommendation: Integrate a CSRF protection library (e.g., csurf) and ensure tokens are embedded in forms and validated on the server.
Section sources
- server/index.ts
- The server implements strict JWT verification using
jsonwebtokenlibrary inauthenticateTokenmiddleware. - JWT tokens contain user identity, role, session ID, and email. Access cookies are signed with
JWT_SECRETand set asHttpOnly,Secure(in production), andSameSite=Lax.
Section sources
- server/index.ts
- Order of middleware matters:
- Body parsing (express.json, express.urlencoded)
- Session middleware (express-session)
- Logging middleware
- Route registration
- Vite/Static serving (development/production)
- Authentication middleware can be mounted globally or per-route.
Section sources
- server/index.ts
- server/index.ts
- server/index.ts
- Current: MemoryStore for in-memory sessions with daily pruning.
- Alternative strategies:
- Redis/MongoDB-backed stores for distributed deployments.
- Configure store option in session({}) with appropriate adapter.
Section sources
- server/storage.ts
- server/index.ts
- Logout endpoint calls req.session.destroy and returns success.
- Recommendation: Invalidate refresh tokens (if applicable), clear client-side tokens, and invalidate any device-specific session identifiers.
Section sources
- server/routes.ts
- Use secure cookies in production.
- Regenerate session IDs after login to prevent fixation.
- Enforce strict SameSite policies and consider adding CSRF protection.
Section sources
- server/index.ts
- server/index.ts
- Implement rate limiting at the HTTP layer for login endpoints.
- Consider account lockout or CAPTCHA after repeated failures.
- Monitor and log suspicious activity.
Section sources
- server/routes.ts
- express-session provides session management and integrates with MemoryStore.
- MemoryStore is initialized in storage.ts and bound to the session middleware.
- WebSocket servers depend on the shared session store to validate connections.
graph TB
Pkg["package.json"] --> ES["express-session"]
Pkg --> MS["memorystore"]
ES --> Sess["Session Middleware<br/>server/index.ts"]
MS --> Store["MemoryStore<br/>server/storage.ts"]
Sess --> Store
WS["WebSocket Server<br/>server/chat-ws.ts"] --> Store
Diagram sources
- package.json
- server/index.ts
- server/storage.ts
- server/chat-ws.ts
Section sources
- package.json
- server/index.ts
- server/storage.ts
- server/chat-ws.ts
- MemoryStore is suitable for development and small-scale deployments; consider Redis/MongoDB stores for production scalability.
- Periodic pruning reduces memory footprint but does not replace proper session invalidation.
- WebSocket rate limiting prevents flooding; consider adding IP-based limits and message size caps.
[No sources needed since this section provides general guidance]
- 401 Not Authenticated on protected routes
- Ensure login succeeded and req.session.userId is set.
- Verify the session cookie is being sent with the request.
- 403 Forbidden on role-restricted routes
- Confirm req.session.userRole matches allowed roles.
- WebSocket connection rejected
- Check that connect.sid is present and valid.
- Ensure session store contains the session and userId is present.
- Production error about SESSION_SECRET
- Set SESSION_SECRET in environment variables.
Section sources
- server/middleware.ts
- server/routes.ts
- server/chat-ws.ts
- server/index.ts
PersonalLearningPro uses express-session for authentication and session persistence with MemoryStore. Authentication is enforced via middleware and route handlers, while WebSocket connections validate sessions using the same store. Secure cookie settings are enabled in production, and sessions are pruned periodically. To harden the system, add CORS, CSRF protection, and consider migrating to a scalable session store. Implement rate limiting for login endpoints and enforce secure logout practices.