Skip to content

wallet-sdk PR3: auth + user#1121

Closed
orveth wants to merge 1 commit into
sdk/pr2-corefrom
sdk/pr3-auth-user
Closed

wallet-sdk PR3: auth + user#1121
orveth wants to merge 1 commit into
sdk/pr2-corefrom
sdk/pr3-auth-user

Conversation

@orveth

@orveth orveth commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

PR3 of the @agicash/wallet-sdk build, stacked on PR2 (#1120). Implements Slice 1 (auth + user) of the no-cache contract (§4) — replacing PR2's auth/user NotImplemented stubs with real implementations wired into Sdk.create.

Implemented

AuthDomain (re-housed framework-free from app/features/user/auth.ts + shared/auth.ts):

  • signIn / signUp / signInGuest / signOut / refresh / resetPassword / changePassword / upgradeGuest / beginGoogleSignIn / completeOAuth.
  • Strips all React/TanStack/react-router/window plumbing. The OpenSecret SDK calls + token/session handling become plain async methods.
  • Sign-in paths resolve the agicash User (the wallet.users DB row keyed by the OpenSecret user id) and emit auth:signed-in; sign-out emits auth:signed-out.
  • Guest sign-in re-signs into a stored guest account if present, else mints + persists one (via the injected StorageAdapter, not window.localStorage).
  • OAuth is a browser redirect (beginGoogleSignIn → { authUrl }), web-only. No getCurrentSession (contract decision 4 — methods return User, not a session; the JWT stays SDK-internal).

UserDomain (re-housed from user-hooks.tsx + user-repository.ts):

  • getCurrentUser() — enclave id → DB row → User, or null when signed out.
  • updateUsername(username) — throws DomainError when the username is taken (Postgres 23505 unique-violation; master raised UniqueConstraintError).

Internals added:

  • OpenSecretClient gains the OS-SDK auth wrappers + hasSession() (re-houses master's isLoggedIn onto the StorageAdapter).
  • UserRepositorywallet.users get + username update over the SDK-owned Supabase client.
  • SessionResolver — the shared id → User resolution + auth:* emission (both domains use it).
  • db-user mapper, framework-free crypto (password + SHA-256) and guest-account-storage.
  • The Supabase access-token provider now short-circuits to null when signed out (matches master supabase-session.ts).

Lifted vs re-housed

  • Verbatim logic (re-housed, no framework): ReadUserRepository.toUserdbUserToUser; WriteUserRepository.update username path; computeSHA256; generateRandomPassword (off window.crypto); isLoggedIn (off window.localStorage).
  • Re-housed framework-free (hooks → async methods): the entire useAuthActions body, the guest new-vs-stored branch, useUser/useUpdateUsername.
  • Net-new wiring: updateUsername as a first-class method (master had it UI-only); auth:signed-in/auth:signed-out emission; the session-token signed-out guard.

Still stubbed (later slices)

accounts + scan (S2), cashu + spark (S3), transactions + contacts + transfers (S4), background + realtime (S5), exchangeRate.

CI (full script, locally green)

  • biome format --write packages/wallet-sdk → clean
  • root bun --filter='*' run test@agicash/wallet-sdk 64 pass / 0 fail, web-wallet 133 pass / 0 fail (both exit 0)
  • package tsc --noEmit, biome format (check), biome lint → all clean

Tests added: dbUserToUser (full/guest/null defaults), UserRepository (get + NotFound + the taken-username → DomainError path + write-payload assertion), SessionResolver (null/resolved/emit), AuthDomainImpl (guest branching, reset-hashes-the-secret, OAuth forwarding, delegations), UserDomainImpl, crypto helpers (known SHA-256 vectors).

🤖 Generated with Claude Code

Implement AuthDomain + UserDomain, replacing PR2's auth/user NotImplemented
stubs with real impls wired into Sdk.create (Slice 1 of the build plan).

AuthDomain (re-housed framework-free from app/features/user/auth.ts +
shared/auth.ts): signIn / signUp / signInGuest / signOut / refresh /
resetPassword / changePassword / upgradeGuest / beginGoogleSignIn /
completeOAuth. Strips the React/TanStack/window plumbing; sign-in paths
resolve the agicash User (the wallet.users DB row keyed by the OpenSecret
user id) and emit auth:signed-in; sign-out emits auth:signed-out. OAuth is a
browser redirect ({ authUrl }), web-only. No getCurrentSession (contract
decision 4).

UserDomain (re-housed from user-hooks + user-repository): getCurrentUser()
(enclave id -> DB row -> User, or null) + updateUsername() (throws DomainError
on a taken username -> Postgres 23505).

Internals added: OpenSecretClient gains the OS-SDK auth wrappers + hasSession()
(re-houses isLoggedIn onto the StorageAdapter); UserRepository (wallet.users
get + username update over the SDK-owned Supabase client); SessionResolver
(shared id->User + auth:* emission); db-user mapper; framework-free crypto
(password + sha256) and guest-account storage. The Supabase token provider now
short-circuits to null when signed out.

Tests: db-user mapper (full/guest/null defaults), UserRepository (get +
NotFound + the taken-username -> DomainError path + payload assertion),
SessionResolver (null/resolved/emit), AuthDomainImpl (guest new-vs-stored
branching, reset hashes the secret, OAuth forwarding, delegations), UserDomain,
crypto helpers (known SHA-256 vectors). 64 wallet-sdk tests pass; web-wallet
133 pass; full CI (root test both packages + tsc + biome format/lint) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agicash Error Error Jun 3, 2026 9:46pm
agicash-root-dir-test Ready Ready Preview, Comment Jun 3, 2026 9:46pm

Request Review

@supabase

supabase Bot commented Jun 3, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project hrebgkfhjpkbxpztqqke because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

* untyped) Supabase client reads in this slice; replaced by the generated `Database`
* types when those are lifted into the package.
*/
export type AgicashDbUser = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we don't need to redefine these types. we need to keep generating them from the db.

i forgot to mention that in discord chats but migrations should also live in the sdk lib. Everything related to wallet db schema is basically wallet sdk domain and should live there

@orveth

orveth commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the reactive design-B stack (SDK #1129-#1138 + lib-extract #1140-#1143). This no-cache stack was the reference substrate the reactive PRs were built from (~90% reused, reads adapted to Query); it was never reviewed/merged, so nothing is lost. Closing as superseded — branch retained for reference.

@orveth orveth closed this Jun 5, 2026
@pmilic021 pmilic021 deleted the sdk/pr3-auth-user branch June 22, 2026 09:43
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