thread-master/internal/middleware/auth.go

98 lines
3.1 KiB
Go

package middleware
import (
"net/http"
"strings"
"haixun-backend/internal/config"
"haixun-backend/internal/library/authctx"
app "haixun-backend/internal/library/errors"
"haixun-backend/internal/library/errors/code"
authusecase "haixun-backend/internal/model/auth/domain/usecase"
"haixun-backend/internal/response"
"github.com/zeromicro/go-zero/rest"
)
const MemberAuthorizationHeader = "X-Member-Authorization"
// Auth validates member JWT from Authorization: Bearer <access_token>.
// Use for all protected routes except AI endpoints that reserve Authorization for provider tokens.
func Auth(tokens authusecase.TokenUseCase, cfg config.AuthConf, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
actor, err := resolveActor(r, tokens, cfg, r.Header.Get("Authorization"), "missing bearer token")
if err != nil {
response.Write(r.Context(), w, nil, err)
return
}
next(w, r.WithContext(authctx.WithActor(r.Context(), actor)))
}
}
// MemberAuth validates member JWT from X-Member-Authorization.
// AI routes keep Authorization for provider API keys.
func MemberAuth(tokens authusecase.TokenUseCase, cfg config.AuthConf, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
actor, err := resolveActor(r, tokens, cfg, r.Header.Get(MemberAuthorizationHeader), "missing member bearer token")
if err != nil {
response.Write(r.Context(), w, nil, err)
return
}
next(w, r.WithContext(authctx.WithActor(r.Context(), actor)))
}
}
// RestAuth wraps Auth as a go-zero route middleware.
func RestAuth(tokens authusecase.TokenUseCase, cfg config.AuthConf) rest.Middleware {
return func(next http.HandlerFunc) http.HandlerFunc {
return Auth(tokens, cfg, next)
}
}
// RestMemberAuth wraps MemberAuth as a go-zero route middleware.
func RestMemberAuth(tokens authusecase.TokenUseCase, cfg config.AuthConf) rest.Middleware {
return func(next http.HandlerFunc) http.HandlerFunc {
return MemberAuth(tokens, cfg, next)
}
}
func resolveActor(
r *http.Request,
tokens authusecase.TokenUseCase,
cfg config.AuthConf,
authorizationHeader string,
missingMessage string,
) (authctx.Actor, error) {
if cfg.DevHeaderFallback {
tenantID := strings.TrimSpace(r.Header.Get("X-Tenant-ID"))
uid := strings.TrimSpace(r.Header.Get("X-UID"))
if tenantID != "" && uid != "" {
return authctx.Actor{TenantID: tenantID, UID: uid}, nil
}
}
raw := bearerToken(authorizationHeader)
if raw == "" {
return authctx.Actor{}, app.For(code.Auth).AuthUnauthorized(missingMessage)
}
if tokens == nil {
return authctx.Actor{}, app.For(code.Auth).SysNotImplemented("token usecase is not configured")
}
claims, err := tokens.ParseAccessToken(r.Context(), raw)
if err != nil {
return authctx.Actor{}, err
}
return authctx.Actor{TenantID: claims.TenantID, UID: claims.UID, JTI: claims.JTI}, nil
}
func BearerToken(header string) string {
return bearerToken(header)
}
func bearerToken(header string) string {
const prefix = "Bearer "
if !strings.HasPrefix(header, prefix) {
return ""
}
return strings.TrimSpace(strings.TrimPrefix(header, prefix))
}