File size: 3,473 Bytes
6bc074c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Package authresolver 解析请求的 Authorization header,返回对应的 *accounts.Account。
//
// 设计原则:
//   - 不持有 token 池,池由 accounts.Pool 管理
//   - 只负责"从请求头拿到 token 字符串,定位/创建一个 account"
//   - customer key 匹配: 视为无外部 token,返回 nil,让 handler 从池中取一个
//   - 外部 access_token: 创建临时账号 (TypeNoAuth 或 TypeFree)
//   - 外部 JWT (eyJhbGciOiJSUzI1NiI...): 创建临时账号 (TypeFree)
//   - UUID 设备号: 创建临时账号 (TypeNoAuth)
//
// 配合 handler 的使用模式:
//   authresolver.ResolveAccessToken(c) → 返回 access_token 字符串
//   handler 拿字符串去 pool.Lookup() / pool.CreateTemporary()
package authresolver

import (
	"aurora/internal/accounts"
	"errors"
	"os"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// AccessTokenResolver 从 gin.Context 解析 access token。
type AccessTokenResolver struct {
	// CustomerKey 全局密钥,匹配时视为"无外部 token" (用 customer key 当默认鉴权)
	CustomerKey string
}

func New() *AccessTokenResolver {
	return &AccessTokenResolver{
		CustomerKey: os.Getenv("Authorization"),
	}
}

// ResolveAccessToken 从 gin.Context 解析 Authorization header 中的 token 字符串。
//
// 返回:
//   - token: 解析出的 token (可能是 customer key / JWT access token / UUID device id / "")
//   - isCustomerKey: true 表示 token 等于 CustomerKey,handler 应走"无外部 token"分支
//   - err: 解析失败 (极少发生)
func (r *AccessTokenResolver) ResolveAccessToken(c *gin.Context) (token string, isCustomerKey bool, err error) {
	authHeader := c.GetHeader("Authorization")
	token, _ = splitAuthHeader(authHeader)

	if token != "" && r.CustomerKey != "" && token == r.CustomerKey {
		// customer key 匹配,视为无外部 token
		return "", true, nil
	}
	return token, false, nil
}

// ResolveAccount 从 gin.Context 解析后直接返回对应的 *accounts.Account(临时账号)。
//
// 行为:
//   - customer key 或无 token → 返回 nil (handler 应从池中取)
//   - UUID → 创建 TypeNoAuth 临时账号
//   - JWT access_token → 创建 TypeFree 临时账号 (Token 字段存 JWT)
//   - 其它 → 兜底: 当成 TypeFree
//
// 不消耗 pool 中的账号,不修改 pool。
func (r *AccessTokenResolver) ResolveAccount(c *gin.Context) (*accounts.Account, error) {
	token, isCustomerKey, err := r.ResolveAccessToken(c)
	if err != nil {
		return nil, err
	}
	if isCustomerKey || token == "" {
		return nil, nil
	}

	// 区分 UUID 和 JWT
	if isValidUUID(token) {
		acct := accounts.NewAccount(uuid.NewString(), accounts.TypeNoAuth, token)
		return acct, nil
	}

	// JWT access_token (eyJhbGciOiJSUzI1NiI...) 或其它 → TypeFree
	acct := accounts.NewAccount(uuid.NewString(), accounts.TypeFree, token)
	return acct, nil
}

func splitAuthHeader(authHeader string) (string, string) {
	payload := strings.TrimSpace(authHeader)
	if len(payload) >= len("Bearer ") && strings.EqualFold(payload[:len("Bearer ")], "Bearer ") {
		payload = strings.TrimSpace(payload[len("Bearer "):])
	}
	parts := strings.SplitN(payload, ",", 2)
	token := strings.TrimSpace(parts[0])
	if len(parts) == 1 {
		return token, ""
	}
	return token, strings.TrimSpace(parts[1])
}

func isValidUUID(s string) bool {
	_, err := uuid.Parse(s)
	return err == nil
}

// ErrNoToken 表示请求未携带有效 token
var ErrNoToken = errors.New("no token in request")