Skip to content

Server requires authentication but doesn't advertise the auth mechanism #604

@clgtm

Description

@clgtm

Looks like the WWW-Authenticate header is missing.

Command:   authprobe scan --llm-max-tokens=1080 --openai-api-key=***REDACTED*** http://localhost:8019/mcp
Scanning:  http://localhost:8019/mcp
Scan time: Feb 20, 2026 06:45:16 UTC
Github:    https://github.com/authprobe/authprobe

Funnel
  [1] MCP probe (401 + WWW-Authenticate)      [-] SKIP
        auth appears required (initialize returned 401/403)

  [2] MCP initialize + tools/list             [X] FAIL
        initialize -> 401 (error: Unauthorized)

  [3] PRM fetch matrix                        [X] FAIL
        PRM unreachable or unusable; OAuth discovery unavailable

  [4] Auth server metadata                    [-] SKIP
        no authorization_servers in PRM

  [5] Token endpoint readiness (heuristics)   [-] SKIP
        no token_endpoint in metadata

  [6] Dynamic client registration (RFC 7591)  [-] SKIP
        no registration_endpoint in metadata

Primary Finding (HIGH): AUTH_REQUIRED_BUT_NOT_ADVERTISED (confidence 1.00)
  Evidence:
      initialize -> 401
      initialize error: Unauthorized
      WWW-Authenticate: (missing)
      PRM unreachable or unusable; OAuth discovery unavailable
      Auth appears required but OAuth discovery was not advertised. Next steps: add
      WWW-Authenticate + PRM for OAuth/MCP discovery, or document the required non-OAuth auth
      (e.g., SigV4).

┌───────────────────────┤ CALL TRACE ├───────────────────────┐
Call Trace Using: https://github.com/authprobe/authprobe

  ┌────────────┐                                                    ┌────────────┐    
  │ authprobe  │                                                    │ MCP Server │    
  └─────┬──────┘                                                    └─────┬──────┘    
        │                                                                 │           
        │ ╔═══ Step 1: MCP probe                    ═══════╪═══════════════════╗
        │  GET http://localhost:8019/mcp                                 
        │  Reason: 401 + WWW-Authenticate discovery                      
        │    Accept:  text/event-stream
        │    Host:    localhost:8019
        ├─────────────────────────────────────────────────────────────────►│
        │  200 OK                                                        
        │    Access-Control-Allow-Headers:   Content-Type, Authorization, Accept, Mcp-Session-Id
        │    Access-Control-Allow-Methods:   POST, GET, DELETE, OPTIONS
        │    Access-Control-Allow-Origin:    *
        │    Access-Control-Expose-Headers:  Mcp-Session-Id
        │    Access-Control-Max-Age:         86400
        │    Cache-Control:                  no-cache, no-transform
        │    Connection:                     keep-alive
        │    Content-Type:                   text/event-stream
        │    Date:                           Fri, 20 Feb 2026 06:45:08 GMT
        │    Strict-Transport-Security:      max-age=31536000; includeSubDomains
        │    X-Content-Type-Options:         nosniff
        │    X-Frame-Options:                DENY
        │    X-Powered-By:                   Express
        │    X-Xss-Protection:               1; mode=block
        │◄─────────────────────────────────────────────────────────────────┤
        │                                                                  │
        │ ╔═══ Step 2: MCP initialize               ═══════╪═══════════════════╗
        │  POST http://localhost:8019/mcp                                
        │  Reason: Step 2: MCP initialize + tools/list (pre-init tools/list)
        │    Accept:                application/json, text/event-stream
        │    Content-Type:          application/json
        │    Host:                  localhost:8019
        │    Mcp-Protocol-Version:  2025-11-25
        ├─────────────────────────────────────────────────────────────────►│
        │  401 Unauthorized                                              
        │    Access-Control-Allow-Headers:   Content-Type, Authorization, Accept, Mcp-Session-Id
        │    Access-Control-Allow-Methods:   POST, GET, DELETE, OPTIONS
        │    Access-Control-Allow-Origin:    *
        │    Access-Control-Expose-Headers:  Mcp-Session-Id
        │    Access-Control-Max-Age:         86400
        │    Connection:                     keep-alive
        │    Content-Length:                 76
        │    Content-Type:                   application/json; charset=utf-8
        │    Date:                           Fri, 20 Feb 2026 06:45:16 GMT
        │    Etag:                           W/"4c-qMAtz4tDjU5Eupw6k4HTOkiq+do"
        │    Keep-Alive:                     timeout=5
        │    Ratelimit-Limit:                20
        │    Ratelimit-Policy:               20;w=900
        │    Ratelimit-Remaining:            19
        │    Ratelimit-Reset:                900
        │    Strict-Transport-Security:      max-age=31536000; includeSubDomains
        │    X-Content-Type-Options:         nosniff
        │    X-Frame-Options:                DENY
        │    X-Powered-By:                   Express
        │    X-Xss-Protection:               1; mode=block
        │◄─────────────────────────────────────────────────────────────────┤
        │                                                                  │
        │  POST http://localhost:8019/mcp                                
        │  Reason: Step 2: MCP initialize + tools/list (initialize)      
        │    Accept:                application/json, text/event-stream
        │    Content-Type:          application/json
        │    Host:                  localhost:8019
        │    Mcp-Protocol-Version:  2025-11-25
        ├─────────────────────────────────────────────────────────────────►│
        │  401 Unauthorized                                              
        │    Access-Control-Allow-Headers:   Content-Type, Authorization, Accept, Mcp-Session-Id
        │    Access-Control-Allow-Methods:   POST, GET, DELETE, OPTIONS
        │    Access-Control-Allow-Origin:    *
        │    Access-Control-Expose-Headers:  Mcp-Session-Id
        │    Access-Control-Max-Age:         86400
        │    Connection:                     keep-alive
        │    Content-Length:                 76
        │    Content-Type:                   application/json; charset=utf-8
        │    Date:                           Fri, 20 Feb 2026 06:45:16 GMT
        │    Etag:                           W/"4c-qMAtz4tDjU5Eupw6k4HTOkiq+do"
        │    Keep-Alive:                     timeout=5
        │    Ratelimit-Limit:                20
        │    Ratelimit-Policy:               20;w=900
        │    Ratelimit-Remaining:            18
        │    Ratelimit-Reset:                900
        │    Strict-Transport-Security:      max-age=31536000; includeSubDomains
        │    X-Content-Type-Options:         nosniff
        │    X-Frame-Options:                DENY
        │    X-Powered-By:                   Express
        │    X-Xss-Protection:               1; mode=block
        │◄─────────────────────────────────────────────────────────────────┤
        ▼                                                                  ▼

