or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

auth.mdindex.mdjsonrpc.mdmcp-client.mdmcp-protocol.mdmcp-server.mdmcp-transports.mdmcp-types.mdoauthex.md
tile.json

oauthex.mddocs/

OAuth Extensions

The oauthex package implements OAuth 2.0 extensions, including Protected Resource Metadata (RFC 9728), Authorization Server Metadata (RFC 8414), and Dynamic Client Registration (RFC 7591).

Overview

This package provides types and utilities for OAuth 2.0, enabling both servers and clients to work with OAuth 2.0 protected resources:

  • Server-side: Publish protected resource metadata to help clients discover authentication requirements
  • Client-side: Discover metadata, retrieve authorization server information, and register clients dynamically

Build Tag Note: Client-side functions (metadata discovery, authorization server metadata, dynamic client registration) require building with the mcp_go_client_oauth build tag. Server-side functionality does not require special build tags.

Package Information

import "github.com/modelcontextprotocol/go-sdk/oauthex"

Protected Resource Metadata

ProtectedResourceMetadata

Metadata for an OAuth 2.0 protected resource as defined in RFC 9728.

type ProtectedResourceMetadata struct {
    Resource                              string   `json:"resource"`
    AuthorizationServers                  []string `json:"authorization_servers,omitempty"`
    JWKSURI                               string   `json:"jwks_uri,omitempty"`
    ScopesSupported                       []string `json:"scopes_supported,omitempty"`
    BearerMethodsSupported                []string `json:"bearer_methods_supported,omitempty"`
    ResourceSigningAlgValuesSupported     []string `json:"resource_signing_alg_values_supported,omitempty"`
    ResourceName                          string   `json:"resource_name,omitempty"`
    ResourceDocumentation                 string   `json:"resource_documentation,omitempty"`
    ResourcePolicyURI                     string   `json:"resource_policy_uri,omitempty"`
    ResourceTOSURI                        string   `json:"resource_tos_uri,omitempty"`
    TLSClientCertificateBoundAccessTokens bool     `json:"tls_client_certificate_bound_access_tokens,omitempty"`
    AuthorizationDetailsTypesSupported    []string `json:"authorization_details_types_supported,omitempty"`
    DPOPSigningAlgValuesSupported         []string `json:"dpop_signing_alg_values_supported,omitempty"`
    DPOPBoundAccessTokensRequired         bool     `json:"dpop_bound_access_tokens_required,omitempty"`
}

Fields:

  • Resource: Protected resource's resource identifier (required)
  • AuthorizationServers: List of OAuth authorization server issuer identifiers (RFC 8414)
  • JWKSURI: URL of the protected resource's JSON Web Key Set document
  • ScopesSupported: Scope values used in authorization requests for this resource (recommended)
  • BearerMethodsSupported: Methods for sending bearer tokens ("header", "body", "query")
  • ResourceSigningAlgValuesSupported: JWS signing algorithms for resource responses
  • ResourceName: Human-readable name for display to end users
  • ResourceDocumentation: URL of developer documentation
  • ResourcePolicyURI: URL of usage policy information
  • ResourceTOSURI: URL of terms of service
  • TLSClientCertificateBoundAccessTokens: Whether mutual-TLS bound tokens are supported (default: false)
  • AuthorizationDetailsTypesSupported: Supported 'type' values for authorization_details (RFC 9396)
  • DPOPSigningAlgValuesSupported: JWS algorithms for validating DPoP proof JWTs (RFC 9449)
  • DPOPBoundAccessTokensRequired: Whether DPoP-bound tokens are always required (default: false)

Example metadata:

{
  "resource": "https://api.example.com",
  "authorization_servers": [
    "https://auth.example.com"
  ],
  "scopes_supported": [
    "mcp:read",
    "mcp:write",
    "mcp:admin"
  ],
  "bearer_methods_supported": [
    "header"
  ],
  "resource_name": "Example MCP Server",
  "resource_documentation": "https://api.example.com/docs"
}

Publishing Protected Resource Metadata

When implementing an OAuth-protected MCP server, publish metadata at the well-known location:

/.well-known/oauth-protected-resource

or at a custom location specified in the WWW-Authenticate header.

Example server implementation:

package main

import (
    "encoding/json"
    "net/http"
    "github.com/modelcontextprotocol/go-sdk/oauthex"
)

