verae-mcp-timestamping-serv.../internal/tools/bearer.go
George Lambert 369f9210bd
Some checks failed
test-and-deploy / test (push) Failing after 12m11s
test-and-deploy / deploy-138 (push) Has been cancelled
fix(auth): clear public write errors and Grok client path
Public DEC-654 still requires a caller bearer; anonymous session now
returns that guidance as a tool error instead of silent valid:false.
Error text names JWT and API key, Grok bearer_token_file, and local
stdio. BearerFromRequest also accepts X-Api-Key / X-Verae-Key.
Document Grok stdio vs public URL; add scripts/sync-mcp-bearer.sh.
2026-10-12 14:09:22 -04:00

77 lines
2 KiB
Go

package tools
import (
"encoding/json"
"strings"
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/pkg/digest"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// BearerFromRequest extracts the caller credential from the MCP HTTP request.
// Order: Authorization: Bearer, then X-Api-Key / X-Verae-Key (raw token).
func BearerFromRequest(req *mcp.CallToolRequest) string {
if req == nil || req.Extra == nil || req.Extra.Header == nil {
return ""
}
h := req.Extra.Header
if tok := bearerFromAuthHeader(h.Get("Authorization")); tok != "" {
return tok
}
// Some gateways forward the key under these names instead of Authorization.
for _, name := range []string{"X-Api-Key", "X-Verae-Key", "X-Verae-Token"} {
if tok := strings.TrimSpace(h.Get(name)); tok != "" {
return tok
}
}
return ""
}
func bearerFromAuthHeader(auth string) string {
auth = strings.TrimSpace(auth)
if auth == "" {
return ""
}
const prefix = "bearer "
if len(auth) > len(prefix) && strings.EqualFold(auth[:len(prefix)], prefix) {
return strings.TrimSpace(auth[len(prefix):])
}
return ""
}
// assertDigestArgs rejects forbidden content/bytes keys on raw tool arguments.
func assertDigestArgs(req *mcp.CallToolRequest) error {
if req == nil || req.Params == nil || len(req.Params.Arguments) == 0 {
return nil
}
var args map[string]any
if err := json.Unmarshal(req.Params.Arguments, &args); err != nil {
return nil
}
return digest.AssertDigestOnly(args)
}
// looksLikeCID reports whether receiptID looks like a VeraeTime/IPFS CID
// rather than a local hex64 ledger receipt id.
func looksLikeCID(receiptID string) bool {
id := strings.TrimSpace(receiptID)
if id == "" {
return false
}
lower := strings.ToLower(id)
if strings.HasPrefix(lower, "qm") ||
strings.HasPrefix(lower, "bafy") ||
strings.HasPrefix(lower, "bafk") ||
strings.HasPrefix(lower, "bagu") {
return true
}
if len(id) == 64 {
for _, c := range lower {
if (c < '0' || c > '9') && (c < 'a' || c > 'f') {
return true
}
}
return false
}
return true
}