package lazyoauth ¶
import "golazy.dev/lazyoauth"
Variables ¶
var ErrInvalidClient, ErrInvalidRequest, ErrInvalidGrant, ErrRefreshTokenReplay, ErrUnauthorized, ErrUnsupportedFlow ¶
var (
ErrInvalidClient = errors.New("lazyoauth: invalid client")
ErrInvalidRequest = errors.New("lazyoauth: invalid request")
ErrInvalidGrant = errors.New("lazyoauth: invalid grant")
ErrRefreshTokenReplay = errors.New("lazyoauth: refresh token replay detected")
ErrUnauthorized = errors.New("lazyoauth: unauthorized")
ErrUnsupportedFlow = errors.New("lazyoauth: unsupported flow")
)
Types ¶
type AuthCode ¶
AuthCode is an authorization code record.
type AuthCode struct {
Code string `json:"code"`
ClientID string `json:"client_id"`
RedirectURI string `json:"redirect_uri"`
CodeChallenge string `json:"code_challenge,omitempty"`
CodeChallengeMethod string `json:"code_challenge_method,omitempty"`
User lazyauth.User `json:"user"`
Scope []string `json:"scope,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
}
type ClaimsMapper ¶
ClaimsMapper maps an authenticated user and OAuth client to JWT claims.
type ClaimsMapper interface {
ClaimsFor(context.Context, lazyauth.User, Client) (lazyjwt.Claims, error)
}
type ClaimsMapperFunc ¶
ClaimsMapperFunc adapts a function to ClaimsMapper.
type ClaimsMapperFunc func(context.Context, lazyauth.User, Client) (lazyjwt.Claims, error)
func (fn ClaimsMapperFunc) ClaimsFor ¶
func (fn ClaimsMapperFunc) ClaimsFor(ctx context.Context, user lazyauth.User, client Client) (lazyjwt.Claims, error)
type Client ¶
Client is an OAuth client.
type Client struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
RedirectURIs []string `json:"redirect_uris"`
Domain string `json:"domain,omitempty"`
}
type Config ¶
Config configures an OAuth authorization server and resource server.
type Config struct {
Issuer string
Resource string
Auth lazyauth.Config
Store Store
// RefreshTokenStates persists the minimal rotation and replay state for
// signed, self-contained refresh tokens. When nil, Store is used if it
// implements RefreshTokenStateStore; otherwise an in-memory store is used.
RefreshTokenStates RefreshTokenStateStore
Signer lazyjwt.Signer
// Validator overrides the resource-server JWT validator. When empty, a
// validator is derived from Signer, Issuer, and Resource. The access-token
// typ requirement remains framework-owned and cannot be overridden.
Validator lazyjwt.ValidatorConfig
ClaimsMapper ClaimsMapper
AuthorizePath string
TokenPath string
RegisterPath string
JWKSPath string
AccessTokenTTL time.Duration
RefreshTokenTTL time.Duration
AllowDynamicClients bool
// AllowClientIDMetadataDocuments advertises and accepts HTTPS URLs as
// client IDs. ClientIDMetadataDocumentHosts must contain the exact trusted
// authorities (host or host:port) from which metadata may be fetched.
AllowClientIDMetadataDocuments bool
ClientIDMetadataDocumentHosts []string
// ClientIDMetadataDocumentHTTPClient overrides the bounded default client,
// primarily for custom transports and tests. Redirects remain constrained
// to ClientIDMetadataDocumentHosts.
ClientIDMetadataDocumentHTTPClient *http.Client
// DisableAuthorizationResponseIssuerMetadata omits the RFC 9207 metadata
// claim that clients must require an iss parameter in authorization
// responses. The server still emits iss. Use this only as a temporary
// compatibility workaround for clients that discard iss before validation.
DisableAuthorizationResponseIssuerMetadata bool
// LoginTitle and LoginDescription brand the built-in authorization login
// page. They are rendered as escaped text and may be left empty for the
// framework defaults.
LoginTitle string
LoginDescription string
}
type MemoryStore ¶
MemoryStore is an in-memory OAuth store.
type MemoryStore struct {
Clients map[string]Client `json:"clients"`
Codes map[string]AuthCode `json:"codes"`
Refresh map[string]RefreshToken `json:"refresh"`
Families map[string]RefreshTokenFamily `json:"refresh_token_families"`
// contains filtered or unexported fields
}
func NewDiskStore ¶
NewDiskStore loads or creates a JSON OAuth store at path.
func NewDiskStore(path string) (*MemoryStore, error)
func NewMemoryStore ¶
NewMemoryStore creates an empty in-memory store.
func NewMemoryStore() *MemoryStore
func (store *MemoryStore) CreateFamily ¶
CreateFamily starts refresh-token rotation state for a newly issued grant.
func (store *MemoryStore) CreateFamily(_ context.Context, family RefreshTokenFamily) error
func (store *MemoryStore) GetClient ¶
func (store *MemoryStore) GetClient(_ context.Context, id string) (Client, error)
func (store *MemoryStore) GetRefreshToken ¶
func (store *MemoryStore) GetRefreshToken(_ context.Context, token string) (RefreshToken, error)
func (store *MemoryStore) RevokeFamily ¶
RevokeFamily invalidates a refresh-token family. It is idempotent.
func (store *MemoryStore) RevokeFamily(_ context.Context, familyID string) error
func (store *MemoryStore) Rotate ¶
Rotate atomically advances a refresh-token family or revokes it on replay.
func (store *MemoryStore) Rotate(_ context.Context, rotation RefreshTokenRotation) error
func (store *MemoryStore) SaveAuthCode ¶
func (store *MemoryStore) SaveAuthCode(_ context.Context, code AuthCode) error
func (store *MemoryStore) SaveClient ¶
func (store *MemoryStore) SaveClient(_ context.Context, client Client) error
func (store *MemoryStore) SaveRefreshToken ¶
func (store *MemoryStore) SaveRefreshToken(_ context.Context, token RefreshToken) error
func (store *MemoryStore) TakeAuthCode ¶
func (store *MemoryStore) TakeAuthCode(_ context.Context, code string) (AuthCode, error)
type RefreshToken ¶
RefreshToken is a legacy opaque refresh token record.
Deprecated: new refresh tokens are signed, self-contained grants whose replay state is persisted through RefreshTokenStateStore. Store retains these methods so existing implementations and previously issued opaque tokens remain compatible during migration.
type RefreshToken struct {
Token string `json:"token"`
ClientID string `json:"client_id"`
User lazyauth.User `json:"user"`
Scope []string `json:"scope,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
}
type RefreshTokenFamily ¶
RefreshTokenFamily is the minimal server-side state for one rotating refresh token family. The signed token carries the grant; the store tracks only the current token ID and whether the family has been revoked.
type RefreshTokenFamily struct {
ID string `json:"id"`
CurrentTokenID string `json:"current_token_id"`
ExpiresAt time.Time `json:"expires_at"`
Revoked bool `json:"revoked"`
}
type RefreshTokenRotation ¶
RefreshTokenRotation describes one atomic refresh-token rotation.
type RefreshTokenRotation struct {
FamilyID string
PresentedTokenID string
NextTokenID string
ExpiresAt time.Time
}
type RefreshTokenStateStore ¶
RefreshTokenStateStore tracks refresh-token rotation and family revocation.
Rotate must atomically compare PresentedTokenID with the family's current token ID. A mismatch means replay: the implementation must revoke the family and return ErrRefreshTokenReplay. Missing, expired, or already-revoked families return ErrInvalidGrant. The signed refresh token carries user, client, scope, resource, and expiry data; implementations must not store the token itself. Implementations may opportunistically delete expired or revoked families; a missing family must continue to return ErrInvalidGrant.
type RefreshTokenStateStore interface {
CreateFamily(context.Context, RefreshTokenFamily) error
Rotate(context.Context, RefreshTokenRotation) error
RevokeFamily(context.Context, string) error
}
type Server ¶
Server serves OAuth endpoints and validates bearer tokens.
type Server struct {
// contains filtered or unexported fields
}
func New ¶
New creates an OAuth server.
func New(config Config) (*Server, error)
func (server *Server) Handler ¶
Handler serves OAuth endpoints and protects non-OAuth requests with bearer token validation.
func (server *Server) Handler(next http.Handler) http.Handler
func (server *Server) HandlesPath ¶
HandlesPath reports whether path belongs to this OAuth server.
func (server *Server) HandlesPath(path string) bool
func (server *Server) Protect ¶
Protect validates bearer tokens before calling next.
func (server *Server) Protect(next http.Handler) http.Handler
func (server *Server) ServeHTTP ¶
ServeHTTP serves OAuth metadata and endpoint requests.
func (server *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
type Store ¶
Store persists OAuth clients, authorization codes, and refresh tokens.
type Store interface {
SaveClient(context.Context, Client) error
GetClient(context.Context, string) (Client, error)
SaveAuthCode(context.Context, AuthCode) error
TakeAuthCode(context.Context, string) (AuthCode, error)
SaveRefreshToken(context.Context, RefreshToken) error
GetRefreshToken(context.Context, string) (RefreshToken, error)
}
Package lazyoauth provides OAuth server and resource-server primitives.
It is intentionally independent from specific account databases and from MCP. Applications provide lazyauth authenticators, token stores, and claim mapping policy. Signed refresh tokens carry their grant across server restarts while Config.RefreshTokenStates accepts a focused durable RefreshTokenStateStore for rotation, replay detection, and family revocation. lazyapp can then use the same OAuth server for MCP clients such as Codex or Claude, browser-integrated companion applications, or other dependent clients.