Files
geo/server/internal/tenant/transport/auth_handler.go
T
root 19037a9e4b feat(auth): apply LoginGuard to tenant and ops login endpoints
Wire LoginGuard into tenant-api and ops-api login flows: acquire a
permit before checking credentials, record a failure on every invalid
attempt to drive lockouts, and clear counters on success. Tenant-api
now also forwards the request IP into the service so per-IP and
IP+identifier limits actually fire under real traffic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 01:07:16 +08:00

85 lines
2.0 KiB
Go

package transport
import (
"strings"
"github.com/gin-gonic/gin"
"github.com/geo-platform/tenant-api/internal/bootstrap"
"github.com/geo-platform/tenant-api/internal/shared/auth"
"github.com/geo-platform/tenant-api/internal/shared/response"
"github.com/geo-platform/tenant-api/internal/tenant/app"
"github.com/geo-platform/tenant-api/internal/tenant/repository"
)
type AuthHandler struct {
svc *app.AuthService
}
func NewAuthHandler(a *bootstrap.App) *AuthHandler {
return &AuthHandler{
svc: app.NewAuthService(
repository.NewAuthRepository(a.DB),
repository.NewTenantPlanRepository(a.DB),
a.KolProfiles,
a.JWT,
a.Sessions,
a.LoginGuard,
),
}
}
func (h *AuthHandler) Login(c *gin.Context) {
var req app.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
req.IP = c.ClientIP()
resp, err := h.svc.Login(c.Request.Context(), req)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, resp)
}
func (h *AuthHandler) Refresh(c *gin.Context) {
var req app.RefreshRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
resp, err := h.svc.Refresh(c.Request.Context(), req)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, resp)
}
func (h *AuthHandler) Me(c *gin.Context) {
actor, ok := auth.ActorFromCtx(c.Request.Context())
if !ok {
response.Error(c, response.ErrUnauthorized(40100, "not_authenticated", "not authenticated"))
return
}
resp, err := h.svc.Me(c.Request.Context(), actor)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, resp)
}
func (h *AuthHandler) Logout(c *gin.Context) {
header := c.GetHeader("Authorization")
parts := strings.SplitN(header, " ", 2)
token := ""
if len(parts) == 2 {
token = parts[1]
}
_ = h.svc.Logout(c.Request.Context(), token)
response.Success(c, nil)
}