feat: Keycloak OIDC auth -- protect write ops #24
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/pal-e-docs-app!24
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "22-feat-keycloak-oidc-auth-protect-write-op"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Changes
src/auth.ts: Auth.js config with Keycloak provider, JWT role extraction from access token, and redirect callback that intercepts signout to route through Keycloak logout endpointsrc/hooks.server.ts: Wire Auth.js handle into SvelteKit server hookssrc/routes/+layout.server.ts: Passsessionto layout data alongside existingprojectssrc/routes/+layout.svelte: Auth-aware navbar (user name + sign out when authenticated, sign in button when not); hide Quick-Jot FAB andnkeyboard shortcut for unauthenticated userssrc/routes/api/notes/+server.ts: Gate POST endpoint behind authenticated session check, return 401 if not authenticatedsrc/routes/signin/+page.svelte++page.server.ts: Sign-in page with Keycloak redirect; already-authenticated users are redirected to homesrc/routes/signout/+page.svelte++page.server.ts: Sign-out page with hiddenredirectToinput that triggers Keycloak logout endpoint to clear SSO sessionk8s/deployment.yaml: AddenvFromforpal-e-auth-secretssecret andAUTH_TRUST_HOST: "true"env vark8s/kustomization.yaml: Includepal-e-auth-secrets.enc.yamlin resources for ArgoCD SOPS CMP decryptionpackage.json: Add@auth/sveltekitdependencyTest Plan
npm run buildpassesnpm run lintpassesnpm run checkpasses with 0 errors/api/noteswithout session returns 401/signinshows Keycloak login (not auto-re-authenticated)Review Checklist
Related
8a9ed86(SOPS-encryptedpal-e-auth-secrets.enc.yaml)PR #24 Review
feat: Keycloak OIDC auth -- protect write ops
Branch:
22-feat-keycloak-oidc-auth-protect-write-op->mainBLOCKERS
None.
NITS
.env.examplenot updated with auth env vars. Local development requiresAUTH_SECRET,AUTH_KEYCLOAK_ID,AUTH_KEYCLOAK_SECRET, andAUTH_KEYCLOAK_ISSUERbut.env.examplestill only documentsPAL_E_DOCS_API_URL. Developers cloning the repo will not know what auth environment variables to set. Add placeholder entries to.env.example.Hardcoded Tailscale URLs in
src/auth.ts.KEYCLOAK_LOGOUT_URL,APP_URL, andCLIENT_IDare string constants rather than environment variables. This works for the current single-environment deployment, but makes local development impossible without code changes. Consider reading these from env vars with the current values as fallback defaults. Low priority given the tailnet-only deployment model.@ts-expect-errorin session callback. Thesession.rolesassignment uses@ts-expect-errorinstead of extending the Auth.jsSessiontype via asrc/app.d.tsdeclaration. This works but is fragile -- a future Auth.js upgrade that adds arolesfield would silently change behavior. Consider adding a proper type augmentation insrc/app.d.ts:token.accessTokenstored but never used. In the JWT callback,token.accessToken = account.access_tokenis set but never referenced outside that callback. The roles are extracted immediately from the decoded payload. This adds the full access token to the JWT cookie unnecessarily, increasing cookie size. If roles are the only thing needed, remove thetoken.accessTokenassignment.PR body says "Closes #22" but also mentions "Closes #20". The issue checklist in #22 says
PR opened with "Closes #20"which appears to be a copy-paste artifact from the issue template. The PR body itself correctly referencesCloses #22. No action needed, just noting the issue-side discrepancy.ACCEPTANCE CRITERIA VERIFICATION
POST /api/notesis gated. Layout passes session to UI but does not redirect unauthenticated users.{#if isAuthenticated && !quickJotOpen}guards the FAB. Keyboard shortcutnalso gated byisAuthenticated.+server.tscheckslocals.auth()and throwserror(401, 'Authentication required')before any body parsing.signin/+page.server.tsexportsactions: { default: signIn }which triggers Auth.js Keycloak OIDC flow. Already-authenticated users get303 /redirect.{session?.user?.name ?? 'User'}and a styled "Sign out" link whenisAuthenticated.nshortcut both controlled byisAuthenticatedderived state.redirectTo=__keycloak_logout__. Theredirectcallback inauth.tsintercepts this sentinel value and constructs the Keycloak logout URL withpost_logout_redirect_uriandclient_id. This matches the proven westside-app pattern.trustHost: trueover HTTPS), and SameSite=Lax. No overrides that would weaken these defaults.ADDITIONAL CHECKS
envFrom: - secretRef: name: pal-e-auth-secretscorrectly references the encrypted secret.pal-e-auth-secrets.enc.yamladded to resources list.export const handle = authHandle.await locals.auth()thenif (!session?.user)is the correct Auth.js pattern.ENC[AES256_GCM,...]). Age recipient key matches known public keyage15ct78fr4scv.... No plaintext credentials anywhere.{KEYCLOAK_LOGOUT_URL}?post_logout_redirect_uri={APP_URL}&client_id={CLIENT_ID}.$derived(!!session?.user)->isAuthenticatedgates both FAB and keyboard shortcut.bg-[#0e0e18],border-[#1a1a2e],text-gray-200/500palette. Sign-in button uses#e94560accent per constraint.SOP COMPLIANCE
22-feat-keycloak-oidc-auth-protect-write-opreferences issue #22)Closes #22plan-pal-e-docsorphase-pal-e-docs-frontend-auth(the issue does reference the lineage, but the PR body omits it)VERDICT: APPROVED
Clean, well-scoped implementation that follows the proven westside-app Auth.js + Keycloak pattern. All 8 acceptance criteria are met. SOPS encryption is properly configured. No secrets exposed. The nits above (
.env.example, hardcoded URLs,@ts-expect-error, unusedtoken.accessToken, missing plan slug in PR body) are all non-blocking improvements that can be tracked separately.