func main() {
    // Define protected resource metadata
    metadata := &oauthex.ProtectedResourceMetadata{
        Resource: "https://api.example.com",
        AuthorizationServers: []string{
            "https://auth.example.com",
        },
        ScopesSupported: []string{
            "mcp:read",
            "mcp:write",
        },
        BearerMethodsSupported: []string{"header"},
        ResourceName:           "Example MCP API",
        ResourceDocumentation:  "https://api.example.com/docs",
    }

    // Serve metadata at well-known location
    http.HandleFunc("/.well-known/oauth-protected-resource", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(metadata)
    })

    // Serve MCP endpoints with authentication
    // ... (see auth.md for authentication setup)

    http.ListenAndServe(":8080", nil)
}

Client-Side Metadata Discovery

Note: Functions in this section require the mcp_go_client_oauth build tag.

GetProtectedResourceMetadataFromID

Retrieves protected resource metadata from a resource server by its ID.

func GetProtectedResourceMetadataFromID(
    ctx context.Context,
    resourceID string,
    c *http.Client,
) (*ProtectedResourceMetadata, error)

Parameters:

  • ctx: Context for the HTTP request
  • resourceID: The resource ID (HTTPS URL of the resource server)
  • c: HTTP client to use (uses http.DefaultClient if nil)

Returns:

  • *ProtectedResourceMetadata: Retrieved metadata
  • error: Error if retrieval or validation fails

Behavior:

  • Constructs well-known URI by inserting /.well-known/oauth-protected-resource into the resource ID path
  • Issues GET request to retrieve metadata
  • Validates that the metadata's resource field matches the resource ID
  • Returns error if resource field mismatch (security violation)

Example:

import (
    "context"
    "net/http"
    "github.com/modelcontextprotocol/go-sdk/oauthex"
)

func discoverFromID(ctx context.Context) (*oauthex.ProtectedResourceMetadata, error) {
    // For resource https://api.example.com/server
    // Fetches from https://api.example.com/.well-known/oauth-protected-resource/server
    metadata, err := oauthex.GetProtectedResourceMetadataFromID(
        ctx,
        "https://api.example.com/server",
        nil, // use default HTTP client
    )
    if err != nil {
        return nil, err
    }

    // Use metadata for OAuth flow
    authServer := metadata.AuthorizationServers[0]
    scopes := metadata.ScopesSupported
    // ... configure OAuth client

    return metadata, nil
}

GetProtectedResourceMetadataFromHeader

Retrieves protected resource metadata using information from WWW-Authenticate headers.

func GetProtectedResourceMetadataFromHeader(
    ctx context.Context,
    serverURL string,
    header http.Header,
    c *http.Client,
) (*ProtectedResourceMetadata, error)

Parameters:

  • ctx: Context for the HTTP request
  • serverURL: The URL the client used to access the resource server
  • header: HTTP headers from the 401 Unauthorized response
  • c: HTTP client to use (uses http.DefaultClient if nil)

Returns:

  • *ProtectedResourceMetadata: Retrieved metadata, or nil if no metadata URL in header
  • error: Error if retrieval or validation fails

Behavior:

  • Parses WWW-Authenticate headers to find resource_metadata URL
  • Returns (nil, nil) if no metadata URL found
  • Issues GET request to retrieve metadata
  • Validates that metadata's resource field matches serverURL
  • Validates authorization server URLs to prevent XSS attacks

Example:

import (
    "context"
    "net/http"
    "github.com/modelcontextprotocol/go-sdk/oauthex"
)

func handleUnauthorized(ctx context.Context, serverURL string, resp *http.Response) error {
    // Get metadata from 401 response headers
    metadata, err := oauthex.GetProtectedResourceMetadataFromHeader(
        ctx,
        serverURL,
        resp.Header,
        nil,
    )
    if err != nil {
        return err
    }

    if metadata == nil {
        // No metadata available, use fallback authentication
        return nil
    }

    // Discover authorization server and initiate OAuth flow
    authServerURL := metadata.AuthorizationServers[0]
    requiredScopes := metadata.ScopesSupported
    // ... perform OAuth flow

    return nil
}

ParseWWWAuthenticate

Parses WWW-Authenticate header strings into authentication challenges.

