-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMfaOtpProps.cs
More file actions
58 lines (48 loc) · 2.51 KB
/
Copy pathMfaOtpProps.cs
File metadata and controls
58 lines (48 loc) · 2.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using redb.Core.Attributes;
namespace redb.Identity.Core.Models;
/// <summary>
/// B3: server-side OTP material for SMS / Email MFA methods.
/// <para>
/// The plaintext OTP lives <b>only</b> in memory between generation and delivery; what we
/// persist is the SHA-256 hash of the plaintext (so a database leak does not disclose
/// live codes). <see cref="Jti"/> is a per-issuance random identifier; the client-side
/// state (see <see cref="MfaState"/>) carries it instead of the plaintext code, so the
/// code never appears in URLs / HTTP logs / referer headers.
/// </para>
/// <para>
/// Consumption is single-use: <see cref="Consumed"/> flips to <c>true</c> atomically under
/// <c>LockForUpdate</c> during verify, and subsequent attempts against the same
/// <see cref="Jti"/> are rejected regardless of code correctness. Expired
/// (<see cref="ExpiresAt"/> < now) or consumed rows are soft-deleted by the
/// <c>timer://identity-mfa-otp-cleanup</c> route.
/// </para>
/// </summary>
[RedbScheme("identity.mfa_otp")]
public class MfaOtpProps
{
/// <summary>Random per-issuance identifier. Matches <see cref="MfaState.OtpJti"/>.</summary>
public string Jti { get; set; } = "";
/// <summary>Owner user id (joined to <c>_users</c>).</summary>
public long UserId { get; set; }
/// <summary>Delivery method that issued this OTP: <c>sms</c> or <c>email</c>.</summary>
public string Method { get; set; } = "";
/// <summary>SHA-256 of the plaintext OTP, lowercase hex. Never store plaintext.</summary>
public string CodeHash { get; set; } = "";
/// <summary>Masked destination for UI display (e.g. <c>+7***1234</c>, <c>u***@example.com</c>).</summary>
public string DestinationMasked { get; set; } = "";
/// <summary>Issue timestamp (UTC).</summary>
public DateTimeOffset IssuedAt { get; set; }
/// <summary>Absolute expiry — verify requests after this instant fail regardless of code.</summary>
public DateTimeOffset ExpiresAt { get; set; }
/// <summary>
/// True once a successful verify has consumed the code. Mutated under <c>LockForUpdate</c>
/// to enforce single-use semantics even under concurrent verify attempts.
/// </summary>
public bool Consumed { get; set; }
/// <summary>
/// Count of verify attempts (success+fail). Used by the cleanup route to highlight abuse
/// and by diagnostics; not directly consulted for rate-limiting (that lives on
/// <c>MfaProps.OtpAttemptsSinceLastSent</c>).
/// </summary>
public int Attempts { get; set; }
}