diff --git a/docs/web/diagrams/auth/delete-account.mmd b/docs/web/diagrams/auth/delete-account.mmd
index b80b396a..8467d2d5 100644
--- a/docs/web/diagrams/auth/delete-account.mmd
+++ b/docs/web/diagrams/auth/delete-account.mmd
@@ -37,7 +37,7 @@ sequenceDiagram
UserMgr->>UserStore: DeleteAsync(user)
activate UserStore
UserStore->>DB: DELETE FROM UserAccount WHERE UserAccountID = @Id
- Note right of UserStore: UserCredential/UserVerification/UserAvatar cascade on delete. Posts, comments, photos, and follows use ON DELETE NO ACTION — if any reference this account, the DELETE throws a foreign key violation (SQL error 547)
+ Note right of UserStore: UserCredential, UserVerification, and UserProfile cascade on delete (UserAvatar cascades transitively through UserProfile). Posts, comments, photos, and follows use ON DELETE NO ACTION — if any reference this account, the DELETE throws a foreign key violation (SQL error 547)
alt Foreign key violation (dependent records exist)
UserStore-->>UserMgr: IdentityResult.Failed (AccountHasDependentRecords)
diff --git a/docs/web/diagrams/auth/login.mmd b/docs/web/diagrams/auth/login.mmd
index e6d87273..06de7dcc 100644
--- a/docs/web/diagrams/auth/login.mmd
+++ b/docs/web/diagrams/auth/login.mmd
@@ -55,7 +55,7 @@ sequenceDiagram
Note right of UserMgr: HasPasswordAsync short-circuits to false (no active credential hydrated) without calling the hasher — otherwise delegates to Argon2PasswordHasher
UserMgr->>Argon2: VerifyHashedPassword(user, user.PasswordHash, password)
activate Argon2
- Note right of Argon2: Split stored "salt:hash", rehash password with same salt, constant-time compare (CryptographicOperations.FixedTimeEquals)
+ Note right of Argon2: Parse stored PHC string ($argon2id$v=19$m=...,t=...,p=...$salt$hash), rehash password with the embedded salt/parameters, constant-time compare (CryptographicOperations.FixedTimeEquals)
Argon2-->>UserMgr: Success/Failed
deactivate Argon2
UserMgr-->>LoginHandler: true/false
diff --git a/docs/web/diagrams/auth/registration.mmd b/docs/web/diagrams/auth/registration.mmd
index c8f58484..d5ae2494 100644
--- a/docs/web/diagrams/auth/registration.mmd
+++ b/docs/web/diagrams/auth/registration.mmd
@@ -15,6 +15,7 @@ sequenceDiagram
end
box rgb(219,238,221) Repository / Cross-slice
participant UserStore as DapperUserStore
(IUserStore/IUserPasswordStore/IUserEmailStore)
+ participant ProfileHandler as CreateUserProfileHandler
(Profile slice)
participant EmailDispatcher as EmailDispatcher
(Features.Emails)
end
participant DB as SQL Server
@@ -39,8 +40,8 @@ sequenceDiagram
activate UserMgr
UserMgr->>Argon2: HashPassword(user, password)
activate Argon2
- Note right of Argon2: Argon2id — salt 16 bytes, memory 64MB, iterations 4, parallelism = CPU count, hash output 32 bytes
- Argon2-->>UserMgr: "base64(salt):base64(hash)"
+ Note right of Argon2: Argon2id — salt 16 bytes, memory 64MB, iterations 4, parallelism 2 (fixed, to avoid thread-pool exhaustion), hash output 32 bytes
+ Argon2-->>UserMgr: "$argon2id$v=19$m=65536,t=4,p=2$<salt>$<hash>" (PHC string)
deactivate Argon2
UserMgr->>UserStore: SetPasswordHashAsync(user, hash)
@@ -84,6 +85,19 @@ sequenceDiagram
Mediator->>API: propagate
API->>User: 409 Conflict "Username or email already exists"
else result.Succeeded
+ RegHandler->>Mediator: Send(CreateUserProfileCommand(user.Id, ""))
+ activate Mediator
+ Mediator->>ProfileHandler: Handle(command)
+ activate ProfileHandler
+ ProfileHandler->>DB: INSERT INTO UserProfile (UserAccountId, Biography)
+ activate DB
+ DB-->>ProfileHandler: UserProfileId
+ deactivate DB
+ ProfileHandler-->>Mediator: UserProfileId
+ deactivate ProfileHandler
+ Mediator-->>RegHandler: UserProfileId
+ deactivate Mediator
+
RegHandler->>TokenSvc: GenerateAccessToken(user)
activate TokenSvc
TokenSvc->>JWT: GenerateJwt(userId, username, expiry)
@@ -117,7 +131,7 @@ sequenceDiagram
RegHandler->>Mediator: Send(SendRegistrationEmailCommand) (Shared.Application cross-slice contract)
activate Mediator
- Note right of Mediator: Routed to Features.Emails' SendRegistrationEmailHandler — the only cross-slice call in the backend
+ Note right of Mediator: Routed to Features.Emails' SendRegistrationEmailHandler — the second of two cross-slice dispatches in this flow (profile creation is the first)
Mediator->>EmailDispatcher: SendRegistrationEmailAsync(firstName, email, confirmationToken)
activate EmailDispatcher