func ParseWWWAuthenticate(headers []string) ([]challenge, error)

Parameters:

  • headers: Slice of WWW-Authenticate header values

Returns:

  • []challenge: Parsed challenges (internal type, not exported)
  • error: Error if header is malformed

Behavior:

  • Parses per RFC 9110, Section 11.6.1
  • Handles multiple challenges separated by commas
  • Correctly handles commas within quoted strings
  • Keys are case-insensitive (returned lowercase)

Note: This is a lower-level function. Typically use GetProtectedResourceMetadataFromHeader instead, which calls this internally.

ResourceMetadataURL

Extracts the resource metadata URL from parsed authentication challenges.

func ResourceMetadataURL(cs []challenge) string

Parameters:

  • cs: Slice of parsed challenges

Returns:

  • string: Resource metadata URL, or empty string if not found

Behavior:

  • Searches challenges for resource_metadata parameter
  • Returns the first matching URL found

Note: This is a lower-level function. Typically use GetProtectedResourceMetadataFromHeader instead, which calls this internally.

Authorization Server Metadata

Note: Functions and types in this section require the mcp_go_client_oauth build tag.

AuthServerMeta

Metadata for an OAuth 2.0 authorization server as defined in RFC 8414.

type AuthServerMeta struct {
    Issuer                                         string   `json:"issuer"`
    AuthorizationEndpoint                          string   `json:"authorization_endpoint"`
    TokenEndpoint                                  string   `json:"token_endpoint"`
    JWKSURI                                        string   `json:"jwks_uri"`
    RegistrationEndpoint                           string   `json:"registration_endpoint,omitempty"`
    ScopesSupported                                []string `json:"scopes_supported,omitempty"`
    ResponseTypesSupported                         []string `json:"response_types_supported"`
    ResponseModesSupported                         []string `json:"response_modes_supported,omitempty"`
    GrantTypesSupported                            []string `json:"grant_types_supported,omitempty"`
    TokenEndpointAuthMethodsSupported              []string `json:"token_endpoint_auth_methods_supported,omitempty"`
    TokenEndpointAuthSigningAlgValuesSupported     []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`
    ServiceDocumentation                           string   `json:"service_documentation,omitempty"`
    UILocalesSupported                             []string `json:"ui_locales_supported,omitempty"`
    OpPolicyURI                                    string   `json:"op_policy_uri,omitempty"`
    OpTOSURI                                       string   `json:"op_tos_uri,omitempty"`
    RevocationEndpoint                             string   `json:"revocation_endpoint,omitempty"`
    RevocationEndpointAuthMethodsSupported         []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`
    RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`
    IntrospectionEndpoint                          string   `json:"introspection_endpoint,omitempty"`
    IntrospectionEndpointAuthMethodsSupported      []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`
    IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"`
    CodeChallengeMethodsSupported                  []string `json:"code_challenge_methods_supported,omitempty"`
}

Fields:

Required fields:

  • Issuer: Authorization server identifier URL
  • AuthorizationEndpoint: OAuth 2.0 authorization endpoint URL
  • TokenEndpoint: OAuth 2.0 token endpoint URL
  • JWKSURI: JSON Web Key Set document URL
  • ResponseTypesSupported: Supported response_type values

Recommended fields:

  • RegistrationEndpoint: Dynamic Client Registration endpoint URL
  • ScopesSupported: Supported OAuth 2.0 scope values
  • ResponseModesSupported: Supported response_mode values
  • GrantTypesSupported: Supported OAuth 2.0 grant types
  • CodeChallengeMethodsSupported: Supported PKCE challenge methods

Optional fields:

  • Authentication methods and signing algorithms for token/revocation/introspection endpoints
  • ServiceDocumentation: Developer documentation URL
  • UILocalesSupported: Supported BCP47 language tags
  • OpPolicyURI: Operator policy URL
  • OpTOSURI: Terms of service URL
  • RevocationEndpoint: Token revocation endpoint URL
  • IntrospectionEndpoint: Token introspection endpoint URL

GetAuthServerMeta

Retrieves authorization server metadata from an OAuth authorization server.

func GetAuthServerMeta(
    ctx context.Context,
    issuerURL string,
    c *http.Client,
) (*AuthServerMeta, error)

