-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMeMfaController.cs
More file actions
72 lines (65 loc) · 2.96 KB
/
Copy pathMeMfaController.cs
File metadata and controls
72 lines (65 loc) · 2.96 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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using redb.Identity.Contracts.Routes;
using redb.Route.Controllers.Attributes;
namespace redb.Identity.Http.Controllers;
/// <summary>
/// H3 (v1.0 DoD §6): self-service MFA management at
/// <c>/api/v1/identity/me/mfa/*</c>. Mirrors the admin <c>/mfa/*</c> surface but
/// always derives the user id from the access-token subject — under no circumstances
/// may a caller setup, confirm, disable, or regenerate codes for someone else.
/// Auth: Bearer with <c>identity:manage</c> or <c>identity:account</c> scope.
/// </summary>
[Route("me/mfa")]
public class MeMfaController : IdentityControllerBase
{
/// <summary>Return MFA enrollment status for the caller.</summary>
[HttpGet]
public async Task<object?> Status()
{
return await Forward(IdentityEndpoints.MeMfa, "status",
new Dictionary<string, object?>());
}
/// <summary>
/// Begin enrolment of an additional MFA method. Body is forwarded as-is
/// (<c>method</c>, <c>destination</c>, …); the user id is supplied by the
/// processor from the token subject and overrides any value sent by the client.
/// </summary>
[HttpPost("setup")]
public async Task<object?> Setup([FromBody] Dictionary<string, object?> body)
{
return await Forward(IdentityEndpoints.MeMfa, "setup", body);
}
/// <summary>Confirm a pending MFA enrolment with the OTP code returned to the user.</summary>
[HttpPost("confirm")]
public async Task<object?> Confirm([FromBody] Dictionary<string, object?> body)
{
return await Forward(IdentityEndpoints.MeMfa, "confirm", body);
}
/// <summary>Disable a single MFA method registered to the caller.</summary>
[HttpDelete("{method}")]
public async Task<object?> Disable([FromRoute("method")] string method)
{
return await Forward(IdentityEndpoints.MeMfa, "disable",
new Dictionary<string, object?> { ["method"] = method });
}
/// <summary>Regenerate the caller's recovery codes (invalidates all previous codes).</summary>
[HttpPost("recovery-codes")]
public async Task<object?> RegenerateRecoveryCodes()
{
return await Forward(IdentityEndpoints.MeMfa, "regenerate-recovery",
new Dictionary<string, object?>());
}
/// <summary>
/// MFA backup-codes download UX — regenerates the caller's recovery codes and returns
/// them as a <c>text/plain</c> attachment with <c>Content-Disposition: attachment;
/// filename=redb-recovery-codes.txt</c>. Mirrors GitHub / Google / Auth0 behaviour:
/// downloading is destructive (atomically invalidates the prior batch) since recovery
/// codes are stored only as salted hashes server-side and have no other moment of
/// existing in plaintext.
/// </summary>
[HttpGet("recovery-codes/download")]
public async Task<object?> DownloadRecoveryCodes()
{
return await Forward(IdentityEndpoints.MeMfa, "download-recovery",
new Dictionary<string, object?>());
}
}