Use case — Protected resource metadata (RFC 9728)
A client that holds a resource identifier (an API base URL) needs to learn which authorization server protects it and what it accepts — scopes, bearer methods, the JWKS it signs responses with — without that being hard-coded. RFC 9728 defines a well-known document for exactly that, the resource-server counterpart to the OP's own /.well-known/openid-configuration.
op.WithProtectedResources lets the OP host these documents for one or more resource servers it protects. Each document is served read-only and names the OP's own issuer in authorization_servers. The registration can also name confidential clients that are allowed to introspect access tokens addressed to that resource.
Use this when clients discover APIs by resource identifier, or when more than one API needs machine-readable metadata that points back to the same OP. If your clients are all manually configured and already know the issuer, this metadata is optional; it will not add token validation or gateway behavior.
The OP serves both documents in that ladder; validating the bearer token stays with the resource server.
Specs referenced on this page
The OP advertises, it does not enforce
The OP serves the metadata and stamps itself as the resource's authorization server. It does not validate the resource server's bearer tokens — that stays the resource server's job. This option is about publishing the relationship, not about turning the OP into a gateway.
Wiring
import "github.com/libraz/go-oidc-provider/op"
op.New(
op.WithIssuer("https://op.example.com"),
/* required options */
op.WithProtectedResources(op.ProtectedResource{
Resource: "https://op.example.com/api",
ScopesSupported: []string{"api.read", "api.write"},
BearerMethodsSupported: []string{"header"},
IntrospectionClients: []string{"api-introspector"},
}),
)| Field | Document parameter |
|---|---|
Resource (required) | resource — an RFC 8707 resource indicator: an absolute URI with no fragment |
ScopesSupported | scopes_supported |
BearerMethodsSupported | bearer_methods_supported — header, body, query |
ResourceSigningAlgValuesSupported | resource_signing_alg_values_supported |
JWKSURI | jwks_uri |
ResourceName | name |
IntrospectionClients | not an RFC 9728 field; configures audience-scoped /introspect delegation |
Resource is validated at op.New like an RFC 8707 resource parameter; an invalid identifier refuses to start. At least one resource is required, and two resources that canonicalise to the same identifier — or whose paths collide on the same well-known mount — are rejected.
What is served
The document lives under /.well-known/oauth-protected-resource. When the resource identifier has a path component, RFC 9728 §3.1 appends it to the well-known path, so each registered resource is served independently:
curl https://op.example.com/.well-known/oauth-protected-resource/api{
"resource": "https://op.example.com/api",
"authorization_servers": ["https://op.example.com"],
"scopes_supported": ["api.read", "api.write"],
"bearer_methods_supported": ["header"]
}authorization_servers is stamped with the OP's own issuer — the link a client follows back to discovery. Registering two resources (/orders and /inventory) serves each at its own path-suffixed location; the path is derived from the resource's path component only (host-insensitive per §3.1), which is why two resources whose paths would collide are rejected at construction rather than panicking the router.
IntrospectionClients is an OP authorization rule, not a metadata field. The named caller must authenticate as a confidential client; public clients and token_endpoint_auth_method=none receive 401 invalid_client at /introspect. Without delegation, a caller sees only its own access tokens. With IntrospectionClients: []string{"api-introspector"}, that confidential client may introspect access tokens whose audience is exactly this resource. The audience check is scoped per resource, and refresh tokens are never delegated.
Read next
- Discovery — the OP's own
/.well-known/openid-configuration. - Rich authorization requests — describe access at a resource the client targets.
- mTLS / Sender-constrained tokens — binding the bearer tokens a resource server then validates.