Parameters:

  • ctx: Context for the HTTP request
  • issuerURL: The authorization server's issuer URL
  • c: HTTP client to use (uses http.DefaultClient if nil)

Returns:

  • *AuthServerMeta: Retrieved authorization server metadata
  • error: Error if retrieval, validation, or PKCE check fails

Behavior:

  • Tries well-known paths per RFC 8414:
    • /.well-known/oauth-authorization-server
    • /.well-known/openid-configuration
  • Uses first successful response
  • Validates that Issuer field matches issuerURL
  • Validates that server implements PKCE (CodeChallengeMethodsSupported not empty)
  • Returns error if issuer mismatch or PKCE not supported

Example:

import (
    "context"
    "github.com/modelcontextprotocol/go-sdk/oauthex"
    "golang.org/x/oauth2"
)

func setupOAuthClient(ctx context.Context, authServerURL string) (*oauth2.Config, error) {
    // Fetch authorization server metadata
    metadata, err := oauthex.GetAuthServerMeta(ctx, authServerURL, nil)
    if err != nil {
        return nil, err
    }

    // Configure OAuth 2.0 client using discovered endpoints
    config := &oauth2.Config{
        ClientID:     "your-client-id",
        ClientSecret: "your-client-secret",
        Endpoint: oauth2.Endpoint{
            AuthURL:  metadata.AuthorizationEndpoint,
            TokenURL: metadata.TokenEndpoint,
        },
        Scopes: []string{"mcp:read", "mcp:write"},
    }

    return config, nil
}

Dynamic Client Registration

Note: Functions and types in this section require the mcp_go_client_oauth build tag.

Dynamic Client Registration (DCR) allows clients to register themselves with an authorization server programmatically per RFC 7591.

ClientRegistrationMetadata

Client metadata for the DCR POST request.

type ClientRegistrationMetadata struct {
    RedirectURIs            []string `json:"redirect_uris"`
    TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method,omitempty"`
    GrantTypes              []string `json:"grant_types,omitempty"`
    ResponseTypes           []string `json:"response_types,omitempty"`
    ClientName              string   `json:"client_name,omitempty"`
    ClientURI               string   `json:"client_uri,omitempty"`
    LogoURI                 string   `json:"logo_uri,omitempty"`
    Scope                   string   `json:"scope,omitempty"`
    Contacts                []string `json:"contacts,omitempty"`
    TOSURI                  string   `json:"tos_uri,omitempty"`
    PolicyURI               string   `json:"policy_uri,omitempty"`
    JWKSURI                 string   `json:"jwks_uri,omitempty"`
    JWKS                    string   `json:"jwks,omitempty"`
    SoftwareID              string   `json:"software_id,omitempty"`
    SoftwareVersion         string   `json:"software_version,omitempty"`
    SoftwareStatement       string   `json:"software_statement,omitempty"`
}

Fields:

Required:

  • RedirectURIs: Redirection URI strings for redirect-based flows

Optional (with defaults):

  • TokenEndpointAuthMethod: Authentication method for token endpoint (default: "client_secret_basic")
  • GrantTypes: OAuth 2.0 grant types (default: ["authorization_code"])
  • ResponseTypes: OAuth 2.0 response types (default: ["code"])

Recommended:

  • ClientName: Human-readable client name
  • ClientURI: Web page with client information

Optional:

  • LogoURI: Logo URL for display
  • Scope: Space-separated scope values
  • Contacts: Contact email addresses
  • TOSURI: Terms of service URL
  • PolicyURI: Privacy policy URL
  • JWKSURI: JSON Web Key Set URL
  • JWKS: JSON Web Key Set passed by value
  • SoftwareID: Unique identifier for client software
  • SoftwareVersion: Version identifier for client software
  • SoftwareStatement: JWT asserting client metadata

ClientRegistrationResponse

Response from the authorization server after successful registration.

type ClientRegistrationResponse struct {
    ClientRegistrationMetadata
    ClientID              string    `json:"client_id"`
    ClientSecret          string    `json:"client_secret,omitempty"`
    ClientIDIssuedAt      time.Time `json:"client_id_issued_at,omitempty"`
    ClientSecretExpiresAt time.Time `json:"client_secret_expires_at,omitempty"`
}

Fields:

  • ClientRegistrationMetadata: All registered metadata (embedded, may contain modified/defaulted values)
  • ClientID: Newly issued OAuth 2.0 client identifier (required)
  • ClientSecret: Optional client secret string
  • ClientIDIssuedAt: Unix timestamp when client_id was issued
  • ClientSecretExpiresAt: Unix timestamp when secret expires (0 if never expires, required if client_secret issued)

ClientRegistrationError

Error response from the authorization server for failed registration.

type ClientRegistrationError struct {
    ErrorCode        string `json:"error"`
    ErrorDescription string `json:"error_description,omitempty"`
}

Fields:

  • ErrorCode: Required error code (RFC 7591, Section 3.2.2)
  • ErrorDescription: Optional human-readable error message

Methods:

func (e *ClientRegistrationError) Error() string

Returns formatted error string.

RegisterClient

Performs Dynamic Client Registration.

func RegisterClient(
    ctx context.Context,
    registrationEndpoint string,
    clientMeta *ClientRegistrationMetadata,
    c *http.Client,
) (*ClientRegistrationResponse, error)

Parameters:

  • ctx: Context for the HTTP request
  • registrationEndpoint: DCR endpoint URL (from AuthServerMeta.RegistrationEndpoint)
  • clientMeta: Client metadata to register
  • c: HTTP client to use (uses http.DefaultClient if nil)

Returns:

  • *ClientRegistrationResponse: Registration response on success
  • error: Error if registration fails (may be *ClientRegistrationError)

Behavior:

  • POSTs client metadata to registration endpoint
  • Returns ClientRegistrationResponse on 201 Created status
  • Returns ClientRegistrationError on 400 Bad Request status
  • Validates that response includes required client_id field
  • Returns generic error for other status codes

Example:

import (
    "context"
    "fmt"
    "github.com/modelcontextprotocol/go-sdk/oauthex"
)

func registerNewClient(ctx context.Context, authServer *oauthex.AuthServerMeta) error {
    // Prepare client metadata
    clientMeta := &oauthex.ClientRegistrationMetadata{
        RedirectURIs: []string{"https://myapp.example.com/callback"},
        ClientName:   "My MCP Client",
        ClientURI:    "https://myapp.example.com",
        GrantTypes:   []string{"authorization_code", "refresh_token"},
        ResponseTypes: []string{"code"},
        Scope:        "mcp:read mcp:write",
        Contacts:     []string{"admin@myapp.example.com"},
    }

    // Register with authorization server
    response, err := oauthex.RegisterClient(
        ctx,
        authServer.RegistrationEndpoint,
        clientMeta,
        nil,
    )
    if err != nil {
        // Check if it's a registration error
        if regErr, ok := err.(*oauthex.ClientRegistrationError); ok {
            return fmt.Errorf("registration failed: %s - %s",
                regErr.ErrorCode, regErr.ErrorDescription)
        }
        return err
    }

    // Use registered credentials
    fmt.Printf("Registered! Client ID: %s\n", response.ClientID)
    if response.ClientSecret != "" {
        fmt.Printf("Client Secret: %s\n", response.ClientSecret)
        if !response.ClientSecretExpiresAt.IsZero() {
            fmt.Printf("Secret expires: %s\n", response.ClientSecretExpiresAt)
        }
    }

    return nil
}

Integration with Authentication

The oauthex package complements the auth package by providing metadata discovery:

  1. Client attempts to access protected MCP resource without authentication
  2. Server returns 401 Unauthorized with WWW-Authenticate header:
    WWW-Authenticate: Bearer resource_metadata=https://api.example.com/.well-known/oauth-protected-resource
  3. Client fetches metadata from the URL
  4. Client discovers authorization servers and required scopes
  5. Client obtains token from authorization server
  6. Client retries request with bearer token

Example client flow:

package main

import (
    "context"
    "fmt"
    "net/http"
    "github.com/modelcontextprotocol/go-sdk/mcp"
    "golang.org/x/oauth2"
)

func connectWithOAuth(serverURL string) (*mcp.ClientSession, error) {
    ctx := context.Background()

    // Attempt connection without token
    client := mcp.NewClient(&mcp.Implementation{Name: "oauth-client"}, nil)
    transport := &mcp.StreamableClientTransport{Endpoint: serverURL}

    session, err := client.Connect(ctx, transport, nil)
    if err != nil {
        // Check if error indicates authentication required
        // (implementation-specific error handling)

        // Fetch protected resource metadata
        // (using custom HTTP client or oauthex utilities)

        // Discover authorization server
        authServerURL := "https://auth.example.com" // from metadata

        // Configure OAuth client
        config := &oauth2.Config{
            ClientID:     "your-client-id",
            ClientSecret: "your-client-secret",
            Endpoint: oauth2.Endpoint{
                AuthURL:  authServerURL + "/authorize",
                TokenURL: authServerURL + "/token",
            },
            Scopes: []string{"mcp:read", "mcp:write"},
        }

        // Obtain token (various OAuth flows available)
        token, err := config.PasswordCredentialsToken(ctx, "username", "password")
        if err != nil {
            return nil, err
        }

        // Retry with authenticated client
        httpClient := config.Client(ctx, token)
        transport = &mcp.StreamableClientTransport{
            Endpoint:   serverURL,
            HTTPClient: httpClient,
        }

        return client.Connect(ctx, transport, nil)
    }

    return session, nil
}

RFC 9728 Compliance

This implementation follows RFC 9728 (OAuth 2.0 Protected Resource Metadata) with the following notes:

Supported features:

  • Standard metadata fields (§2)
  • Well-known URI discovery (§3)
  • WWW-Authenticate header parsing
  • Metadata validation

Unsupported features:

  • Additional custom keys (§2, last sentence)
  • Human-readable metadata internationalization (§2.1)
  • Signed metadata JWTs (§2.2)

The implementation can safely ignore signed metadata per §2.2 of the specification.

Discovery Methods

There are two primary ways to discover protected resource metadata:

1. Well-Known URI

Metadata can be discovered using the well-known URI pattern:

https://{host}/.well-known/oauth-protected-resource{/path}

For example, if the resource server is at https://api.example.com/mcp, the metadata would be at:

https://api.example.com/.well-known/oauth-protected-resource/mcp

2. WWW-Authenticate Header

When a client attempts to access a protected resource without proper authentication, the server returns a 401 Unauthorized response with a WWW-Authenticate header:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"

The client can parse this header to discover the metadata URL.

Use Cases

Server: Publishing Metadata

Publish metadata to help clients discover authentication requirements:

metadata := &oauthex.ProtectedResourceMetadata{
    Resource:            "https://api.example.com",
    AuthorizationServers: []string{"https://auth.example.com"},
    ScopesSupported:     []string{"mcp:read", "mcp:write"},
    ResourceName:        "Example MCP Server",
}

http.HandleFunc("/.well-known/oauth-protected-resource", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(metadata)
})

Client: Discovering Requirements

Fetch and parse metadata to discover authentication requirements:

import (
    "encoding/json"
    "net/http"
    "github.com/modelcontextprotocol/go-sdk/oauthex"
)

func discoverAuthRequirements(resourceURL string) (*oauthex.ProtectedResourceMetadata, error) {
    // Fetch metadata
    metadataURL := resourceURL + "/.well-known/oauth-protected-resource"
    resp, err := http.Get(metadataURL)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    // Parse metadata
    var metadata oauthex.ProtectedResourceMetadata
    if err := json.NewDecoder(resp.Body).Decode(&metadata); err != nil {
        return nil, err
    }

    // Use metadata to configure OAuth flow
    fmt.Printf("Authorization Servers: %v\n", metadata.AuthorizationServers)
    fmt.Printf("Required Scopes: %v\n", metadata.ScopesSupported)

    return &metadata, nil
}

Best Practices

  1. Always use HTTPS: Protected resource metadata URLs must use HTTPS
  2. Validate metadata: Verify that the resource field matches your resource URL
  3. Cache metadata: Metadata typically doesn't change frequently; cache it appropriately
  4. Handle missing metadata: Not all servers publish metadata; have fallback authentication mechanisms
  5. Check required scopes: Ensure your client requests all required scopes
  6. Keep metadata current: Update metadata when authentication requirements change

Related Standards

  • RFC 9728: OAuth 2.0 Protected Resource Metadata
  • RFC 8414: OAuth 2.0 Authorization Server Metadata
  • RFC 6749: OAuth 2.0 Authorization Framework
  • RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication
  • RFC 9396: OAuth 2.0 Rich Authorization Requests
  • RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP)