mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 10:04:44 +00:00
Fix Argon2 hash-format claims and add the missing profile-creation step to the auth diagrams
This commit is contained in:
@@ -37,7 +37,7 @@ sequenceDiagram
|
|||||||
UserMgr->>UserStore: DeleteAsync(user)
|
UserMgr->>UserStore: DeleteAsync(user)
|
||||||
activate UserStore
|
activate UserStore
|
||||||
UserStore->>DB: DELETE FROM UserAccount WHERE UserAccountID = @Id
|
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)
|
alt Foreign key violation (dependent records exist)
|
||||||
UserStore-->>UserMgr: IdentityResult.Failed (AccountHasDependentRecords)
|
UserStore-->>UserMgr: IdentityResult.Failed (AccountHasDependentRecords)
|
||||||
|
|||||||
@@ -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
|
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)
|
UserMgr->>Argon2: VerifyHashedPassword(user, user.PasswordHash, password)
|
||||||
activate Argon2
|
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
|
Argon2-->>UserMgr: Success/Failed
|
||||||
deactivate Argon2
|
deactivate Argon2
|
||||||
UserMgr-->>LoginHandler: true/false
|
UserMgr-->>LoginHandler: true/false
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ sequenceDiagram
|
|||||||
end
|
end
|
||||||
box rgb(219,238,221) Repository / Cross-slice
|
box rgb(219,238,221) Repository / Cross-slice
|
||||||
participant UserStore as DapperUserStore<br/>(IUserStore/IUserPasswordStore/IUserEmailStore)
|
participant UserStore as DapperUserStore<br/>(IUserStore/IUserPasswordStore/IUserEmailStore)
|
||||||
|
participant ProfileHandler as CreateUserProfileHandler<br/>(Profile slice)
|
||||||
participant EmailDispatcher as EmailDispatcher<br/>(Features.Emails)
|
participant EmailDispatcher as EmailDispatcher<br/>(Features.Emails)
|
||||||
end
|
end
|
||||||
participant DB as SQL Server
|
participant DB as SQL Server
|
||||||
@@ -39,8 +40,8 @@ sequenceDiagram
|
|||||||
activate UserMgr
|
activate UserMgr
|
||||||
UserMgr->>Argon2: HashPassword(user, password)
|
UserMgr->>Argon2: HashPassword(user, password)
|
||||||
activate Argon2
|
activate Argon2
|
||||||
Note right of Argon2: Argon2id — salt 16 bytes, memory 64MB, iterations 4, parallelism = CPU count, hash output 32 bytes
|
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: "base64(salt):base64(hash)"
|
Argon2-->>UserMgr: "$argon2id$v=19$m=65536,t=4,p=2$<salt>$<hash>" (PHC string)
|
||||||
deactivate Argon2
|
deactivate Argon2
|
||||||
|
|
||||||
UserMgr->>UserStore: SetPasswordHashAsync(user, hash)
|
UserMgr->>UserStore: SetPasswordHashAsync(user, hash)
|
||||||
@@ -84,6 +85,19 @@ sequenceDiagram
|
|||||||
Mediator->>API: propagate
|
Mediator->>API: propagate
|
||||||
API->>User: 409 Conflict "Username or email already exists"
|
API->>User: 409 Conflict "Username or email already exists"
|
||||||
else result.Succeeded
|
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)
|
RegHandler->>TokenSvc: GenerateAccessToken(user)
|
||||||
activate TokenSvc
|
activate TokenSvc
|
||||||
TokenSvc->>JWT: GenerateJwt(userId, username, expiry)
|
TokenSvc->>JWT: GenerateJwt(userId, username, expiry)
|
||||||
@@ -117,7 +131,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
RegHandler->>Mediator: Send(SendRegistrationEmailCommand) (Shared.Application cross-slice contract)
|
RegHandler->>Mediator: Send(SendRegistrationEmailCommand) (Shared.Application cross-slice contract)
|
||||||
activate Mediator
|
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)
|
Mediator->>EmailDispatcher: SendRegistrationEmailAsync(firstName, email, confirmationToken)
|
||||||
activate EmailDispatcher
|
activate EmailDispatcher
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user