Help us shape the future of Go! Take the 2026 Go Developer Survey

oauth

package
v0.17.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: AGPL-3.0 Imports: 20 Imported by: 0

Documentation

Overview

Package oauth signs MCP clients in (R-413, design 06 §3.6).

Pando serves MCP itself, and an MCP client that has no token gets one the way the MCP specification's authorization section says: OAuth 2.1, with Pando as the authorization server and the console as its consent page.

  1. The client registers itself (RFC 7591): a name and the addresses it may be sent back to. No secret: every client is public.
  2. It sends the person's browser to the console with a PKCE challenge (RFC 7636, S256 only). The person, signed in however their account signs in, sees the client's name and where it will be sent back to, and approves. The browser goes back to the client with a one-time code.
  3. The client trades the code and the verifier for a delegated token (R-058), named for the client, under host policy's lifetime (R-061).

It is R-406's flow (package clisignin) with the client's own redirect address in place of the CLI's loopback port, and the same binding: a code is worth nothing without the verifier, which never left the client.

Index

Constants

View Source
const CodeLifetime = clisignin.CodeLifetime

CodeLifetime is how long an approval waits for the client, as a CLI sign-in's (clisignin.CodeLifetime).

Variables

This section is empty.

Functions

This section is empty.

Types

type AccountReader

type AccountReader interface {
	ByID(ctx context.Context, userID string) (state.User, bool, error)
}

AccountReader reads accounts (state.Users).

type Approved

type Approved struct {
	RedirectURL string
	ClientName  string
	ExpiresAt   time.Time
}

Approved is where the browser goes with the code.

type ClientStore

type ClientStore interface {
	Create(ctx context.Context, c state.OAuthClient) error
	ByID(ctx context.Context, id string) (state.OAuthClient, bool, error)
}

ClientStore keeps registered clients (state.OAuthClients).

type CodeStore

type CodeStore interface {
	Create(ctx context.Context, c state.OAuthCode) error
	Take(ctx context.Context, codeHash string) (state.OAuthCode, bool, error)
}

CodeStore keeps approvals (state.OAuthCodes).

type Consent struct {
	ClientID   string `json:"client_id"`
	ClientName string `json:"client_name"`

	// ReturnsTo is the host, or native app scheme, the browser goes back to.
	ReturnsTo string `json:"returns_to"`
	// contains filtered or unexported fields
}

Consent is what the person approving is shown.

type ErrorResponse

type ErrorResponse struct {
	Status      int    `json:"-"`
	Code        string `json:"error"`
	Description string `json:"error_description,omitempty"`
}

ErrorResponse is an OAuth error response (RFC 6749 §5.2, RFC 7591 §3.2.2): what registration and the token endpoint answer, since a client reads those, not Pando's error envelope.

func (*ErrorResponse) Error

func (e *ErrorResponse) Error() string

type Exchanged

type Exchanged struct {
	Issued state.Issued

	// UserID is the account the token acts as (R-058).
	UserID     string
	ApprovedBy string
	ClientID   string
	ClientName string
}

Exchanged is a token made for an approved client.

type Registered

type Registered struct {
	ClientID                string   `json:"client_id"`
	ClientIDIssuedAt        int64    `json:"client_id_issued_at"`
	ClientName              string   `json:"client_name"`
	RedirectURIs            []string `json:"redirect_uris"`
	GrantTypes              []string `json:"grant_types"`
	ResponseTypes           []string `json:"response_types"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method"`
}

Registered is the answer to a registration (RFC 7591 §3.2.1).

type Registration

type Registration struct {
	ClientName              string   `json:"client_name"`
	RedirectURIs            []string `json:"redirect_uris"`
	GrantTypes              []string `json:"grant_types,omitempty"`
	ResponseTypes           []string `json:"response_types,omitempty"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method,omitempty"`
}

Registration is a client's registration request (RFC 7591 §2). Fields Pando does not use are ignored; the answer says what was registered.

type Request

type Request struct {
	ClientID            string `json:"client_id"`
	RedirectURI         string `json:"redirect_uri"`
	ResponseType        string `json:"response_type"`
	CodeChallenge       string `json:"code_challenge"`
	CodeChallengeMethod string `json:"code_challenge_method"`
	State               string `json:"state"`
	Resource            string `json:"resource"`
	Scope               string `json:"scope"`
}

Request is an authorization request (RFC 6749 §4.1.1, RFC 7636 §4.3, RFC 8707 §2), as the console's approval page sends it on from its address.

type Service

type Service struct {
	Clients ClientStore
	Codes   CodeStore
	Users   AccountReader
	Tokens  TokenMinter
	Policy  interface {
		Load(ctx context.Context) (policy.Document, error)
	}
	Clock clock.Clock
}

Service registers clients, approves their sign-ins and trades them for tokens. Like clisignin, it decides nothing about who is asking: the caller has established that a signed-in person, not a token, approved, and audits.

func (*Service) Approve

func (s *Service) Approve(ctx context.Context, userID, approvedBy string, req Request, resources []string, issuer string) (Approved, error)

Approve records that userID, signed in as approvedBy, lets the client sign in as them, and says where to send the browser. issuer goes on the redirect as iss (RFC 9207), so a client can tell which server answered.

func (*Service) Check

func (s *Service) Check(ctx context.Context, req Request, resources []string) (Consent, error)

Check validates an authorization request against its client and says what to show the person. resources are the values RFC 8707's resource parameter may name: Pando's MCP endpoint and Pando itself.

Every refusal is shown on the approval page rather than sent back to the client [P]: one with an unknown client or redirect address must be (RFC 6749 §4.1.2.1), and drawing the rest the same way keeps one path.

func (*Service) Deny

func (s *Service) Deny(ctx context.Context, req Request, resources []string, issuer string) (string, error)

Deny says where to send the browser when the person cancels: the client's address with access_denied (RFC 6749 §4.1.2.1), once the request is shown to be a real client's, so Cancel cannot be used to send a browser anywhere else.

func (*Service) Exchange

func (s *Service) Exchange(ctx context.Context, req TokenRequest) (Exchanged, *ErrorResponse)

Exchange trades a code and its verifier for a delegated token. The approval is gone whatever the outcome. When a code that named an approval is refused, the Exchanged returned with the error says whose it was, so the caller can audit that and only that, as clisignin.Exchange.

func (*Service) Register

func (s *Service) Register(ctx context.Context, reg Registration) (Registered, *ErrorResponse)

Register records a client. Open to anyone (R-413): what a client can do is ask a person to approve it, and the person sees its name and where it will be sent back to. A client nobody approves is removed after a day.

type Token

type Token struct {
	AccessToken secret.Value `json:"-"`
	TokenType   string       `json:"token_type"`
	ExpiresIn   int64        `json:"expires_in,omitempty"`
}

Token is the token endpoint's answer (RFC 6749 §5.1). No refresh token: the token lives as long as host policy lets any token live, and a client signs in again after that.

func TokenFor

func TokenFor(e Exchanged, now time.Time) Token

TokenFor is the answer for an exchanged token, at now.

type TokenMinter

type TokenMinter interface {
	Create(ctx context.Context, kind, name, ownerUserID, createdBy string, expiresAt *time.Time) (state.Issued, error)
}

TokenMinter mints tokens (state.Tokens).

type TokenRequest

type TokenRequest struct {
	GrantType    string
	Code         string
	RedirectURI  string
	ClientID     string
	CodeVerifier string
}

TokenRequest is a token request (RFC 6749 §4.1.3, RFC 7636 §4.5).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL