Express.js / Intermediate: modules, identity and adapters / Lesson 9
Principals, cookies and session lifecycles
Separate authentication evidence from cookie transport and enforce object policy throughout a session.
On this page
Lesson goals
- Explain what a session identifier, cookie attribute and principal each establish.
- Plan rotation, expiry, logout and shared-store behavior without using an in-memory production shortcut.
A cookie is a transport mechanism, not proof by itself
An incoming Cookie header is caller input. Authentication validates the credential or session through the chosen protocol and constructs a principal with a defined lifetime. Authorization then decides which objects and actions that principal may access. Express itself does not provide a complete login system. A session middleware can associate a browserโs identifier with server-side state, but it does not invent password verification, account recovery or tenant policy. Keep those decisions in reviewed components and avoid interpreting any cookie called userId as a trusted identity merely because a browser sent it.
Describe cookie policy with its deployment assumptions
The illustrative cookie options mark a session identifier HttpOnly, Secure and SameSite Lax with a narrow path. HttpOnly restricts script access to the cookie but does not prevent all script-driven actions or CSRF. Secure concerns secure transport; a reverse-proxy deployment must correctly represent the original connection through its reviewed trust policy. SameSite behavior depends on browser site semantics and the actual cross-site workflow. Values such as lifetime and path need application-specific choices. The JSON is a design record, not a working session configuration or a secret-bearing cookie.
{"cookie":{"httpOnly":true,"secure":true,"sameSite":"lax","path":"/"},"sessionId":"not-contained-in-this-record","store":"reviewed-shared-store-required"}
The record contains policy labels only. It sets no browser cookie, signs nothing and provisions no session store.
Session state needs expiry, rotation and store ownership
express-session stores session data server-side while the cookie carries an identifier. Its default MemoryStore is not intended for production use and does not provide a suitable shared durable design across workers. Select a supported store with clear expiry, failure and concurrency semantics. Regenerate or rotate session identity after authentication and privilege changes according to the protocol to address fixation risks. Logout must retire server-side authority and clear the matching cookie attributes. Deleting a browser cookie alone may leave a stolen identifier valid at the server until expiry or revocation.
Test expiry and revocation as separate conditions
The local predicate accepts a synthetic session only when its expiry lies strictly in the future and it has not been revoked. At the exact expiry instant it is invalid. This is a domain model, not credential verification or a cryptographic session implementation. It intentionally demonstrates a boundary case that a UI timer cannot enforce securely on its own. The server must evaluate current eligibility, including account and tenant changes required by policy. Do not keep a copied role in session state forever if revocation-sensitive decisions require a fresher source.
function activeSession(session,now) {
return !session.revoked && now < session.expiresAt;
}
console.assert(activeSession({expiresAt:1000,revoked:false},999));
console.assert(!activeSession({expiresAt:1000,revoked:false},1000));
console.assert(!activeSession({expiresAt:1000,revoked:true},999));
The first case passes; expiry and explicit revocation each deny independently. No cookie or session middleware is executed.
Keep session identity and object scope aligned
A signed-in user is not automatically eligible for every course or tenant. Build a current principal and pass it into shared domain policy for reads and writes. Protect session identifiers from logs, URLs and broad client storage. Cache data under the correct principal scope, and retire cached state and pending responses when identity changes. Test store outages under a policy that fails safely rather than silently converting an unverified session into a privileged guest. Record the chosen middleware and store versions; cookie and session examples are implementation contracts, not portable defaults across every authentication architecture.
Common errors and fixes
Any userId cookie becomes an authenticated principal
- Why it happens
- Unverified transport input was trusted as identity.
- How to fix it
- Validate the session or credential through the reviewed authentication protocol.
- Prevent it next time
- Test arbitrary cookie values and expired authority.
Logout leaves a stolen session usable
- Why it happens
- Only the local cookie was cleared.
- How to fix it
- Retire server-side authority and clear the matching cookie.
- Prevent it next time
- Verify revocation separately from browser removal.
Workers see inconsistent login state
- Why it happens
- An unsuitable process-local store was treated as shared infrastructure.
- How to fix it
- Choose a reviewed store and explicit failure/expiry semantics.
- Prevent it next time
- Test multi-instance assumptions in authorized integration.
Check your understanding
1. Does HttpOnly eliminate CSRF?
Show worked solution
No. It prevents ordinary script reads of the cookie, but browsers can still attach it to eligible requests and scripts can initiate actions. Use a reviewed CSRF and origin policy for the actual credential architecture. Cookie attributes, authentication and authorization each address different boundaries and should not be collapsed into one setting.
2. Why test the exact expiry instant rather than only before and long after?
Show worked solution
It establishes whether the deadline is inclusive or exclusive and prevents an off-by-one validity gap. The model uses now < expiresAt, so equality denies. A real session integration still needs a trustworthy time policy, store cleanup and revocation behavior; deleting expired storage is not the same as deciding that a present record is currently valid.
Key takeaways
- Cookie transport, session authority and object authorization are separate contracts.
- Rotation, expiry and revocation require server-side lifecycle decisions.