cover/Elixir.WalletAuth.Plugs.RequireMfa.html

1 defmodule WalletAuth.Plugs.RequireMfa do
2 @moduledoc """
3 Plug that enforces MFA/OTP step-up verification for high-risk routes.
4
5 Per ADR 0006: OTP/MFA required for high-risk operations.
6
7 Checks the `:mfa_verified` claim in the current JWT claims or in the
8 conn session. If not present or false, returns 401 with OTP_REQUIRED code.
9
10 ## Usage
11 ```elixir
12 pipeline :high_risk do
13 plug WalletAuth.Plugs.VerifyAccessToken
14 plug WalletAuth.Plugs.RequireMfa
15 end
16 ```
17 """
18
19 import Plug.Conn
20
21 alias WalletApiContracts.ErrorEnvelope
22 alias WalletApiContracts.ErrorCodes
23
24
:-(
def init(opts), do: opts
25
26 def call(conn, _opts) do
27
:-(
claims = conn.assigns[:current_claims] || %{}
28
29
:-(
if mfa_verified?(claims) do
30
:-(
conn
31 else
32 conn
33 |> put_resp_content_type("application/json")
34 |> put_resp_header("x-request-id", get_req_id(conn))
35 |> send_resp(401, build_otp_required_error())
36
:-(
|> halt()
37 end
38 end
39
40 defp mfa_verified?(claims) do
41
:-(
claims["mfa_verified"] == true
42 end
43
44 defp build_otp_required_error do
45 ErrorEnvelope.build(
46 ErrorCodes.otp_required(),
47 "MFA/OTP verification required for this operation",
48 :auth,
49 false
50 )
51
:-(
|> Jason.encode!()
52 end
53
54 defp get_req_id(conn) do
55
:-(
case get_req_header(conn, "x-request-id") do
56
:-(
[id | _] -> id
57
:-(
[] -> WalletSharedKernel.Correlation.new_request_id()
58 end
59 end
60 end
Line Hits Source