| | | 1 | | using ArturRios.Heimdall.Domain.Entities; |
| | | 2 | | |
| | | 3 | | namespace ArturRios.Heimdall.Command.Services; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// The result of <see cref="ITwoFactorFactorVerifier.VerifyAsync" />: whether the submitted code |
| | | 7 | | /// or recovery code matched, and — when it did — which row (if any) the caller must mark |
| | | 8 | | /// consumed. At most one of <see cref="ConsumedEmailCode" />/<see cref="ConsumedRecoveryCode" /> |
| | | 9 | | /// is ever set, since a match is either an app code (neither), an email code, or a recovery code. |
| | | 10 | | /// </summary> |
| | 55 | 11 | | public sealed record TwoFactorFactorVerificationResult( |
| | 170 | 12 | | bool Matched, |
| | 50 | 13 | | TwoFactorEmailCode? ConsumedEmailCode, |
| | 106 | 14 | | TwoFactorRecoveryCode? ConsumedRecoveryCode) |
| | | 15 | | { |
| | 2 | 16 | | public static readonly TwoFactorFactorVerificationResult NoMatch = new(false, null, null); |
| | | 17 | | |
| | 29 | 18 | | public static TwoFactorFactorVerificationResult AppCodeMatch { get; } = new(true, null, null); |
| | | 19 | | |
| | | 20 | | public static TwoFactorFactorVerificationResult ForEmailCode(TwoFactorEmailCode emailCode) => |
| | 12 | 21 | | new(true, emailCode, null); |
| | | 22 | | |
| | | 23 | | public static TwoFactorFactorVerificationResult ForRecoveryCode(TwoFactorRecoveryCode recoveryCode) => |
| | 39 | 24 | | new(true, null, recoveryCode); |
| | | 25 | | } |
| | | 26 | | |
| | | 27 | | /// <summary> |
| | | 28 | | /// Verifies a submitted second factor — a TOTP/email code, or a recovery code — against a |
| | | 29 | | /// person's <see cref="TwoFactorAuth" /> configuration. Extracted out of |
| | | 30 | | /// <c>VerifyTwoFactorAuthCommandHandler</c> (UC-38) so the same "code against TOTP, or against |
| | | 31 | | /// the current email code, or against an unused recovery code" comparison is written exactly |
| | | 32 | | /// once and reused wherever else a second factor must be proven — <c>DisableTwoFactorAuthCommandHandler</c> |
| | | 33 | | /// (UC-39) and <c>RegenerateRecoveryCodesCommandHandler</c> (UC-40). |
| | | 34 | | /// </summary> |
| | | 35 | | public interface ITwoFactorFactorVerifier |
| | | 36 | | { |
| | | 37 | | /// <summary> |
| | | 38 | | /// Checks <paramref name="recoveryCode" /> against an unused, matching recovery code when it |
| | | 39 | | /// is supplied; otherwise checks <paramref name="code" /> against a current TOTP code (when |
| | | 40 | | /// <see cref="TwoFactorAuth.AppEnabled" />) and, failing that, against a live email code |
| | | 41 | | /// (when <see cref="TwoFactorAuth.EmailEnabled" />). Writes only what guessing itself costs — |
| | | 42 | | /// an accepted TOTP step (FR-2F-14) and a charged email-code guess (FR-2F-13). Spending the |
| | | 43 | | /// returned email or recovery code is the caller's, once every other check for its own use |
| | | 44 | | /// case has also passed, and is done through <c>IAtomicWrites</c> so only one request can. |
| | | 45 | | /// </summary> |
| | | 46 | | Task<TwoFactorFactorVerificationResult> VerifyAsync( |
| | | 47 | | TwoFactorAuth twoFactorAuth, string? code, string? recoveryCode); |
| | | 48 | | } |