Skip to content

Vault client + OAuth 2.1 PKCE + multi-vault switcher - #2

Merged
unforced merged 1 commit into
mainfrom
vault-client-oauth
Apr 18, 2026
Merged

Vault client + OAuth 2.1 PKCE + multi-vault switcher#2
unforced merged 1 commit into
mainfrom
vault-client-oauth

Conversation

@unforced

Copy link
Copy Markdown
Contributor

Summary

  • Full OAuth 2.1 auth code + PKCE S256 flow: RFC 8414 discovery → RFC 7591 dynamic client registration → PKCE authorize → callback → token exchange. Vault REQUIRES DCR (it validates client_id against its oauth_clients table) so this is the only way in.
  • Multi-vault store (zustand) with tokens persisted per-vault URL in localStorage and active vault id in sessionStorage-adjacent state. Users can connect several vaults and switch without re-auth.
  • Thin VaultClient over HTTP API — always sends Bearer ${token}, throws VaultAuthError on 401 so TanStack Query skips retries.
  • Router shell with four routes: / (home), /add (URL entry → OAuth), /oauth/callback (code + state → token), /vaults (manage).
  • Tests (33 total): RFC 7636 PKCE test vector, URL normalizer, discovery metadata validation, begin/complete OAuth orchestration with mocked fetch, VaultClient bearer-auth path, and App render.

Test plan

  • bun run typecheck clean
  • bun run lint clean
  • bun run test — 33/33 pass
  • bun run build succeeds
  • bun run dev serves app at http://localhost:5173
  • Manual: connect to a local vault via OAuth and verify tokens persist across reload
  • Manual: add a second vault, switch via header select, confirm independent tokens
  • Manual: remove a vault, confirm token cleared from localStorage

Notes

  • No hardcoded vault conventions — client is fully agnostic, works for any vault URL.
  • Bun's --localstorage-file flag (default without a path) installs a broken localStorage global that shadows jsdom's. Test setup polyfills with a simple in-memory Storage. Runtime storage functions are defensive (try/catch) so module init won't throw even if localStorage is unavailable.

🤖 Generated with Claude Code

Any Parachute Vault requires an OAuth-minted access token, so Lens must
handle discovery (RFC 8414), dynamic client registration (RFC 7591), and
the PKCE S256 flow — hardcoded credentials aren't an option when the UI
targets unknown vaults. Tokens are scoped per vault URL in localStorage
so users can connect to several vaults without re-auth, and the zustand
store exposes the active vault to TanStack Query.

Tests cover the pure PKCE helpers (RFC 7636 test vector), URL
normalization, discovery metadata validation, the full begin/complete
OAuth orchestration with mocked fetch, and the vault client Bearer-auth
path. A memory-Storage polyfill in test setup works around Bun's broken
`localStorage` global when vitest runs under `bun run`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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