Skip to content

[auth 6] Complete adapter-neutral identity, profile, account and binding contracts #194

Description

@artiphishle

Roadmap: #188

Respect the [auth] roadmap first and the AGENTS.md file of every repository touched.

Previous roadmap step

[auth 5] (#193) establishes one brokered OAuth flow across Web, iOS and Android. This issue defines the stable adapter-neutral domain consumed after authentication so profile UI, bindings and account operations do not depend on Supabase/provider metadata shapes.

Repository impact

Likely:

  • ankhorage/contracts
  • ankhorage/supabase-auth
  • ankhorage/runtime
  • ankhorage/infra where generated profile persistence/trigger ownership must evolve
  • ankhorage/studio only for configuration/generation consumption

Pause at package release boundaries. Contracts own neutral serializable/interfaces; Supabase Auth implements them; Runtime exposes them; Studio must not duplicate them.

Goal

Complete one canonical Auth/account domain that separates:

Auth identity/session
  -> authenticated subject and provider-normalized claims

Editable user profile
  -> app-owned mutable fields

Account operations
  -> security/identity mutations and lifecycle actions

The same generated UI and bindings must work with future Auth adapters without knowing their provider-specific metadata.

Current baseline and gaps

Current AuthUser already normalizes fields such as email, phone, username, displayName and avatarUrl, while AuthProfileSpec can request fields including firstName and lastName.

Gaps to resolve:

  • firstName/lastName are not canonical normalized Auth/Profile fields everywhere;
  • identity/session claims and editable profile values are not explicitly separated;
  • no generic profile read/update adapter contract exists;
  • account actions such as change email/password/delete account are absent or provider-specific;
  • bindings have generic context/state but no canonical auth or profile source;
  • provider metadata merge/ownership policy is not explicit.

Domain direction

The exact APIs must be finalized in Contracts, but conceptually:

interface AuthUser {
  id: string;
  email?: string;
  phone?: string;
  username?: string;
  displayName?: string;
  firstName?: string;
  lastName?: string;
  avatarUrl?: string;
  metadata?: Record<string, unknown>;
}

interface UserProfile {
  id: string;
  displayName?: string;
  firstName?: string;
  lastName?: string;
  avatarUrl?: string;
  custom?: Record<string, unknown>;
}

Do not freeze this sketch without deciding whether email/phone/username belong only to identity or may also be mirrored read-only into profile presentation.

Requirements

Identity/session

  • adapters normalize provider/backend-specific claims into canonical fields;
  • raw provider metadata remains available only as an escape hatch, not the preferred binding surface;
  • session serialization remains secure and minimal;
  • no client secret/provider token leakage into normalized user metadata;
  • identity fields have explicit mutability/verification semantics.

Profile adapter/capabilities

Define capability-driven operations, conceptually:

getProfile()
updateProfile(patch)

Potential additional capabilities must be explicit rather than assumed.

The profile store may be an app-owned table for Supabase today and another implementation later. Generated UI consumes only the neutral result.

Account operations

Define neutral capabilities/results for supported operations such as:

  • change email/identifier;
  • change password;
  • request/confirm re-authentication where required;
  • sign out all devices;
  • delete account;
  • link/unlink identity where later supported.

Passwords are write-only inputs. They never appear in AuthSession, AuthUser, UserProfile or bindings.

Email changes are account operations, not ordinary profile-table mutation.

Provider profile initialization and merge policy

Model explicit policy for first login and future login refresh, for example:

initializeFromProvider:
  displayName
  avatarUrl
  firstName
  lastName

refreshFromProvider:
  avatarUrl (optional)

A user-edited profile value must not be overwritten on every OAuth login unless configuration explicitly declares provider authority.

Define ownership/source metadata only if required; avoid unnecessary complexity while preserving deterministic behavior.

Canonical bindings

Extend binding sources with explicit neutral domains, conceptually:

{ kind: 'auth', path: 'user.email' }
{ kind: 'profile', path: 'displayName' }

Requirements:

  • typed/validated paths where current binding architecture supports them;
  • lifecycle states for loading/error/empty profile;
  • read-only identity vs editable profile distinction;
  • no recommendation to bind UI directly to provider-specific metadata.full_name/picture keys;
  • account actions available as canonical event/action targets rather than arbitrary operation strings.

Supabase implementation

  • normalize Google/provider aliases consistently;
  • include first/last name where available;
  • read/update the configured app-owned profile table through correct policies;
  • preserve trigger/API/app profile creation strategies;
  • map neutral account actions to Supabase Auth methods where supported;
  • advertise unsupported capabilities honestly.

Likely files

Contracts, non-exhaustive:

src/auth.ts
src/bindings.ts
src/types.ts
src/profile-contract.test.ts
src/auth-*.test.ts
src/bindings.test.ts

Supabase Auth:

src/session.ts
src/profileVerification.ts
src/types.ts
src/createSupabaseAuthAdapter.ts
src/accountLifecycle.ts

Runtime/Infra/Studio paths must be determined from current binding context, profile generation and action ownership.

Phases

Phase 1 — canonical domain and capabilities

Finalize AuthUser/UserProfile/account-operation contracts, mutability semantics and capability reporting.

Phase 2 — binding source/action contracts

Add neutral Auth/Profile sources and account action targets with validation/runtime resolution contracts.

Phase 3 — Supabase implementation and profile ownership

Implement normalization, profile read/update, merge policy and account capabilities behind @ankhorage/supabase-auth and generated Infra profile ownership.

Phase 4 — Runtime/Studio integration hardening

Expose canonical context/action resolution and update configuration/admin diagnostics without provider-specific UI coupling.

Tests

Cover at minimum:

  • provider claim aliases normalize into canonical user fields;
  • firstName/lastName round-trip where configured;
  • raw metadata does not override canonical fields unexpectedly;
  • profile get/update capability and unsupported behavior;
  • first-login provider initialization;
  • later login does not overwrite user-owned fields outside merge policy;
  • email/password account operations remain distinct from profile mutation;
  • password never serializes into session/profile/binding values;
  • auth and profile binding path resolution/lifecycle;
  • account action capability checks and result mapping;
  • Supabase profile table/RLS-safe behavior;
  • another fake adapter can satisfy the same contracts without Supabase types.

Acceptance criteria

  1. Identity, editable profile and account operations are explicitly separate.
  2. Canonical user/profile fields include the configured name/avatar concerns consistently.
  3. Adapters advertise profile/account capabilities honestly.
  4. Provider metadata initialization/refresh follows an explicit merge policy.
  5. User-edited profile fields are not silently overwritten on every login.
  6. Bindings use canonical auth/profile sources rather than provider metadata keys.
  7. Password and email security operations are modeled correctly.
  8. Supabase implements the neutral contracts without leaking Supabase types into generated UI.
  9. Future adapters can implement the same contracts.
  10. Behavior-focused tests, package releases/changesets and full validation pass.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions