| | | 1 | | namespace ArturRios.Heimdall.Command.Services; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// The person a UC-38 challenge token was redeemed for. Carries only what |
| | | 5 | | /// <c>VerifyTwoFactorAuthCommandHandler</c> needs to look the person back up — the rest of the |
| | | 6 | | /// token's claims are the presentation layer's concern. |
| | | 7 | | /// </summary> |
| | | 8 | | /// <param name="PersonId">The <c>PublicId</c> of the person the challenge token names.</param> |
| | | 9 | | /// <param name="ChallengeId"> |
| | | 10 | | /// Which challenge this is. It must still equal the configuration's <c>TwoFactorAuth.ChallengeId</c> |
| | | 11 | | /// for the token to be honoured, which is what makes a challenge redeemable once (FR-2F-10). |
| | | 12 | | /// </param> |
| | | 13 | | /// <param name="SecurityStamp"> |
| | | 14 | | /// The person's <c>SecurityStamp</c> when the challenge was issued, or <c>null</c> when the token |
| | | 15 | | /// carries none. It must still equal the person's for the token to be honoured (FR-AU-11), so a |
| | | 16 | | /// credential change in between ends the challenge as it ends every other token. |
| | | 17 | | /// </param> |
| | 798 | 18 | | public record TwoFactorChallengePrincipal(Guid PersonId, Guid ChallengeId, Guid? SecurityStamp = null); |
| | | 19 | | |
| | | 20 | | /// <summary> |
| | | 21 | | /// Issues the short-lived challenge token AF-11g returns instead of a full authentication token |
| | | 22 | | /// (FR-2F-07, NFR-17), when a person with active two-factor authentication passes UC-11's |
| | | 23 | | /// password check. Separate from <see cref="IAuthTokenIssuer" /> because the two tokens carry |
| | | 24 | | /// deliberately different claims — this one is scoped only to second-factor verification and |
| | | 25 | | /// expires far sooner. |
| | | 26 | | /// </summary> |
| | | 27 | | public interface ITwoFactorChallengeTokenIssuer |
| | | 28 | | { |
| | | 29 | | /// <param name="personId">The <c>PublicId</c> of the person who passed the password check.</param> |
| | | 30 | | /// <param name="roleId">Their role value (see <c>Roles</c>) — carried so the mapper that reads the token back can b |
| | | 31 | | /// <param name="challengeId">The identifier recorded as the configuration's outstanding challenge, carried in the t |
| | | 32 | | /// <param name="securityStamp">The person's current <c>SecurityStamp</c>, carried so a credential change before the |
| | | 33 | | /// <returns>The signed challenge token and its (short) expiry.</returns> |
| | | 34 | | Task<AuthToken> IssueAsync(Guid personId, int roleId, Guid challengeId, Guid securityStamp); |
| | | 35 | | } |
| | | 36 | | |
| | | 37 | | /// <summary> |
| | | 38 | | /// Validates a UC-38 challenge token — signature, expiry, and the MFA-pending claim (AF-38a, |
| | | 39 | | /// FR-2F-10) — and resolves the person and challenge it names, without any database read. Whether |
| | | 40 | | /// that challenge is still outstanding is the handler's question, answered against the database. |
| | | 41 | | /// </summary> |
| | | 42 | | public interface ITwoFactorChallengeTokenValidator |
| | | 43 | | { |
| | | 44 | | /// <param name="token">The challenge token submitted to <c>POST /api/auth/2fa/verify</c>.</param> |
| | | 45 | | /// <returns>The named person and challenge, or <see langword="null" /> if the token is missing, malformed, unsigned |
| | | 46 | | Task<TwoFactorChallengePrincipal?> ValidateAsync(string? token); |
| | | 47 | | } |