Package cimddraft

import "funnyhaha.technology/rfc/cimddraft"
View the source code

Overview

Package cimddraft implements OAuth Client ID Metadata Documents for an authorization server: checking a client identifier that's a URL, and the metadata document fetched from it.

A Client ID Metadata Document lets a client use a URL it controls as its client_id, with no registration step. The authorization server fetches the URL and reads the client's name and redirect URIs from it. The Model Context Protocol uses it as the main way MCP clients identify themselves.

id, err := cimddraft.ParseClientID(form.Get("client_id"))
// Fetch id.String(), refusing redirects and special-purpose addresses,
// and reading at most cimddraft.MaxDocumentSize bytes.
doc, err := cimddraft.ParseDocument(body, id)

The package doesn't fetch. Fetching safely needs network policy, such as refusing the addresses rfc6890 lists (Section 8.6), that belongs to the server.

Client ID Metadata Documents are an IETF draft, draft-ietf-oauth-client-id-metadata-document. This package implements revision -02, whose text is in draft-ietf-oauth-client-id-metadata-document-02.txt in this directory, and the doc comments cite its sections. It tracks the draft's latest revision, and moving to a new one is a single change reviewed against the difference between the texts. When the draft is published as an RFC, a package named after that RFC replaces this one.

Index

Constants

const MaxDocumentSize = 5 * 1024
source

MaxDocumentSize is the most an authorization server should read of a Client ID Metadata Document: 5 kilobytes (Section 8.7). A document that doesn't end within it is an error, not a truncated document.

const MetadataSupported = "client_id_metadata_document_supported"
source

MetadataSupported is the authorization server metadata member that says the server accepts Client ID Metadata Documents (Section 6). Set it with rfc8414's Metadata.WithExtension.

Variables

var (
	// ErrInvalidClientID is returned by [ParseClientID] for a URL that
	// breaks the rules of Section 3.
	ErrInvalidClientID = errors.New("invalid client identifier URL")

	// ErrInvalidDocument is returned by [ParseDocument] for a document
	// that isn't JSON, or breaks the rules of Section 4 or 4.1.
	ErrInvalidDocument = errors.New("invalid client ID metadata document")
)
source

Types

ClientID source

type ClientID struct {
	// contains filtered or unexported fields
}

ClientID is a Client Identifier URL. Use ParseClientID to check a string. Two ClientIDs are the same client only if their strings are identical (Section 3).

ParseClientID source

func ParseClientID(s string) (ClientID, error)

ParseClientID returns s as a ClientID, or ErrInvalidClientID unless, following Section 3, it uses https, has a host and a path, and has no userinfo, fragment, or "." or ".." path segment. A query is allowed, since Section 3 only says it SHOULD NOT be there.

(ClientID) Host source

func (c ClientID) Host() string

Host returns the URL's host name, without the port. Show it to the user when asking them to authorize the client, since anyone can choose the client name in the document, but not the host it's served from.

(ClientID) String source

func (c ClientID) String() string

String returns the URL.

Document source

type Document struct {
	ClientID                string   `json:"client_id"`
	ClientName              string   `json:"client_name,omitempty"`
	ClientURI               string   `json:"client_uri,omitempty"`
	LogoURI                 string   `json:"logo_uri,omitempty"`
	RedirectURIs            []string `json:"redirect_uris,omitempty"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method,omitempty"`
	GrantTypes              []string `json:"grant_types,omitempty"`
	ResponseTypes           []string `json:"response_types,omitempty"`
	Scope                   string   `json:"scope,omitempty"`
	JWKSURI                 string   `json:"jwks_uri,omitempty"`
}

Document is the client metadata in a Client ID Metadata Document: the RFC 7591 members an authorization server needs to show and redirect to the client. Read one with ParseDocument.

See Section 4.

ParseDocument source

func ParseDocument(data []byte, id ClientID) (Document, error)

ParseDocument reads the Client ID Metadata Document fetched from the client ID, and returns ErrInvalidDocument if it isn't a JSON object or:

It doesn't limit the size of data. Read at most MaxDocumentSize bytes first.