From 796e1e0d4c197ec591588166e0e675eff7d62795 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 8 May 2026 08:00:35 +0800 Subject: [PATCH] feat(mcp): TokenService.Authenticate (hash lookup + revoked/expired check + async last_used) --- internal/mcp/token_service.go | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/internal/mcp/token_service.go b/internal/mcp/token_service.go index ef949b4..9aa1690 100644 --- a/internal/mcp/token_service.go +++ b/internal/mcp/token_service.go @@ -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")