Skip to content

feat(auth): wire up Authentik sign-in via Supabase custom OAuth provider - #30

Merged
rghamilton3 merged 2 commits into
mainfrom
worktree-feat+oauth-sign-in
Aug 2, 2026
Merged

rghamilton3 merged 2 commits into
mainfrom
worktree-feat+oauth-sign-in

Conversation

@rghamilton3

Copy link
Copy Markdown
Contributor

Summary

  • Add signInWithAuthentik() to src/lib/auth/authService.ts, calling supabase.auth.signInWithOAuth against the custom:authentik provider already configured in Supabase (auth.custom_oauth_providers)
  • Add a "Continue with Authentik" button to AuthForm.svelte (shown on sign-in/sign-up, not on password reset), passing window.location.href as redirectTo so any ?next= survives the OAuth round trip and the existing layout guard hands the user back to where they started
  • supabase-js's Provider type hasn't caught up with Supabase's custom-OAuth-provider feature yet, so the provider id needs a 'custom:authentik' as Provider cast — confirmed correct against Supabase's own docs and the installed @supabase/auth-js type defs

Test plan

  • bun run check
  • bun run lint
  • bun run test:unit -- --run (338/338 passing, including new coverage for the OAuth service call and the button's visibility/error states)
  • Manual click-through against a live Authentik instance

Claude-Session: https://claude.ai/code/session_015PrayZEbYqmTBCxQKWFLse

Supabase's custom_oauth_providers table already had a healthy Authentik
integration configured, but the app never called signInWithOAuth and
had no button for it — the provider was unreachable from the client.
Add signInWithAuthentik() (authService.ts) and a "Continue with
Authentik" button on the sign-in/sign-up screens, redirecting back
through the current URL so any `?next=` survives the round trip.

Claude-Session: https://claude.ai/code/session_015PrayZEbYqmTBCxQKWFLse
@github-actions

github-actions Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Claude finished @rghamilton3's task in 4m 2s —— View job


Review: Authentik sign-in via Supabase custom OAuth provider

  • Gather context (read changed files, diff against origin/main)
  • Review authService.ts OAuth changes
  • Review AuthForm.svelte changes
  • Review test coverage
  • Check project conventions (CLAUDE.md, Svelte 5 runes, tokens)
  • Post review feedback

Note: I couldn't run bun install/bun run check/lint/test:unit myself — this sandbox has no network access, so dependency install requires approval I can't obtain here. The review below is a static read of the diff; I'm trusting the PR's own checked-off test-plan items for compile/lint/test status.

Strengths

  • signInWithAuthentik follows the existing authService.ts shape exactly: same fail(error) → AuthFailure path, same doc-comment style, same "no state held here" philosophy as signIn/signUp.
  • AuthForm.svelte's onAuthentikSignIn mirrors onSubmit's busy/error handling, including the correct nuance that busy is only reset on the error path (success navigates away, so nothing after the call runs) — and that's called out in a comment.
  • Good test coverage on both the service call shape (authService.spec.ts) and the button's visibility across signin/signup/reset modes plus its error state (AuthForm.svelte.spec.ts).
  • All new styling uses existing tokens (var(--space-*), var(--text-muted), var(--border), etc.) — no raw values introduced, per CLAUDE.md.
  • OAuth is explicitly in scope per REQ-SC-001 ("Authentication shall be provided by Supabase Auth (email/password and, optionally, third-party OAuth providers)").
  • The next= round-trip doesn't reopen an open-redirect: even if redirectTo carries an attacker-influenced ?next=, the layout guard's safeNext() (src/routes/+layout.svelte) re-validates the origin before navigating, so this button doesn't bypass that existing protection.

Findings

1. redirectTo: window.location.href is a new pattern in this file — worth confirming against the live redirect allow-list (medium confidence, flagging for the pending manual test)
src/lib/auth/authService.ts:71-77 passes the entire current URL, including the live ?next= query string, as redirectTo. Every other redirect in this codebase instead builds a fixed, predictable URL — e.g. AuthForm.svelte:88's password-reset flow uses ${window.location.origin}${resolve('/account')}, never the raw href. supabase/config.toml:166-171 documents additional_redirect_urls as "a list of exact URLs," and none of the configured entries include a path, let alone a dynamic query string. This is likely fine in practice (Supabase's redirect-URL validation is commonly origin-scoped and query-agnostic), but since the PR's own test plan already has "Manual click-through against a live Authentik instance" unchecked, that's exactly the scenario that would surface a mismatch here — worth explicitly confirming the ?next=... survives the round trip (and that sign-in from a deep-linked ?next= actually lands back where expected), not just a bare /signin click-through.

2. No design mockup covers this UI (low severity, process note)
CLAUDE.md states design/screens/*.html are the canonical layouts to consult "before any UI work." There's no OAuth/SSO button or divider pattern in design/, so the "Continue with Authentik" button + or divider (AuthForm.svelte:204-215, :353-368) is a net-new pattern with nothing to check it against. It reuses existing primitives (nk-btn--secondary, tokens) sensibly, so this isn't blocking — just flagging that it's introducing UI ahead of the mockups, in case that's meant to be reconciled later.

3. 'custom:authentik' as Provider cast — unverified here
authService.ts:19. The PR description says this was checked against the installed @supabase/auth-js type defs and bun run check passed. I couldn't independently verify the cast compiles (no network access to fetch/install @supabase/auth-js@2.107.0's types in this sandbox) — noting only so CI's type-check result is the actual source of truth here, not this review.

Nothing else stood out — no security issues (auth still goes entirely through supabase.auth.*, no hand-rolled tokens), no rune violations, no lint-relevant formatting issues in the diff.
· branch worktree-feat+oauth-sign-in

…edirect allow-list

- Read `error`/`error_description` off the URL fragment on AuthForm mount, since a
  failed Authentik round trip (denied consent, unconfigured provider) has no
  onAuthStateChange event and previously landed the user back at sign-in with no
  explanation
- Reset `busy` via `finally` in onAuthentikSignIn so a resolve without an actual
  navigation (blocked popup, bfcache restore) can't leave the form stuck disabled
- Wildcard supabase/config.toml's additional_redirect_urls to the full path+query per
  Supabase's own docs (exact-match allow list) — redirectTo carries `?next=`, which the
  previous bare-origin entries would have silently dropped
- Document the Authentik provider's out-of-repo provisioning dependency
  (auth.custom_oauth_providers, dashboard-only) in authService.ts and CLAUDE.md
- Add tests for the re-entrancy guard, the busy-reset regression, stale-error
  clearing, and the new fragment-error handling

Claude-Session: https://claude.ai/code/session_015PrayZEbYqmTBCxQKWFLse
@rghamilton3
rghamilton3 merged commit 1ffe876 into main Aug 2, 2026
3 checks passed
@rghamilton3
rghamilton3 deleted the worktree-feat+oauth-sign-in branch August 2, 2026 02:17
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.

1 participant