package middleware import ( "github.com/gofiber/fiber/v2" "wucher/internal/service" "wucher/internal/shared/pkg/apperrorsx" ) const pinVerificationHeader = "X-PIN-Verification-Token" func PinRequired(svc *service.AuthService, action string) fiber.Handler { return pinRequired(svc, action, "", true) } func PinRequiredForPermission(svc *service.AuthService, action, permissionKey string) fiber.Handler { return pinRequired(svc, action, permissionKey, true) } func PinRequiredReusable(svc *service.AuthService, action string) fiber.Handler { return pinRequired(svc, action, "", false) } func PinRequiredReusableForPermission(svc *service.AuthService, action, permissionKey string) fiber.Handler { return pinRequired(svc, action, permissionKey, false) } func pinRequired(svc *service.AuthService, action, permissionKey string, consume bool) fiber.Handler { return func(c *fiber.Ctx) error { if svc == nil { e := apperrorsx.New(apperrorsx.ErrUserUnauthorized) e.Message = "auth service not configured" return apperrorsx.Write(c, e) } raw := c.Locals("user_id") userID, ok := raw.([]byte) if !ok || len(userID) == 0 { e := apperrorsx.New(apperrorsx.ErrUserUnauthorized) e.Message = "missing auth context" return apperrorsx.Write(c, e) } if permissionKey != "" { required, err := svc.PermissionRequiresPIN(c.UserContext(), permissionKey) if err != nil { e := apperrorsx.New(apperrorsx.ErrForbiddenAccess) e.Message = "failed to verify PIN requirement" return apperrorsx.Write(c, e) } if !required { return c.Next() } } token := c.Get(pinVerificationHeader) sessionID := "" for _, accessToken := range service.CookieValues(c.Get(fiber.HeaderCookie), svc.AccessCookieName()) { sid, err := svc.ParseAccessTokenSessionID(c.UserContext(), accessToken) if err == nil { sessionID = sid break } } if sessionID == "" { return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrTokenInvalid)) } var err error if consume { err = svc.ConsumeSecurityPINActionTokenInSession(c.UserContext(), userID, action, token, sessionID) } else { err = svc.ValidateSecurityPINActionTokenInSession(c.UserContext(), userID, action, token, sessionID) } if err != nil { return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrPINVerificationRequired202)) } return c.Next() } }