package auth import ( "encoding/json" "errors" "net/http" "net/http/httptest" "testing" ) func fakePortal(t *testing.T, installAPI bool) *httptest.Server { mux := http.NewServeMux() js := func(w http.ResponseWriter, code int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) _ = json.NewEncoder(w).Encode(v) } body := func(r *http.Request) map[string]any { m := map[string]any{} _ = json.NewDecoder(r.Body).Decode(&m) return m } if installAPI { mux.HandleFunc("/portal/api/install/capabilities", func(w http.ResponseWriter, r *http.Request) { js(w, 200, map[string]any{"installApi": true, "emailVerification": true, "passwordPolicy": map[string]any{"minLength": 12, "requireLetter": true, "requireDigit": true}}) }) mux.HandleFunc("/portal/api/install/register", func(w http.ResponseWriter, r *http.Request) { js(w, 202, map[string]any{"pendingId": "p1"}) }) mux.HandleFunc("/portal/api/install/verify", func(w http.ResponseWriter, r *http.Request) { if body(r)["code"] != "123456" { js(w, 400, map[string]any{"error": "invalid_code"}) return } js(w, 201, map[string]any{"token": "xVerae-verified-abcdefgh"}) }) mux.HandleFunc("/portal/api/install/login", func(w http.ResponseWriter, r *http.Request) { b := body(r) if b["totpCode"] == nil { js(w, 401, map[string]any{"error": "totp_required"}) return } js(w, 200, map[string]any{"token": "xVerae-login-abcdefgh"}) }) } mux.HandleFunc("/portal/api/login", func(w http.ResponseWriter, r *http.Request) { js(w, 200, map[string]any{"token": "sess"}) }) mux.HandleFunc("/portal/api/signup", func(w http.ResponseWriter, r *http.Request) { js(w, 200, map[string]any{"token": "sess"}) }) mux.HandleFunc("/portal/api/tokens", func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") != "Bearer sess" { js(w, 401, map[string]any{"error": "no"}) return } js(w, 200, map[string]any{"token": "xVerae-minted-abcdefgh"}) }) mux.HandleFunc("/portal/api/logout", func(w http.ResponseWriter, r *http.Request) { js(w, 200, map[string]any{}) }) s := httptest.NewServer(mux) t.Cleanup(s.Close) t.Setenv("VERAE_PORTAL_BASE", s.URL) t.Setenv("VERAE_ALLOW_INSECURE_PORTAL", "1") return s } func TestPortalFallback(t *testing.T) { fakePortal(t, false) caps := GetCapabilities() if caps.InstallAPI || caps.Policy.MinLength != DefaultPolicy.MinLength { t.Fatalf("caps %+v", caps) } k, err := InstallLogin(caps, "a@b.co", "pw", "", "n") if err != nil || k != "xVerae-minted-abcdefgh" { t.Fatal(k, err) } rr, err := InstallRegister(caps, "N", "a@b.co", "pw", "n") if err != nil || rr.Key == "" || rr.PendingID != "" { t.Fatal(rr, err) } } func TestPortalInstallAPI(t *testing.T) { fakePortal(t, true) caps := GetCapabilities() if !caps.InstallAPI || caps.Policy.MinLength != 12 { t.Fatalf("caps %+v", caps) } if _, err := InstallLogin(caps, "a@b.co", "pw", "", "n"); !errors.Is(err, ErrTOTPRequired) { t.Fatal("want totp_required", err) } if k, err := InstallLogin(caps, "a@b.co", "pw", "123456", "n"); err != nil || k != "xVerae-login-abcdefgh" { t.Fatal(k, err) } rr, err := InstallRegister(caps, "N", "a@b.co", "pw", "n") if err != nil || rr.PendingID != "p1" { t.Fatal(rr, err) } if _, err := InstallVerify("p1", "000000", "n"); err == nil { t.Fatal("bad code accepted") } if k, err := InstallVerify("p1", "123456", "n"); err != nil || k != "xVerae-verified-abcdefgh" { t.Fatal(k, err) } } func TestPolicyAndHTTPS(t *testing.T) { p := DefaultPolicy for pw, ok := range map[string]bool{"short1": false, "longenoughnodigit": false, "longenough123": true, "george12345x": false} { if (p.Check(pw, "george@x.com") == nil) != ok { t.Errorf("policy %q want ok=%v", pw, ok) } } t.Setenv("VERAE_PORTAL_BASE", "http://example.com") t.Setenv("VERAE_ALLOW_INSECURE_PORTAL", "") if _, _, err := portalPost("/login", "", map[string]any{}); err == nil { t.Fatal("non-https portal allowed") } if MaskKey("xVerae-abcdefghijklmnop") != "xVerae-...mnop" || MaskEmail("george@x.com") != "g***@x.com" { t.Fatal("masking") } }