┌──────────────────┤ ROOT-CAUSE ANALYSIS ├───────────────────┐

Primary Finding: AUTH_REQUIRED_BUT_NOT_ADVERTISED (High Confidence, High Priority)

Summary of the Finding

The MCP OAuth server responded to an initialize request (Step 2) with HTTP 401 Unauthorized, indicating that authorization is required, yet it did not include a WWW-Authenticate header in the response. In addition, the Protected Resource Metadata (PRM) discovery endpoint was unreachable or returned no usable OAuth metadata (Step 3). This absence of both an authentication challenge (WWW-Authenticate) and OAuth authorization server metadata constitutes a fundamental violation of the OAuth and MCP protocol expectations, resulting in an unsupported authentication flow from the client perspective.


Why This Failure Is Valid and Justified

Relevant Specifications and Requirements

  • MCP 2025-11-25:

    • Mandates that when a resource endpoint requires authorization, the server must advertise the authorization method using a WWW-Authenticate header on a 401 Unauthorized response. This allows clients to understand how to authenticate.
    • Requires OAuth discovery metadata (PRM) availability to allow clients to dynamically obtain OAuth endpoints (authorization, token, etc.) necessary for interaction.
  • RFC 8414 (OAuth 2.0 Authorization Server Metadata):

    • Specifies that OAuth endpoints must be discoverable via metadata at a well-known URI (PRM).
    • This metadata enables clients to understand OAuth server capabilities and endpoints, which is critical for dynamic client registration and authorization flows.
    • Without this metadata, clients cannot initiate or continue OAuth authorization.
  • RFC 9728 (OAuth Security Best Practices):

    • Requires that WWW-Authenticate header must be present with the proper OAuth2 scheme or other defined schemes upon a 401 Unauthorized response to enable clients to identify the authorization requirements.
    • Servers should not respond with 401 Unauthorized without indicating the required authentication challenge.
  • JSON-RPC 2.0:

    • While the underlying transport here is HTTP, JSON-RPC 2.0 expects appropriate transport-layer authentication to be handled correctly to ensure valid client-server communication.

Explanation of the Failure in Context

  • The server's initialize endpoint responds with HTTP 401 Unauthorized, meaning the resource requires authentication. However, it fails to provide a WWW-Authenticate header specifying the authentication scheme (e.g., Bearer token for OAuth). This leaves clients unable to determine the method to obtain authorization credentials.

  • Attempts to locate OAuth authorization server metadata (PRM) fail because the discovery endpoint is unreachable or returns no valid OAuth data. This means there is no advertised OAuth authorization or token endpoint metadata available, preventing dynamic OAuth flows.

  • The combination of these two failures means:

    1. Clients see the resource is protected (401) but have no indication of how to authenticate.
    2. Clients cannot discover OAuth server metadata endpoints to request tokens or perform OAuth interactions.
  • This violates MCP and OAuth best practices because authentication requirement indications and OAuth discovery must be advertised together and coherently for clients to succeed.


What Should Be Changed — Correct Server Behavior

  1. Include a WWW-Authenticate Header in 401 Responses

    • Per RFC 9728 Section 3, upon responding with 401 Unauthorized indicating OAuth 2.0 bearer token authentication is required, the server must include a header like:
      WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token is missing or invalid"
      
    • Per MCP 2025-11-25, this header must be present to signal to clients the type of auth expected.
  2. Expose OAuth Authorization Server Metadata via PRM

    • Per RFC 8414, implement a publicly reachable Protected Resource Metadata endpoint under .well-known/oauth-protected-resource (PRM), which advertises:
      • authorization_servers
      • token_endpoint
      • registration_endpoint (if dynamic registration is supported)
    • These endpoints must be advertised over HTTPS URLs to meet transport security requirements (see below).
  3. Serve All Endpoints over HTTPS with Absolute URLs

    • MCP and OAuth metadata must use absolute URLs beginning with https:// for all endpoint URLs (per RFC 3986 and MCP 2025-11-25).
    • Current evidence shows http://localhost URLs used, which violates this requirement and can cause clients to malfunction or reject metadata.
  4. Document Non-OAuth Auth If Used

    • If the server does not offer OAuth authorization and uses an alternative authentication scheme (e.g., AWS SigV4), it must document this clearly in metadata and include an appropriate WWW-Authenticate header describing the scheme, per MCP authorization requirements.

Summary of Correct Server Behavior Example

  • Respond to unauthorized requests with:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions