{"record":{"id":"aecc6af639ce4400","repo":"siyuan-note/siyuan","slug":"validate-oauth-issuer-w","errorCode":null,"errorMessage":"validate OAuth issuer: %w","messagePattern":"validate OAuth issuer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":530,"sourceCode":"}\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 {\n\t\treturn false, nil\n\t}\n\tasm, err := auth.GetAuthServerMetadata(ctx, prm.AuthorizationServers[0], h.client)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"validate OAuth issuer: %w\", err)\n\t}\n\treturn asm != nil && asm.Issuer == credential.Issuer && asm.TokenEndpoint == credential.TokenEndpoint, nil\n}\n\nfunc protectedResourceURLs(metadataURL, resource string) []protectedResourceURL {\n\tvar result []protectedResourceURL\n\tif metadataURL != \"\" {\n\t\tresult = append(result, protectedResourceURL{URL: metadataURL, Resource: resource})\n\t}\n\tresourceURL, err := url.Parse(resource)\n\tif err != nil {\n\t\treturn result\n\t}\n\tmetadata := *resourceURL\n\tmetadata.RawPath = \"\"\n\tmetadata.RawQuery = \"\"\n\tmetadata.Fragment = \"\"\n\tmetadata.Path = \"/.well-known/oauth-protected-resource/\" + strings.TrimLeft(resourceURL.Path, \"/\")","sourceCodeStart":512,"sourceCodeEnd":548,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/oauth.go#L512-L548","documentation":"validateCredentialIssuer wraps a failure from auth.GetAuthServerMetadata (fetching the authorization server's RFC 8414 metadata) with the 'validate OAuth issuer' prefix. The protected resource metadata was fetched fine, but its first listed authorization server's metadata could not be retrieved, so the stored issuer/token endpoint could not be compared.","triggerScenarios":"TokenSource -> validateCredentialIssuer. Thrown when GetAuthServerMetadata against prm.AuthorizationServers[0] errors: the authorization server's /.well-known/oauth-authorization-server endpoint is unreachable, 404s, returns malformed JSON, or TLS fails.","commonSituations":"The authorization server (IdP) is down or migrated to a new issuer URL; the resource metadata lists a stale authorization-server URL; corporate proxy blocks the IdP domain; IdP disabled its well-known discovery endpoint.","solutions":["Check that the authorization server URL from the resource metadata is reachable and publishes its metadata well-known document.","Verify the IdP issuer has not changed after an upgrade; if it has, re-run the OAuth authorize flow to store the new issuer.","Retry on transient network errors — the credential remains stored and valid once the IdP is back.","If the resource metadata lists multiple authorization servers, note only the first is tried; update server config to list the intended one first.","Inspect the wrapped error for the underlying HTTP/TLS cause."],"exampleFix":"// before: metadata lists stale IdP\n{\"authorization_servers\": [\"https://old-idp.example.com\"]}\n// after: update resource metadata to current issuer\n{\"authorization_servers\": [\"https://auth.example.com\"]}","handlingStrategy":"retry","validationCode":"asm, err := auth.GetAuthServerMetadata(ctx, authServerURL, client)\nif err != nil {\n    // authorization server unreachable; check IdP health before revalidation\n}","typeGuard":null,"tryCatchPattern":"asm, err := auth.GetAuthServerMetadata(ctx, prm.AuthorizationServers[0], h.client)\nif err != nil {\n    log.Warn(\"issuer revalidation deferred\", \"err\", err)\n    return true, nil // keep credential, revalidate later\n}","preventionTips":["Monitor IdP availability; revalidation depends on its well-known endpoint","Pin and periodically verify the issuer URL against the IdP's published metadata","Update resource metadata promptly when authorization servers migrate","Keep only the intended authorization server first in the metadata list"],"tags":["oauth","mcp","discovery","network"],"backgroundTag":"http-request-failed","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}