{"record":{"id":"8d3bef3dea18a893","repo":"siyuan-note/siyuan","slug":"oauth-protected-resource-metadata-has-no-authoriza","errorCode":null,"errorMessage":"OAuth protected resource metadata has no authorization server","messagePattern":"OAuth protected resource metadata has no authorization server","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":507,"sourceCode":"\t*oauthex.ProtectedResourceMetadata\n\tMetadataURL string\n}\n\nfunc discoverProtectedResource(ctx context.Context, challenges []oauthex.Challenge, resource string, client *http.Client) (*discoveredProtectedResource, error) {\n\tmetadataURL := \"\"\n\tfor _, challenge := range challenges {\n\t\tif strings.EqualFold(challenge.Scheme, \"bearer\") && challenge.Params[\"resource_metadata\"] != \"\" {\n\t\t\tmetadataURL = challenge.Params[\"resource_metadata\"]\n\t\t\tbreak\n\t\t}\n\t}\n\tfor _, candidate := range protectedResourceURLs(metadataURL, resource) {\n\t\tprm, err := oauthex.GetProtectedResourceMetadata(ctx, candidate.URL, candidate.Resource, client)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif len(prm.AuthorizationServers) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"OAuth protected resource metadata has no authorization server\")\n\t\t}\n\t\treturn &discoveredProtectedResource{ProtectedResourceMetadata: prm, MetadataURL: candidate.URL}, nil\n\t}\n\treturn nil, fmt.Errorf(\"OAuth protected resource metadata not found\")\n}\n\nfunc (h *mcpOAuthHandler) validateCredentialIssuer(ctx context.Context, credential oauthCredential) (bool, error) {\n\tvar challenges []oauthex.Challenge\n\tresource := h.server.URL\n\tif credential.ResourceMetadataURL != \"\" {\n\t\tchallenges = []oauthex.Challenge{{Scheme: \"bearer\", Params: map[string]string{\"resource_metadata\": credential.ResourceMetadataURL}}}\n\t\tresource = credential.Resource\n\t}\n\tprm, err := discoverProtectedResource(ctx, challenges, resource, h.client)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"validate OAuth protected resource: %w\", err)\n\t}\n\tif prm.Resource != credential.Resource || len(prm.AuthorizationServers) == 0 {","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/oauth.go#L489-L525","documentation":"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.","triggerScenarios":"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 [].","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["oauth","mcp","resource-metadata","rfc9728","discovery","authorization-servers"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}