feat(mcp): TokenService.Authenticate (hash lookup + revoked/expired check + async last_used)

This commit is contained in:
2026-05-08 08:00:35 +08:00
parent 0081ad6e49
commit 796e1e0d4c
+34 -1
View File
@@ -180,8 +180,41 @@ func (s *tokenService) IssueSystemToken(ctx context.Context, in IssueSystemToken
ExpiresAt: created.ExpiresAt,
}, nil
}
// Authenticate 校验 plaintext token:
// 1. 计算 hash 反查 row
// 2. 检查 revoked_at IS NULL
// 3. 检查 expires_at IS NULL OR > now()
// 4. 异步触发 last_used_at 更新(不阻塞调用方;使用独立后台 ctx)
//
// 失败映射:
// - row 不存在 → CodeMcpTokenInvalid (401)
// - revoked_at 非空 → CodeMcpTokenRevoked (401)
// - expires_at 已过 → CodeMcpTokenExpired (401)
//
// 返回的 *MCPToken 不含 plaintext;调用方从中读 user_id + scope。
func (s *tokenService) Authenticate(ctx context.Context, plaintext string) (*MCPToken, error) {
panic("not implemented: Authenticate")
if strings.TrimSpace(plaintext) == "" {
return nil, errs.New(errs.CodeMcpTokenInvalid, "missing token")
}
tok, err := s.repo.GetByHash(ctx, HashToken(plaintext))
if err != nil {
return nil, err // 已在 repo 层映射为 CodeMcpTokenInvalid
}
if tok.RevokedAt != nil {
return nil, errs.New(errs.CodeMcpTokenRevoked, "token revoked")
}
if tok.ExpiresAt != nil && !tok.ExpiresAt.After(time.Now()) {
return nil, errs.New(errs.CodeMcpTokenExpired, "token expired")
}
// 异步更新 last_used_at,不阻塞调用方;使用独立 ctx 防 request 取消
go func(id uuid.UUID) {
bg, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = s.repo.UpdateLastUsed(bg, id)
}(tok.ID)
return tok, nil
}
func (s *tokenService) Revoke(ctx context.Context, id, byUserID uuid.UUID) error {
panic("not implemented: Revoke")