siyuan-note/siyuan · error
OAuth protected resource metadata has no authorization serve
Error message
OAuth protected resource metadata has no authorization server
What it means
Returned at oauth.go:506-507 when a protected-resource metadata document was successfully fetched but its authorization_servers array is empty. RFC 9728 requires authorization_servers to point at one or more AS issuers; SiYuan uses prm.AuthorizationServers[0] to look up AS metadata, so an empty list is fatal.
Source
Thrown at kernel/mcp/client/oauth.go:507
*oauthex.ProtectedResourceMetadata
MetadataURL string
}
func discoverProtectedResource(ctx context.Context, challenges []oauthex.Challenge, resource string, client *http.Client) (*discoveredProtectedResource, error) {
metadataURL := ""
for _, challenge := range challenges {
if strings.EqualFold(challenge.Scheme, "bearer") && challenge.Params["resource_metadata"] != "" {
metadataURL = challenge.Params["resource_metadata"]
break
}
}
for _, candidate := range protectedResourceURLs(metadataURL, resource) {
prm, err := oauthex.GetProtectedResourceMetadata(ctx, candidate.URL, candidate.Resource, client)
if err != nil {
continue
}
if len(prm.AuthorizationServers) == 0 {
return nil, fmt.Errorf("OAuth protected resource metadata has no authorization server")
}
return &discoveredProtectedResource{ProtectedResourceMetadata: prm, MetadataURL: candidate.URL}, nil
}
return nil, fmt.Errorf("OAuth protected resource metadata not found")
}
func (h *mcpOAuthHandler) validateCredentialIssuer(ctx context.Context, credential oauthCredential) (bool, error) {
var challenges []oauthex.Challenge
resource := h.server.URL
if credential.ResourceMetadataURL != "" {
challenges = []oauthex.Challenge{{Scheme: "bearer", Params: map[string]string{"resource_metadata": credential.ResourceMetadataURL}}}
resource = credential.Resource
}
prm, err := discoverProtectedResource(ctx, challenges, resource, h.client)
if err != nil {
return false, fmt.Errorf("validate OAuth protected resource: %w", err)
}
if prm.Resource != credential.Resource || len(prm.AuthorizationServers) == 0 {View on GitHub (pinned to 251596fc0d)
Solutions
- On the resource server, populate authorization_servers in /.well-known/oauth-protected-resource with the AS issuer URL(s).
- Verify the correct PRM document is being served (curl the candidate URL from protectedResourceURLs) and that it is valid JSON with the field present.
- If multiple AS issuers are listed, ensure the first one (AuthorizationServers[0]) is the intended one.
Defensive patterns
Strategy: validation
Prevention
- Publish authorization_servers in the /.well-known/oauth-protected-resource document.
- Validate the PRM document with curl before integrating an MCP server.
- Ensure the resource server's reverse proxy does not strip fields from the served JSON.
When it happens
Trigger: discoverProtectedResource fetches a candidate .well-known/oauth-protected-resource document, GetProtectedResourceMetadata returns no error, but the parsed JSON has authorization_servers missing or set to [].
Common situations: Resource server publishes a PRM document but forgot to populate authorization_servers; misconfigured reverse proxy serving a partial document; PRM generated from a template with the field left empty.
Related errors
- OAuth protected resource metadata not found
- discover OAuth authorization server: %w
- OAuth authorization server metadata not found
- validate OAuth protected resource: %w
- mcp oauth authorization required
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/8d3bef3dea18a893.
Report an issue: GitHub.