MCPcopy Create free account
hub / github.com/bytebase/bytebase / authMiddleware

Method authMiddleware

backend/api/mcp/server.go:93–173  ·  view source on GitHub ↗

authMiddleware validates OAuth2 bearer tokens for MCP requests. On 401, it emits an RFC 9728 / MCP-authorization-spec compliant WWW-Authenticate header pointing at the protected-resource-metadata URL. MCP clients (Claude Code, Cursor, etc.) use this header to bootstrap the OAuth flow without out-of

(next echo.HandlerFunc)

Source from the content-addressed store, hash-verified

91// MCP clients (Claude Code, Cursor, etc.) use this header to bootstrap the
92// OAuth flow without out-of-band configuration.
93func (s *Server) authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
94 return func(c *echo.Context) error {
95 // Extract Authorization header
96 authHeader := c.Request().Header.Get("Authorization")
97 if authHeader == "" {
98 return s.unauthorized(c, "authorization required")
99 }
100
101 // Validate Bearer format
102 parts := strings.Fields(authHeader)
103 if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
104 return s.unauthorized(c, "authorization header format must be Bearer {token}")
105 }
106 tokenStr := parts[1]
107
108 // Parse and validate JWT
109 claims := jwt.MapClaims{}
110 token, err := jwt.ParseWithClaims(tokenStr, claims, func(t *jwt.Token) (any, error) {
111 if t.Method.Alg() != jwt.SigningMethodHS256.Name {
112 return nil, errors.New("invalid token signing method")
113 }
114 if kid, ok := t.Header["kid"].(string); ok && kid == "v1" {
115 return []byte(s.secret), nil
116 }
117 return nil, errors.New("invalid token key id")
118 })
119 if err != nil {
120 if strings.Contains(err.Error(), "expired") {
121 return s.unauthorized(c, "token expired")
122 }
123 return s.unauthorized(c, "invalid token")
124 }
125
126 if !token.Valid {
127 return s.unauthorized(c, "invalid token")
128 }
129
130 // Validate audience - accept both user access tokens and OAuth2 access tokens
131 aud, ok := claims["aud"]
132 if !ok {
133 return s.unauthorized(c, "invalid token: missing audience")
134 }
135
136 validAudience := false
137 switch v := aud.(type) {
138 case string:
139 validAudience = v == auth.OAuth2AccessTokenAudience || v == auth.AccessTokenAudience
140 case []any:
141 for _, a := range v {
142 if str, ok := a.(string); ok && (str == auth.OAuth2AccessTokenAudience || str == auth.AccessTokenAudience) {
143 validAudience = true
144 break
145 }
146 }
147 default:
148 }
149 if !validAudience {
150 return s.unauthorized(c, "invalid token: audience mismatch")

Callers 2

TestMCPAuthMiddlewareFunction · 0.95

Calls 6

unauthorizedMethod · 0.95
withAccessTokenFunction · 0.85
withWorkspaceIDFunction · 0.85
GetMethod · 0.80
nextFunction · 0.50
ErrorMethod · 0.45

Tested by 2

TestMCPAuthMiddlewareFunction · 0.76