CRUMB a card from devarno-cloud

Airlock JWT Handoff & Session Cookies

smo1 advanced 8 min read

ELI5

When you log into SMO1, you do not actually log into SMO1 directly. Instead, you log into Airlock (a trusted identity provider), and Airlock sends a sealed letter back to SMO1 saying “this person is who they say they are.” SMO1 reads the seal, believes the letter, and gives you a temporary wristband (session cookie) so you do not have to show the letter again for the next 8 hours.

Technical Deep Dive

Login Flow

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e8f4f8', 'primaryTextColor': '#2d3748', 'primaryBorderColor': '#90cdf4', 'lineColor': '#718096', 'secondaryColor': '#f0fff4', 'tertiaryColor': '#fefcbf'}}}%%
sequenceDiagram
autonumber
actor U as User
participant M as meow-web
participant A as Airlock
participant P as purr-api
U->>M: Click "Sign In"
M->>M: Generate state + PKCE
M->>A: Redirect to /api/auth/handoff?return=...&state=...&code_challenge=...
U->>A: Authenticate (credentials / SSO)
A->>A: Mint Ed25519 JWT
A-->>M: Redirect to /api/auth/callback?token=JWT&next=/dashboard
M->>M: Verify JWT against JWKS (Ed25519)
M->>M: Validate iss, aud, exp
M->>P: POST /internal/users (with internal key)
P->>P: Upsert user record
P-->>M: User data
M->>M: Sign HMAC-SHA256 session cookie
M-->>U: Set-Cookie smo1_session - redirect to /dashboard
U->>M: GET /dashboard (with cookie)
M->>M: Verify cookie HMAC + expiry
M-->>U: Dashboard rendered

PKCE (Proof Key for Code Exchange)

The login flow uses PKCE to prevent authorization code interception:

  1. meow-web generates a random code_verifier (high-entropy string)
  2. Hashes it to produce code_challenge (SHA-256, base64url)
  3. Sends code_challenge to Airlock in the handoff URL
  4. Airlock returns the JWT directly (simplified flow for trusted first-party apps)
  5. meow-web verifies the code_verifier matches the challenge

JWT Verification (meow-web)

// Simplified pseudocode of callback handler
const jwt = await jwtVerify(token, jwks, {
issuer: 'https://airlock.devarno.cloud',
audience: 'smo1',
algorithms: ['Ed25519'],
});
const userId = jwt.payload.sub;
const email = jwt.payload.email;
  • Algorithm: Ed25519 (modern, fast, secure elliptic-curve signature)
  • JWKS endpoint: https://airlock.devarno.cloud/.well-known/jwks.json
  • Cache: 5-minute TTL to support key rotation without downtime

Format: base64url(payload).base64url(hmac)

Payload (JSON, base64url encoded):

{
"sub": "user_abc123",
"email": "user@example.com",
"iat": 1714291200,
"exp": 1714320000
}

HMAC: HMAC-SHA256(JWTSecret, base64url(payload))

Cookie attributes:

  • HttpOnly — inaccessible to JavaScript (XSS protection)
  • Secure — only sent over HTTPS
  • SameSite=Lax — sent on top-level navigations and same-site requests
  • Path=/ — valid for all routes
  • Max-Age=28800 — 8 hours (28,800 seconds)

User Sync to purr-api

After verifying the Airlock JWT, meow-web calls purr-api internally:

POST /internal/users
X-Internal-Key: {INTERNAL_API_KEY}
Content-Type: application/json
{
"id": "user_abc123",
"email": "user@example.com",
"username": "abc123",
"display_name": "Alex"
}

purr-api upserts the user record (insert if not exists, update if changed). This keeps user identity synchronized without requiring the user to register separately.

Session Verification API

meow-web exposes GET /api/auth/session for two purposes:

  1. Client-side auth checks — React components call this to know if the user is logged in
  2. purr-auth fallback — purr-api’s Session Cookie strategy forwards cookies here for validation

Logout

Two endpoints:

  • POST /api/auth/logout — clears smo1_session cookie, returns 200 (API call)
  • GET /api/auth/logout — clears cookie and redirects to landing page (browser navigation)

Both set Max-Age=0 to expire the cookie immediately.

Middleware Route Protection

meow-web edge middleware checks cookie presence:

  • Protected routes (/dashboard/*, /settings/*): redirect to /auth/login if no cookie
  • Auth routes (/auth/login): redirect to /dashboard if cookie is valid
  • Root /: redirect to /dashboard (authed) or landing page (unauthed)

Key Terms

  • Airlock → Centralized OIDC identity provider for the devarno-cloud ecosystem
  • Ed25519 → Modern elliptic-curve digital signature algorithm; faster and more secure than RSA at equivalent key sizes
  • JWKS → JSON Web Key Set; public keys for verifying JWT signatures, fetched from .well-known/jwks.json
  • PKCE → Proof Key for Code Exchange; OAuth 2.0 extension preventing authorization code interception attacks
  • HMAC-SHA256 → Symmetric message authentication code; used to sign session cookies so tampering is detectable
  • Upsert → INSERT if not exists, UPDATE if exists; keeps user data in sync without duplicate rows

Q&A

Q: Why Ed25519 instead of RS256 for Airlock? A: Ed25519 produces smaller signatures (64 bytes vs 256+ bytes), verifies faster, and has simpler, safer implementation properties than RSA. It is the modern standard for JWT signing.

Q: What happens if a user deletes their Airlock account? A: SMO1 does not independently delete the user. The next time the user tries to log in, Airlock will reject them. Operational procedures should include periodic sync of deletion events via webhook or batch job.

Q: Why 8 hours for the session cookie? A: 8 hours balances security and convenience. Short enough that a stolen cookie is time-bound; long enough that a typical workday does not require re-authentication. The Airlock JWT itself may have a shorter expiry (e.g., 1 hour), but the session cookie is independently managed.

Q: Can the session cookie be used to call purr-api directly? A: Only via the Session Cookie auth strategy, which forwards the cookie to meow-web for validation. Direct API calls from scripts should use API keys or JWT Bearer tokens instead.

Examples

Think of the login flow like checking into a hotel:

  • Airlock is the international hotel chain’s central reservation system
  • meow-web is the specific hotel front desk
  • purr-api is the hotel’s internal management system (room assignments, billing)
  • The JWT is your electronic reservation confirmation from the chain
  • The session cookie is the physical room key card the front desk gives you — it works for 8 hours (or until checkout), and if you lose it, the front desk can verify your reservation and issue a new one
  • PKCE is the secret question the chain asks you to prove you are the one who made the reservation, not someone who intercepted your confirmation email

neighbors on the map