You've already forked agentic-coding-workflow
feat(mcp): TokenService.Authenticate (hash lookup + revoked/expired check + async last_used)
This commit is contained in:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user