From 86b60d47518c48b041674e74980076146bbdac51 Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Mon, 1 Jun 2026 17:38:48 +0200 Subject: [PATCH] docs(user-lifecycle): show state of the art --- docs/architecture/user-lifecycle.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/architecture/user-lifecycle.md b/docs/architecture/user-lifecycle.md index f34b4a4b..c1d8bd16 100644 --- a/docs/architecture/user-lifecycle.md +++ b/docs/architecture/user-lifecycle.md @@ -155,7 +155,6 @@ DELETE FROM auth.users WHERE id = $1 ▼ COMMIT ``` - If any hook returns `Err`, the dispatcher propagates it; `delete_user_admin` rolls back the transaction and surfaces the error to the admin endpoint as a 500. The user row, all sessions, all folders/files, all grants — everything stays in place. Hook implementors should keep that strong constraint in mind: returning `Err` from `on_user_deleted` is a heavy hammer. ### Worked example: brand-new user logs in for the first time @@ -172,6 +171,30 @@ On the user's **second** login: same flow up through step 5, but `ensure_home_fo If the home folder gets deleted manually (e.g., SQL `DELETE FROM storage.folders WHERE user_id = $1`), the user's **next** login will re-create it — that's the safety-net behaviour the lifecycle hook contractually owns. +### State of the art: + +```text + DI builds: + UserLifecycleService + ├── AuditLifecycleHook (in user_lifecycle_service.rs) + ├── HomeFolderLifecycleHook (in folder_service.rs) + ├── AuthzCacheLifecycleHook (in pg_acl_engine.rs) + ├── SessionRevocationLifecycleHook (in user_lifecycle_service.rs) + └── ExternalIdentityLifecycleHook (in external_identity_service.rs, stubbed) + + AuthApplicationService fires the dispatcher from: + ├── register() → dispatch_created + ├── setup_create_admin() → dispatch_created + ├── admin_create_user() → dispatch_created + ├── OIDC JIT new-user → dispatch_created + dispatch_login + ├── login() → dispatch_login (BEFORE register_login) + ├── OIDC existing-user login → dispatch_login (BEFORE register_login) + ├── logout() → dispatch_logout(UserInitiated) + ├── refresh_token reuse → dispatch_logout(TokenReused) + ├── change_password() → dispatch_logout(PasswordChanged) + └── delete_user_admin() → dispatch_deleted(AdminDelete, tx) — abort-on-Err +``` + ## Future events (NOT shipped — design door) These events are reserved for situations that don't exist yet but probably will. Adding a method to the trait costs every hook impl a new no-op forever, so we don't add them speculatively. Each row lists what would force the addition.