feat(v1.1.8): app_users table + repo (migration 0026)

Per-app end-user table for the v1.1.8 users::* SDK. Distinct from
admin_users (control-plane operators) — same Argon2id password hash
shape but everything else (uniqueness scope, ownership, lifecycle)
independent.

- Uniqueness on (app_id, lower(email)) — case-insensitive within an
  app; same email may exist across two apps.
- AppUserRepository trait + Postgres impl; every method takes app_id
  explicitly so cross-app reads are unmistakable at the call site
  (matches v1.1.3 cross-app discipline).
- Public AppUserRow never includes the password hash; the credentials
  shape is its own struct returned only by the login lookup.
- Cursor-based list keyed on (created_at, id).
- Reserved a timing-flat dummy Argon2id PHC constant in auth.rs for
  the upcoming login path so the bad-email and good-email branches
  share wall-clock cost.
- Added AppUserId + InvitationId id types in shared::ids.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-05 23:15:47 +02:00
parent 6ef9f436c1
commit 97546e2eb2
6 changed files with 446 additions and 1 deletions

View File

@@ -17,6 +17,7 @@ pub mod app_members_api;
pub mod app_members_repo;
pub mod app_repo;
pub mod app_secrets_repo;
pub mod app_user_repo;
pub mod apps_api;
pub mod auth;
pub mod auth_api;
@@ -76,6 +77,10 @@ pub use admin_user_repo::{
AdminUserCredentials, AdminUserRepository, AdminUserRepositoryError, AdminUserRow,
PostgresAdminUserRepository,
};
pub use app_user_repo::{
AppUserCredentials, AppUserRepository, AppUserRepositoryError, AppUserRow,
ListOpts as AppUserListOpts, ListPage as AppUserListPage, PostgresAppUserRepository,
};
pub use admin_users_api::{admins_router, AdminsState};
pub use api::{admin_router, AdminState};
pub use api_key_repo::{