//go:build !dev && !test package user // Production builds (neither the `dev` nor the `test` tag) must never persist // an unsalted digest and must never write a 2FA code in plaintext by default: // the plaintext [2FA] log delivery and the TWO_FACTOR_PEPPER fallback exist // only in dev/test builds (twofa_dev.go). Here code issuance fails closed on // BOTH missing configuration pieces: // // - a missing TWO_FACTOR_PEPPER (an unsalted digest in the 1M code space // would be offline-brute-forceable from a log/DB leak), mirroring how // main.go refuses to start without a strong JWT_SECRET_KEY; and // - a missing delivery channel. The email/SMS transport is not wired yet // (P6), so the ONLY production channel is the operator's explicit opt-in // to the insecure log-delivery mode (TWO_FACTOR_ALLOW_LOG_DELIVERY=true). // Without it, issuing a code would silently dead-end setup — the user // could never receive the code and the enforced saved-card-payments gate // would lock them out with no way forward. Issuance is refused and the // handlers surface errTwoFADeliveryUnavailable ("2FA requires an email or // SMS delivery channel; contact the salon"). // // The plaintext code is therefore never written to the server log unless the // operator explicitly opted into log delivery and accepted its risk. import ( "errors" "log" "os" ) // twoFAAllowLogDeliveryEnv is the explicit operator opt-in that makes this // production build deliver 2FA codes via the server log ([2FA] prefix) — the // documented INSECURE stand-in for the not-yet-wired email/SMS transport (P6). // Production builds fail closed without it: no delivery channel is configured, // so code issuance is refused (see twoFAEnsureIssueAllowed) and setup surfaces // errTwoFADeliveryUnavailable. Set it ONLY to keep the operator-relays-the-code // flow working in a deployment that understands the risk (anyone with backend // log access can defeat the 2FA gate on saved-card charges). Defined here in // the prod build only — dev/test builds always deliver via the log and never // consult this flag. const twoFAAllowLogDeliveryEnv = "TWO_FACTOR_ALLOW_LOG_DELIVERY" // errTwoFAPepperRequired is returned by twoFAEnsureIssueAllowed when // TWO_FACTOR_PEPPER is unset in a production build. Refusing to issue is the // only safe outcome: without the pepper a pending code would be persisted as an // unsalted SHA-256 digest in the 1M code space, which a log/DB leak could // brute-force offline. var errTwoFAPepperRequired = errors.New("TWO_FACTOR_PEPPER is not set; refusing to issue a 2FA code (an unsalted digest would be offline-brute-forceable)") // twoFAPepper returns the configured HMAC pepper, or "" when unset. Production // builds do NOT warn or fall back to the legacy digest: code issuance fails // closed via twoFAEnsureIssueAllowed, so no pending code is ever persisted as // an unsalted SHA-256 digest. func twoFAPepper() string { return os.Getenv(twoFAPepperEnv) } // twoFADeliveryAvailable reports whether a 2FA code delivery channel exists in // this build. Production: true only when the operator explicitly opted into the // insecure log-delivery mode (TWO_FACTOR_ALLOW_LOG_DELIVERY=true) or a real // email/SMS transport is wired (not yet — P6). Default false: no channel, so // code issuance is refused and setup surfaces errTwoFADeliveryUnavailable // instead of a silent dead-end. func twoFADeliveryAvailable() bool { return os.Getenv(twoFAAllowLogDeliveryEnv) == "true" } // twoFAEnsureIssueAllowed reports whether a 2FA code may be issued in this // deployment. Production requires BOTH a delivery channel and TWO_FACTOR_PEPPER: // without a channel (no email/SMS, no TWO_FACTOR_ALLOW_LOG_DELIVERY=true) the // code could never reach the user — issuing one would silently lock the user // out of the enforced saved-card-payments gate; and without the pepper every // stored code would be an offline-brute-forceable unsalted digest. Either way // issuance is refused (fail-closed). Dev/test builds always allow issuance // (twofa_dev.go). func twoFAEnsureIssueAllowed() error { if os.Getenv(twoFAPepperEnv) == "" { return errTwoFAPepperRequired } if !twoFADeliveryAvailable() { return errTwoFADeliveryUnavailable } return nil } // twoFADeliverCode delivers a fresh verification code to the user. Production // has no wired email/SMS transport (P6), so the ONLY channel is the operator's // explicit, insecure opt-in to log delivery (TWO_FACTOR_ALLOW_LOG_DELIVERY=true // — anyone with backend log access could defeat the 2FA gate on saved-card // charges). WITHOUT that flag the plaintext code is NEVER written to the log; // twoFAEnsureIssueAllowed already refused issuance, so this no-op is // unreachable. With the flag set, the code is written to the [2FA] log line // and an operator relays it to the user out-of-band, exactly like the // documented dev flow — the operator has accepted the risk of log-based // delivery. func twoFADeliverCode(userID, label, code string) { if os.Getenv(twoFAAllowLogDeliveryEnv) == "true" { log.Printf("[2FA] verification code for user %s (%s): %s", userID, label, code) } // Otherwise: deliberate no-op — never log the plaintext code by default. }