Skip to content

feat: defer most session/ban authority to kf-auth#3627

Merged
tefkah merged 13 commits into
mainfrom
tfk/sync-bans
Jun 16, 2026
Merged

feat: defer most session/ban authority to kf-auth#3627
tefkah merged 13 commits into
mainfrom
tfk/sync-bans

Conversation

@tefkah

@tefkah tefkah commented Jun 1, 2026

Copy link
Copy Markdown
Member

Issue(s) Resolved

Currently you can ban a user or revoke their in KF-Auth, but this still keeps people signed into pubpub. This shouldnt happen. This PR lets kf-auth act as the session authority for PubPub, making sessions in PubPub linked to sessions in kf-auth, allowing targeted session revocation sync, allowing bans from kf-auth to affect pubpub and vice-versa, and making sessions much shorter on PubPub requiring occasional silent round trips to kfauth to confirm the session is still valid, which creates eventual consistency between PubPub session state and KF Auth sessions state

This implements the slightly different webhooks tructure from (https://github.com/knowledgefutures/kf-auth/pull/12)

Listens on /api/kf/webhooks rather than /api/kf/profile-sync

See https://github.com/knowledgefutures/kf-auth/pull/12

Explanation of the general idea

explanation_comp.mp4

Demo of the functionality

dem_comp.mp4
Robo summary

Move session & ban management to kf-auth (OIDC) with silent re-auth

What & why

PubPub now delegates identity, sessions, and bans to kf-auth
over OIDC. Previously
PubPub sessions lived 30 days and a ban relied on a single
fire-and-forget webhook —
if it failed, a banned/return user kept a valid session for up
to a month.

This PR makes kf-auth the source of truth and turns PubPub
sessions into short-lived
caches, refreshed transparently via OIDC prompt=none. Bans are
enforced through
several independent layers so no single failure leaves a session
alive.

Changes

Auth flow (server/kf/api.ts, server/kf/oidc.server.ts)

  • /auth/login/auth/callback/auth/session-set OIDC
    flow with PKCE.
  • Silent re-auth: expired sessions for previously-logged-in
    users are renewed in the
    background via prompt=none (buildAuthorizeUrl(..., 'none')), no login page.
  • Identity pinning: a renewal must come back as the same
    account the expired session
    belonged to (compared via the pp-lic hash carried in
    encrypted state) — switching
    accounts must be a deliberate interactive choice, never a side
    effect of renewal.
  • Cross-domain: platform subdomains get a session directly;
    custom domains receive a
    one-time encrypted token and establish the session via
    /auth/session-set.
  • kfSessionId (the ID token sid) is stamped on each session
    so a single revoked
    kf-auth session can be targeted precisely.

Session hardening (server/server.ts,
server/envSchema.ts)

  • SESSION_SECRET from env (was a hardcoded literal);
    httpOnly: true; secure in prod.
  • proxy: true so the Secure cookie is emitted behind Fastly
    without the global
    trust proxy side effects that would break
    req.hostname-based community routing.
  • Short-lived sessions (15m prod) instead of 30 days.
  • Cross-subdomain cookie domain chosen from the deployment env
    (.pubpub.org /
    .duqduq.org), not the edge-rewritten hostname.
  • silentReauthMiddleware registered before the route handlers.

Ban enforcement (server/kf/webhookHandlers.ts,
server/spamTag/userQueries.ts,
server/utils/session.ts)

  • Webhooks from kf-auth: user.updated,
    user.banned/unbanned (→ SpamTag),
    user.sessions-revoked and session.revoked (→ delete
    matching local sessions).
  • Outbound ban/unban sync from PubPub's spam system to kf-auth.

Redirect-loop fixes (staging)

Rolling this out on duqduq surfaced an infinite silent-reauth
loop. Root causes, all fixed:

  1. Circuit breaker not set on failure paths
    /auth/callback and /auth/session-set
    returned 500 on a renewal without tripping pp-renew-failed,
    so any transient failure
    re-looped. Now every renewal failure path uses failRenew()
    (sets the 1h breaker, 302s
    back anonymously, never 500s).
  2. Fastly retries 500s on GET, re-redeeming the single-use
    OIDC code → invalid_grant.
    Fixed by (1) — renewals no longer 500. (A matching
    vcl_fetch guard for ^/auth/ is
    recommended as defense-in-depth; see infra.)
  3. Fastly cached the reauth 302 keyed by pp-lic (the
    session cookie isn't in the page
    cache key), replaying "go reauth" after a successful login.
    silentReauthMiddleware now
    sends Cache-Control: private, no-store.
  4. Secure cookie dropped behind the edge — a small
    middleware maps Fastly's
    Fastly-SSL header to X-Forwarded-Proto: https before the
    session/SSL middleware.

Test Plan

Screenshots (if applicable)

Optional

Notes/Context/Gotchas

Supporting Docs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates PubPub’s KF-Auth integration to keep PubPub sessions aligned with KF-Auth bans/session revocations by switching to a new webhook endpoint and adding session correlation (via sidkfSessionId) plus middleware to re-authenticate or block banned users.

Changes:

  • Replace /api/kf/profile-sync with /api/kf/webhooks and add handlers for profile updates, bans/unbans, and session revocation events.
  • Stamp KF-Auth session ID (sid) onto PubPub sessions to support targeted session deletion on session.revoked.
  • Tighten session cookie settings and add middleware for silent OIDC re-auth (prompt=none) and platform-level ban enforcement.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
stubstub/global/setup.ts Removes noisy env logging during test setup.
server/utils/session.ts Adds session deletion by KF-Auth session id (kfSessionId).
server/spamTag/userQueries.ts Sync bans/unbans outbound to KF-Auth (deferred) and add a skipKfAuthSync guard for webhook-driven updates.
server/server.ts Updates session secret/cookie settings; wires in silent reauth + platform ban guard.
server/middleware/silentReauth.ts Adds browser-only silent reauth redirect logic using pp-lic and a circuit-breaker cookie.
server/middleware/platformBanGuard.ts Blocks API access for platform-banned users (SpamTag confirmed-spam), with caching.
server/kf/webhookHandlers.ts Implements KF-Auth webhook event handlers (profile, bans, session revocations).
server/kf/oidc.server.ts Adds prompt support for authorize URL, ID token claim decoding for sid, and outbound ban/unban sync helpers.
server/kf/api.ts Implements /api/kf/webhooks, silent-renew login flow, and logout redirect change.
server/envSchema.ts Introduces SESSION_SECRET env var in schema.
infra/.env.test Adds SESSION_SECRET for test env validation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/envSchema.ts
Comment thread server/server.ts
Comment thread server/server.ts
Comment thread server/middleware/platformBanGuard.ts Outdated
Comment thread server/kf/api.ts
@tefkah tefkah changed the title feat: sync bans with kf-auth feat: defer most session/ban authority to kf-auth Jun 9, 2026
@tefkah tefkah merged commit d7d9983 into main Jun 16, 2026
2 of 3 checks passed
@tefkah tefkah deleted the tfk/sync-bans branch June 16, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants