106 lines
3.2 KiB
Go
106 lines
3.2 KiB
Go
//go:build darwin
|
|
|
|
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
// RunFirstRunSetup guides the user through customer.verae.ai signup and
|
|
// xVerae- signing-key paste. Interactive (osascript dialogs).
|
|
func RunFirstRunSetup() error {
|
|
if HasXVeraeToken() {
|
|
return nil
|
|
}
|
|
|
|
btn, err := osaButton(
|
|
"Verae Print Stamp needs a customer signing key before it can timestamp documents.\n\nCreate a free account on customer.verae.ai, open Security, and create an xVerae- token.",
|
|
"Verae customer account",
|
|
[]string{"Create account…", "I already have a key", "Cancel"},
|
|
"Create account…",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch btn {
|
|
case "Cancel":
|
|
return fmt.Errorf("setup cancelled")
|
|
case "Create account…":
|
|
_ = exec.Command("open", PortalSignupURL).Start()
|
|
btn2, err2 := osaButton(
|
|
"In the browser:\n1. Create your account (or sign in)\n2. Open the Security tab\n3. Create an xVerae- token\n4. Copy it once — it is shown only once\n\nClick Continue when you have copied the key.",
|
|
"Create your signing key",
|
|
[]string{"Continue", "Cancel"},
|
|
"Continue",
|
|
)
|
|
if err2 != nil || btn2 == "Cancel" {
|
|
return fmt.Errorf("setup cancelled")
|
|
}
|
|
}
|
|
|
|
token, err := osaPrompt(
|
|
"Paste your xVerae- signing key from customer.verae.ai → Security.\n\nIt starts with xVerae- and is shown only once when created.",
|
|
"Paste signing key",
|
|
"",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
token = strings.TrimSpace(token)
|
|
if token == "" {
|
|
return fmt.Errorf("no key pasted")
|
|
}
|
|
if err := SaveXVeraeToken(token); err != nil {
|
|
return err
|
|
}
|
|
_, _ = osaButton(
|
|
"Signing key saved.\n\nYou can Print → Destination → Verae Timestamping from any app.\nKeys are stored in Keychain (verae.xVeraeToken) and ~/.verae/xverae-token.",
|
|
"Verae Print Stamp",
|
|
[]string{"OK"},
|
|
"OK",
|
|
)
|
|
return nil
|
|
}
|
|
|
|
func osaButton(message, title string, buttons []string, defaultBtn string) (string, error) {
|
|
quoted := make([]string, len(buttons))
|
|
for i, b := range buttons {
|
|
quoted[i] = fmt.Sprintf("%q", b)
|
|
}
|
|
script := fmt.Sprintf(
|
|
`button returned of (display dialog %q with title %q buttons {%s} default button %q)`,
|
|
message, title, strings.Join(quoted, ", "), defaultBtn,
|
|
)
|
|
out, err := exec.Command("osascript", "-e", script).Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("dialog cancelled")
|
|
}
|
|
return strings.TrimSpace(string(out)), nil
|
|
}
|
|
|
|
func osaPrompt(message, title, defaultAnswer string) (string, error) {
|
|
script := fmt.Sprintf(
|
|
`text returned of (display dialog %q with title %q default answer %q with hidden answer buttons {"Cancel", "Save key"} default button "Save key")`,
|
|
message, title, defaultAnswer,
|
|
)
|
|
out, err := exec.Command("osascript", "-e", script).Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("prompt cancelled")
|
|
}
|
|
return strings.TrimSpace(string(out)), nil
|
|
}
|
|
|
|
// EnsureReady runs first-run setup when no signing key is present.
|
|
func EnsureReady() error {
|
|
if HasXVeraeToken() {
|
|
return nil
|
|
}
|
|
// Headless (no GUI session): fail with instructions
|
|
if os.Getenv("SSH_CONNECTION") != "" && os.Getenv("DISPLAY") == "" {
|
|
return fmt.Errorf("no xVerae- signing key — run: verae-print setup\nOr open %s , create an account, mint a key under Security, then:\n verae-print setup", PortalSignupURL)
|
|
}
|
|
return RunFirstRunSetup()
|
|
}
|