98 lines
3.1 KiB
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))
|
|
}
|