Session Management

**Referenced Files in This Document** - [worker.js](file://worker.js) - [alliance-login.njk](file://src/alliance-login.njk) - [alliance-members.njk](file://src/alliance-members.njk) - [iaa-base.njk](file://src/_includes/layouts/iaa-base.njk) - [wrangler.jsonc](file://wrangler.jsonc) - [cloudflare-pages.toml](file://cloudflare-pages.toml) - [netlify.toml](file://netlify.toml) - [package.json](file://package.json)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion
  10. Appendices

Introduction

This document explains the session management system for the member authentication portal. It covers how session tokens are structured and encoded, how HMAC signatures are generated and verified, how cookies are set with HttpOnly, Secure, and SameSite attributes, and how the session lifecycle operates from initial authentication through 30-day validity and automatic logout. It also documents the integration between session cookies and member-only content access, including the handleMembersRoute function and unauthorized redirect behavior. Practical guidance is included for debugging sessions, handling expired sessions, and implementing renewal strategies.

Project Structure

The authentication and session logic is implemented in a Cloudflare Worker that serves as a reverse proxy for member-only routes and static assets. The frontend templates define the login and members pages, while configuration files manage secrets and KV namespaces.

graph TB
subgraph "Frontend Templates"
A["alliance-login.njk"]
B["alliance-members.njk"]
C["iaa-base.njk"]
end
subgraph "Worker"
W["worker.js"]
end
subgraph "Configuration"
D["wrangler.jsonc"]
E["cloudflare-pages.toml"]
F["netlify.toml"]
G["package.json"]
end
A --> W
B --> W
C --> A
C --> B
W --> D
W --> E
W --> F
W --> G

Diagram sources

  • [worker.js:1-321](file://worker.js#L1-L321)
  • [alliance-login.njk:1-73](file://src/alliance-login.njk#L1-L73)
  • [alliance-members.njk:1-58](file://src/alliance-members.njk#L1-L58)
  • [iaa-base.njk:1-99](file://src/_includes/layouts/iaa-base.njk#L1-L99)
  • [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
  • [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
  • [netlify.toml:1-26](file://netlify.toml#L1-L26)
  • [package.json:1-32](file://package.json#L1-L32)

Section sources

  • [worker.js:1-321](file://worker.js#L1-L321)
  • [alliance-login.njk:1-73](file://src/alliance-login.njk#L1-L73)
  • [alliance-members.njk:1-58](file://src/alliance-members.njk#L1-L58)
  • [iaa-base.njk:1-99](file://src/_includes/layouts/iaa-base.njk#L1-L99)
  • [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
  • [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
  • [netlify.toml:1-26](file://netlify.toml#L1-L26)
  • [package.json:1-32](file://package.json#L1-L32)

Core Components

  • Session token structure and encoding:
    • Payload format: email concatenated with an expiration timestamp separated by a pipe character.
    • Base64 encoding of the payload portion.
    • HMAC-SHA-256 signature of the payload, hex-encoded.
    • Token format: base64(payload)|hex(HMAC-SHA-256(payload, secret)).
  • HMAC signature generation and verification:
    • Signatures are computed using WebCrypto HMAC with SHA-256.
    • Verification includes decoding base64 payload, parsing email and expiry, checking expiration, recomputing the HMAC, and performing a constant-time comparison to prevent timing attacks.
  • Cookie management:
    • Name: ace_member_session.
    • Attributes: Path=/, HttpOnly, Secure, SameSite=Lax, Max-Age=2592000 seconds (30 days).
    • Cross-origin restrictions: SameSite=Lax allows cross-site contexts to send cookies with top-level navigations.
  • Session lifecycle:
    • Initial authentication: user submits email on the login page; a one-time magic link is issued and emailed.
    • Magic link verification: clicking the link validates the token against KV, deletes it, and issues a session cookie.
    • Access to members area: requests to /alliance/members/* are gated by session verification.
    • Automatic logout: GET /alliance/logout clears the session cookie.
    • Expiration handling: verification rejects expired tokens; users are redirected to login with a next parameter preserved.

Section sources

  • [worker.js:12-58](file://worker.js#L12-L58)
  • [worker.js:163-176](file://worker.js#L163-L176)
  • [worker.js:81-91](file://worker.js#L81-L91)
  • [worker.js:282-295](file://worker.js#L282-L295)

Architecture Overview

The system uses a Cloudflare Worker to intercept member-only routes and enforce session-based access. Static assets are served via the Worker’s assets binding. Secrets and KV namespaces are configured in wrangler.jsonc.

sequenceDiagram
participant U as "User Agent"
participant L as "Login Page<br/>alliance-login.njk"
participant W as "Worker<br/>worker.js"
participant K1 as "KV : MEMBER_EMAILS"
participant K2 as "KV : MAGIC_TOKENS"
participant R as "Resend API"
U->>L : "GET /alliance/login/"
U->>W : "POST /alliance/login/ {email}"
W->>K1 : "Lookup member : <email>"
K1-->>W : "Approved or not"
W->>K2 : "Put token : <hex>=<email>, TTL=900"
W->>R : "Send magic link email"
R-->>W : "OK"
W-->>U : "Redirect /alliance/login/?sent=1"

Diagram sources

  • [worker.js:97-147](file://worker.js#L97-L147)
  • [alliance-login.njk:1-73](file://src/alliance-login.njk#L1-L73)
  • [wrangler.jsonc:17-26](file://wrangler.jsonc#L17-L26)

Detailed Component Analysis

Session Token Structure and Encoding

  • Payload composition: email concatenated with an expiration timestamp using a pipe separator.
  • Base64 encoding: the payload is base64-encoded to produce the first part of the token.
  • Signature generation: HMAC-SHA-256 over the payload using the SESSION_SECRET, hex-encoded.
  • Token format: base64(payload).hex(HMAC-SHA-256(payload, secret)).
flowchart TD
Start(["Create Session Token"]) --> BuildPayload["Build payload: email|expiry"]
BuildPayload --> EncodeB64["Base64 encode payload"]
EncodeB64 --> Sign["Compute HMAC-SHA-256(payload, secret)"]
Sign --> HexSig["Hex-encode signature"]
HexSig --> Compose["Compose token: base64(payload).signature"]
Compose --> End(["Return token"])

Diagram sources

  • [worker.js:32-37](file://worker.js#L32-L37)

Section sources

  • [worker.js:32-37](file://worker.js#L32-L37)

Session Verification and Timing Attack Mitigation

  • Decoding: split token by the dot separator; base64-decode the payload portion.
  • Parsing: extract email and expiry from the payload; reject if missing or invalid.
  • Expiration check: compare current Unix time to expiry; reject if expired.
  • Recompute signature: compute HMAC-SHA-256 over the payload using the SESSION_SECRET.
  • Constant-time comparison: XOR each character code of the provided signature against the expected signature and accumulate differences; return true only if difference equals zero. This prevents timing attacks.
flowchart TD
VStart(["Verify Session Token"]) --> Split["Split token by '.'"]
Split --> Decode["Decode base64 payload"]
Decode --> Parse["Parse email and expiry"]
Parse --> Expired{"Expired?"}
Expired --> |Yes| VFail["Reject"]
Expired --> |No| Sign["Recompute HMAC-SHA-256(payload, secret)"]
Sign --> Compare["Constant-time compare sigs"]
Compare --> Match{"Equal?"}
Match --> |Yes| VPass["Accept and return email"]
Match --> |No| VFail

Diagram sources

  • [worker.js:39-58](file://worker.js#L39-L58)

Section sources

  • [worker.js:39-58](file://worker.js#L39-L58)

Cookie Management and Attributes

  • Cookie name: ace_member_session.
  • Attributes:
    • Path=/: applies cookie to entire site.
    • HttpOnly: prevents client-side script access.
    • Secure: ensures transmission over HTTPS.
    • SameSite=Lax: allows cross-site top-level navigations to send the cookie.
    • Max-Age=2592000: 30-day validity.
  • Logout cookie: identical attributes with Max-Age=0 to expire immediately.
flowchart TD
CStart(["Issue Session Cookie"]) --> Build["Build Set-Cookie header"]
Build --> Attrs["Add attributes: Path=/, HttpOnly, Secure, SameSite=Lax, Max-Age=2592000"]
Attrs --> Send["Set-Cookie: ace_member_session=<token>"]
Send --> CEnd(["Cookie Active"])
LStart(["Logout"]) --> LAttrs["Set attributes: Max-Age=0"]
LAttrs --> LSend["Set-Cookie: ace_member_session= (expired)"]
LSend --> LEnd(["Cookie Removed"])

Diagram sources

  • [worker.js:163-176](file://worker.js#L163-L176)
  • [worker.js:282-295](file://worker.js#L282-L295)

Section sources

  • [worker.js:163-176](file://worker.js#L163-L176)
  • [worker.js:282-295](file://worker.js#L282-L295)

Session Lifecycle: From Authentication to Access and Logout

  • Initial authentication:
    • User enters email on the login page.
    • Worker validates the email and checks membership in KV.
    • If approved, a random token is generated, stored in MAGIC_TOKENS with a 15-minute TTL, and an email is sent containing a verification link.
  • Magic link verification:
    • Clicking the link invokes the verify endpoint.
    • The token is validated against MAGIC_TOKENS; if present, it is deleted and a session cookie is issued.
  • Access to members area:
    • Requests to /alliance/members/* are intercepted by the Worker.
    • The session cookie is extracted and verified; if valid, assets are served; otherwise, the user is redirected to login with the original path preserved.
  • Logout:
    • GET /alliance/logout clears the session cookie and redirects to the login page.
sequenceDiagram
participant U as "User Agent"
participant W as "Worker"
participant K1 as "KV : MEMBER_EMAILS"
participant K2 as "KV : MAGIC_TOKENS"
U->>W : "POST /alliance/login/ {email}"
W->>K1 : "Get member : <email>"
K1-->>W : "Approved or not"
W->>K2 : "Put token : <hex>=<email>, TTL=900"
W-->>U : "Redirect /alliance/login/?sent=1"
U->>W : "GET /alliance/verify/?token=<hex>"
W->>K2 : "Get token : <hex>"
K2-->>W : "email or null"
W->>K2 : "Delete token : <hex>"
W-->>U : "302 to /alliance/members/ with Set-Cookie"
U->>W : "GET /alliance/members/*"
W->>W : "Extract and verify session cookie"
alt Valid
W-->>U : "Serve assets"
else Invalid or Missing
W-->>U : "302 to /alliance/login/?next=/alliance/members/*"
end
U->>W : "GET /alliance/logout/"
W-->>U : "302 to /alliance/login/ with expired cookie"

Diagram sources

  • [worker.js:97-147](file://worker.js#L97-L147)
  • [worker.js:153-177](file://worker.js#L153-L177)
  • [worker.js:81-91](file://worker.js#L81-L91)
  • [worker.js:282-295](file://worker.js#L282-L295)

Section sources

  • [worker.js:97-147](file://worker.js#L97-L147)
  • [worker.js:153-177](file://worker.js#L153-L177)
  • [worker.js:81-91](file://worker.js#L81-L91)
  • [worker.js:282-295](file://worker.js#L282-L295)

Integration Between Session Cookies and Member-Only Content Access

  • The handleMembersRoute function extracts the session cookie, verifies it, and either serves assets or redirects to the login page with the requested path preserved.
  • Unauthorized access attempts are redirected to the login page with a next parameter so users can return seamlessly after authenticating.
flowchart TD
MRStart(["handleMembersRoute"]) --> GetCookie["Extract ace_member_session"]
GetCookie --> Verify["verifySessionToken(secret)"]
Verify --> Valid{"Valid?"}
Valid --> |Yes| Serve["env.ASSETS.fetch(request)"]
Valid --> |No| Redirect["Redirect to /alliance/login/?next=<requested_path>"]

Diagram sources

  • [worker.js:81-91](file://worker.js#L81-L91)

Section sources

  • [worker.js:81-91](file://worker.js#L81-L91)

Frontend Integration and UX Behaviors

  • Login page:
    • Presents an email input and a “Send Login Link” button.
    • Shows contextual messages for expired or invalid links and displays a “check your inbox” confirmation after submission.
  • Members portal:
    • Provides access to exclusive content and a sign-out link.
  • Base layout:
    • Supplies the shared HTML shell for both login and members pages.

Section sources

  • [alliance-login.njk:1-73](file://src/alliance-login.njk#L1-L73)
  • [alliance-members.njk:1-58](file://src/alliance-members.njk#L1-L58)
  • [iaa-base.njk:1-99](file://src/_includes/layouts/iaa-base.njk#L1-L99)

Dependency Analysis

  • Secrets and KV namespaces:
    • SESSION_SECRET: used to sign session tokens.
    • MEMBER_EMAILS: KV namespace storing approved member emails.
    • MAGIC_TOKENS: KV namespace storing one-time magic link tokens with TTL.
  • Worker routing:
    • Member-only routes: /alliance/members/* are gated.
    • Authentication routes: POST /alliance/login/, GET /alliance/verify/.
    • Logout: GET /alliance/logout/.
  • Static asset serving:
    • Assets are served via the ASSETS binding configured in wrangler.jsonc.
  • Deployment and configuration:
    • wrangler.jsonc defines secrets and KV bindings.
    • cloudflare-pages.toml is retained for legacy reference.
    • netlify.toml defines security headers for static assets.
graph LR
Secret["SESSION_SECRET"] --> HMAC["HMAC-SHA-256"]
MemberKV["MEMBER_EMAILS KV"] --> Login["/alliance/login/"]
MagicKV["MAGIC_TOKENS KV"] --> Verify["/alliance/verify/"]
HMAC --> Token["Session Token"]
Token --> MembersRoute["/alliance/members/*"]
Assets["ASSETS binding"] --> MembersRoute

Diagram sources

  • [worker.js:12-14](file://worker.js#L12-L14)
  • [worker.js:97-147](file://worker.js#L97-L147)
  • [worker.js:153-177](file://worker.js#L153-L177)
  • [worker.js:81-91](file://worker.js#L81-L91)
  • [wrangler.jsonc:17-26](file://wrangler.jsonc#L17-L26)

Section sources

  • [worker.js:12-14](file://worker.js#L12-L14)
  • [worker.js:97-147](file://worker.js#L97-L147)
  • [worker.js:153-177](file://worker.js#L153-L177)
  • [worker.js:81-91](file://worker.js#L81-L91)
  • [wrangler.jsonc:17-26](file://wrangler.jsonc#L17-L26)

Performance Considerations

  • Cryptographic operations:
    • HMAC signing and verification occur synchronously in the Worker; keep SESSION_SECRET strong and avoid excessive re-signing.
  • KV latency:
    • Membership and magic link lookups are O(1) with TTL-based invalidation; ensure KV namespaces are provisioned and bound correctly.
  • Cookie size:
    • Session tokens are small (base64 payload plus hex signature), minimizing overhead.
  • Asset delivery:
    • Static assets are served via the Worker’s assets binding; ensure caching headers are appropriate for your CDN and deployment model.

[No sources needed since this section provides general guidance]

Troubleshooting Guide

Common issues and resolutions:

  • Session cookie not set or rejected:
    • Verify that the cookie includes HttpOnly, Secure, SameSite=Lax, and Max-Age=2592000.
    • Confirm that the token format is base64(payload).signature and that the payload contains email|expiry.
    • Ensure SESSION_SECRET is configured and matches the Worker environment.
  • Expiration handling:
    • If verification fails, the Worker rejects the token and redirects to login with an appropriate error parameter.
    • Check that the server time is synchronized; expired tokens are rejected when current time exceeds expiry.
  • Unauthorized redirect behavior:
    • When accessing /alliance/members/* without a valid session, the Worker redirects to login with the requested path preserved via the next parameter.
  • KV configuration:
    • If MEMBER_EMAILS or MAGIC_TOKENS are not configured, the Worker returns a 503 response indicating missing KV namespaces.
    • Follow wrangler.jsonc instructions to create and bind KV namespaces and set secrets.

Practical debugging steps:

  • Inspect browser cookies for ace_member_session and confirm attributes.
  • Use browser developer tools to observe network requests and Set-Cookie headers during login and verification.
  • Temporarily log verification outcomes in the Worker for development builds (not recommended for production).
  • Validate token structure and signature using the documented format and HMAC-SHA-256 with the configured secret.

Section sources

  • [worker.js:81-91](file://worker.js#L81-L91)
  • [worker.js:153-177](file://worker.js#L153-L177)
  • [worker.js:70-75](file://worker.js#L70-L75)
  • [wrangler.jsonc:17-26](file://wrangler.jsonc#L17-L26)

Conclusion

The session management system employs a compact, secure token format with HMAC-SHA-256 signatures, constant-time verification, and robust cookie attributes. The Worker enforces access to member-only content, handles magic-link-based authentication, and provides a seamless logout mechanism. By adhering to the documented configuration and operational behaviors, administrators can maintain a secure and reliable member portal.

[No sources needed since this section summarizes without analyzing specific files]

Appendices

Appendix A: Configuration Checklist

  • Secrets:
    • SESSION_SECRET: set via wrangler secret put.
    • RESEND_API_KEY: for sending magic link emails.
  • KV namespaces:
    • MEMBER_EMAILS: key format member:, value 1.
    • MAGIC_TOKENS: key format token:, value , TTL 900 seconds.
  • Deployment:
    • Bind KV namespaces and secrets in wrangler.jsonc.
    • Build and deploy using npm scripts and wrangler.

Section sources

  • [wrangler.jsonc:28-34](file://wrangler.jsonc#L28-L34)
  • [package.json:11-12](file://package.json#L11-L12)