| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- package routes
- import (
- "crypto/subtle"
- "embed"
- "encoding/base64"
- "fwh-toolbox-server/repositories/api/fwh_pro"
- "strings"
- "time"
- "github.com/gofiber/fiber/v2"
- )
- //go:embed totp_view.html
- var totpViewFiles embed.FS
- // TotpViewSetup registers the protected page on a dedicated Fiber app.
- // An unset password or TOTP secret disables both endpoints.
- func TotpViewSetup(app *fiber.App, secret, password string) {
- if secret == "" || password == "" {
- return
- }
- view := app.Group("/fwh-totp", func(c *fiber.Ctx) error {
- c.Set("Cache-Control", "no-store, max-age=0")
- c.Set("Pragma", "no-cache")
- c.Set("X-Content-Type-Options", "nosniff")
- c.Set("X-Frame-Options", "DENY")
- c.Set("Referrer-Policy", "no-referrer")
- if !validTotpViewAuth(c.Get("Authorization"), password) {
- c.Set("WWW-Authenticate", `Basic realm="FWH TOTP", charset="UTF-8"`)
- return c.SendStatus(fiber.StatusUnauthorized)
- }
- return c.Next()
- })
- view.Get("/", func(c *fiber.Ctx) error {
- page, err := totpViewFiles.ReadFile("totp_view.html")
- if err != nil {
- return fiber.ErrInternalServerError
- }
- c.Type("html", "utf-8")
- return c.Send(page)
- })
- view.Get("/code", func(c *fiber.Ctx) error {
- now := time.Now()
- code, err := fwh_pro.GenerateTOTP(secret, now)
- if err != nil {
- return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "TOTP 配置无效"})
- }
- return c.JSON(fiber.Map{
- "code": code,
- "expiresAt": (now.Unix()/30 + 1) * 30000,
- "serverTime": now.UnixMilli(),
- })
- })
- }
- func validTotpViewAuth(header, password string) bool {
- const prefix = "Basic "
- if !strings.HasPrefix(header, prefix) {
- return false
- }
- decoded, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(header, prefix))
- if err != nil {
- return false
- }
- username, candidate, ok := strings.Cut(string(decoded), ":")
- return ok && subtle.ConstantTimeCompare([]byte(username), []byte("fwh")) == 1 &&
- subtle.ConstantTimeCompare([]byte(candidate), []byte(password)) == 1
- }
|