Skip to content

Add Okta authentication for /intake/* (worker-enforced, testing) #16

Description

@lamontacrook

Goal

Add Okta authentication to gate /intake/* for testing, enforced server-side in the Cloudflare Worker (workers/website/). Uses the existing confidential Okta Web app (client ID + secret) via the OIDC Authorization Code flow.

Why the worker (and not the .aem.page URL)

The originally requested URL stage--drago-toolkit--adobedevxsc.aem.page/intake/form is on Adobe's hostname and cannot be fronted by Cloudflare, so the worker can't intercept it. A confidential client (secret) also can't be used safely in browser-only code. Therefore auth is enforced in the worker, which is pointed at the stage AEM branch and tested via wrangler dev (http://localhost:8787) and/or a *.workers.dev deploy.

Trade-off accepted: testing happens through the worker host, not the literal .aem.page stage URL.

Scope / behavior

  • Gate all of /intake/*; everything else stays public.
  • Any authenticated Okta user is allowed (no group/email restriction).
  • For testing, point the worker origin at stage: AEM_ORG=adobedevxsc, AEM_SITE=drago-toolkit, AEM_BRANCH=stage.

Implementation

  1. workers/website/handlers/auth.js (new) — OIDC Authorization Code flow with Web Crypto (no heavy deps):
    • requireAuth() — allow non-/intake/ and /auth/* paths; allow valid session; else 302 to Okta /v1/authorize with client_id, redirect_uri=<origin>/auth/callback, response_type=code, scope=openid profile email, random state + nonce (short-lived signed cookie carrying original path).
    • handleCallback() — validate state, exchange code at /v1/token using client_id + client_secret (server-side), verify id_token (RS256 via cached JWKS, iss/aud/exp/nonce), set signed session cookie, redirect to saved path.
    • handleLogout() — clear session cookie.
    • Session cookie: HMAC-signed (SESSION_SECRET), HttpOnly; Secure; SameSite=Lax, ~1h TTL.
  2. workers/website/index.js — handle /auth/callback + /auth/logout, then call requireAuth() before the existing ROUTES dispatch.
  3. workers/website/index.js (formatRequest) — add AEM_BRANCH var (default main) so origin = ${AEM_BRANCH}--${AEM_SITE}--${AEM_ORG}.aem.live.
  4. Secrets/config
    • workers/website/.dev.vars (new, gitignored): OKTA_ISSUER, OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, SESSION_SECRET.
    • wrangler.toml: add AEM_BRANCH; document Okta values + SESSION_SECRET set via wrangler secret put for deployed envs.
    • .gitignore: add .dev.vars and .env.

Okta app configuration (manual)

  • Sign-in redirect URI: http://localhost:8787/auth/callback (+ *.workers.dev callback if deployed).
  • Sign-out redirect URI: http://localhost:8787/.
  • Grant type: Authorization Code. Assignment: everyone in the org.

Test plan

  • cd workers/website && npx wrangler dev
  • http://localhost:8787/intake/form → redirect to Okta → log in → returns to form. /intake/* all gated.
  • http://localhost:8787/ and other paths → public, no redirect.
  • Verify: session cookie HttpOnly/Secure/SameSite=Lax; logout clears it; state/nonce mismatch rejected.

Security notes

CSRF via state, replay protection via nonce, full id_token validation, signed short-lived session cookie, secret only in worker env, no token logging.

Blocking prerequisite

  • Okta org domain / issuer URL still needed (e.g. https://<org>.okta.com or https://<org>.okta.com/oauth2/default). Client ID, client secret, and kid are on hand; the issuer is required to resolve the /authorize, /token, and JWKS endpoints.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions