Auth: HttpOnly cookie buhapp_at (без CORS preflight)
- Login/Register ставят cookie (HttpOnly, Secure, SameSite=Lax) - Middleware читает cookie первым - CORS: AllowCredentials=true - Для прямых API-вызовов остался Authorization Bearer - Legacy ?_t= query fallback
This commit is contained in:
parent
249c3d2a8f
commit
5b70bc3137
@ -120,6 +120,7 @@ func main() {
|
|||||||
AllowOrigins: "https://buhapp.mygoodservice.ru,https://t.me,https://web.telegram.org",
|
AllowOrigins: "https://buhapp.mygoodservice.ru,https://t.me,https://web.telegram.org",
|
||||||
AllowHeaders: "Origin, Content-Type, Accept, Authorization, X-Telegram-Bot-Api-Secret-Token",
|
AllowHeaders: "Origin, Content-Type, Accept, Authorization, X-Telegram-Bot-Api-Secret-Token",
|
||||||
AllowMethods: "GET, POST, PUT, DELETE, OPTIONS",
|
AllowMethods: "GET, POST, PUT, DELETE, OPTIONS",
|
||||||
|
AllowCredentials: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
loginLimiter := limiter.New(limiter.Config{
|
loginLimiter := limiter.New(limiter.Config{
|
||||||
|
|||||||
@ -11,15 +11,27 @@ const UserIDKey = "user_id"
|
|||||||
|
|
||||||
func Middleware(svc *Service) fiber.Handler {
|
func Middleware(svc *Service) fiber.Handler {
|
||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
|
var token string
|
||||||
|
// 1. HttpOnly cookie (основной способ — без CORS preflight)
|
||||||
|
token = c.Cookies("buhapp_at")
|
||||||
|
// 2. Authorization: Bearer <token> (для прямых API-вызовов)
|
||||||
|
if token == "" {
|
||||||
h := c.Get("Authorization")
|
h := c.Get("Authorization")
|
||||||
if h == "" {
|
if h != "" {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "missing Authorization header"})
|
|
||||||
}
|
|
||||||
parts := strings.SplitN(h, " ", 2)
|
parts := strings.SplitN(h, " ", 2)
|
||||||
if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
|
if len(parts) == 2 && strings.ToLower(parts[0]) == "bearer" {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid Authorization header"})
|
token = parts[1]
|
||||||
}
|
}
|
||||||
claims, err := svc.Parse(c.UserContext(), parts[1])
|
}
|
||||||
|
}
|
||||||
|
// 3. Fallback — query param ?_t=<token> (legacy)
|
||||||
|
if token == "" {
|
||||||
|
token = c.Query("_t")
|
||||||
|
}
|
||||||
|
if token == "" {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "missing token"})
|
||||||
|
}
|
||||||
|
claims, err := svc.Parse(c.UserContext(), token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid token"})
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid token"})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,21 @@ import (
|
|||||||
"github.com/buhapp/backend/internal/users"
|
"github.com/buhapp/backend/internal/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const cookieName = "buhapp_at"
|
||||||
|
|
||||||
|
// setAuthCookie — кладём access-token в HttpOnly cookie (CORS-friendly, без preflight)
|
||||||
|
func setAuthCookie(c *fiber.Ctx, token string, ttl time.Duration) {
|
||||||
|
c.Cookie(&fiber.Cookie{
|
||||||
|
Name: cookieName,
|
||||||
|
Value: token,
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: int(ttl.Seconds()),
|
||||||
|
HTTPOnly: true,
|
||||||
|
Secure: true,
|
||||||
|
SameSite: "Lax",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type AuthHandler struct {
|
type AuthHandler struct {
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
users *users.Repo
|
users *users.Repo
|
||||||
@ -145,6 +160,8 @@ func (h *AuthHandler) Register(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "token failed"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "token failed"})
|
||||||
}
|
}
|
||||||
|
// ставим access-token в HttpOnly cookie
|
||||||
|
setAuthCookie(c, tokens.Access, h.cfg.JwTAccessTTL())
|
||||||
|
|
||||||
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
|
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
|
||||||
"user": fiber.Map{
|
"user": fiber.Map{
|
||||||
@ -207,12 +224,8 @@ func (h *AuthHandler) Login(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "token failed"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "token failed"})
|
||||||
}
|
}
|
||||||
|
// ставим access-token в HttpOnly cookie — браузер шлёт автоматически, без CORS preflight
|
||||||
uid := u.ID
|
setAuthCookie(c, tokens.Access, h.cfg.JwTAccessTTL())
|
||||||
_ = h.audit.Log(c.UserContext(), &audit.Event{
|
|
||||||
UserID: &uid, Action: "user.login",
|
|
||||||
IP: c.IP(), UserAgent: c.Get("User-Agent"),
|
|
||||||
})
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"user": u,
|
"user": u,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user