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

credentials

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package credentials stores the Credentials of each OAuth Upstream in a file of its own, written only under a lock on a sibling file (ADR 0003).

Index

Constants

View Source
const ReasonExpired = "the credentials expired and cannot be renewed"

ReasonExpired is why an Upstream whose Credentials are dead (see Credentials.Dead) needs a Login.

Variables

View Source
var ErrNoHome = errors.New("cannot locate the credentials: HOME is not set")

ErrNoHome is why NewStore can't return a Store.

View Source
var ErrRefreshRejected = errors.New("the authorization server rejected the refresh token")

ErrRefreshRejected is what a Refresh returns when the Authorization Server rejects the refresh token (invalid_grant).

Functions

This section is empty.

Types

type Binding

type Binding struct {
	URL      string   `json:"url"`
	ClientID string   `json:"client_id,omitempty"` // oauth.clientId
	Scopes   []string `json:"scopes"`              // oauth.scopes; null when the Config has none
}

Binding is what an Upstream's Config said when its Credentials were issued, and must still say for them to be used. oauth.clientSecret is left out, so that rotating it keeps them.

func BindingOf

func BindingOf(u config.Upstream) Binding

BindingOf returns the Binding of u as its Config is now.

type Credentials

type Credentials struct {
	oauth2.Token
	Scopes []string `json:"scopes,omitempty"` // granted

	TokenURL  string           `json:"token_url"`
	AuthStyle oauth2.AuthStyle `json:"auth_style"`
	// The client that Dynamic Client Registration issued, if any.
	ClientID     string `json:"client_id,omitempty"`
	ClientSecret string `json:"client_secret,omitempty"`

	// Identity: the canonical URI of the Upstream and the issuer of the
	// Authorization Server, and the Config they were issued for.
	Resource string  `json:"resource"`
	Issuer   string  `json:"issuer"`
	Config   Binding `json:"config"`
}

Credentials are what a Login leaves behind for one Upstream: its tokens, what is needed to renew them, and the identity they were issued for.

func (*Credentials) Dead

func (c *Credentials) Dead() bool

Dead reports whether c can't be used any more without a new Login: the access token has expired, and there is no refresh token to renew it with.

func (*Credentials) Expired added in v0.3.0

func (c *Credentials) Expired() bool

Expired reports whether the access token of c has expired.

type LoginNeededError

type LoginNeededError struct {
	// Reason is why the Credentials it had can't be used, "" if it had none.
	Reason string
}

LoginNeededError is the error of an Upstream that has no Credentials it can use.

func (*LoginNeededError) Error

func (e *LoginNeededError) Error() string

type Refresh

type Refresh func(ctx context.Context, c *Credentials, metadata string) (*Credentials, error)

Refresh gets a new access token for c from their Authorization Server and returns the Credentials it makes. metadata is the URL of the Protected Resource Metadata that the Upstream named when it rejected c, if it did. It returns ErrRefreshRejected when the server rejects the refresh token, and a *LoginNeededError when c can't be used any more; any other error leaves them as they are.

type Source

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

Source is the Credentials of one Upstream as one process uses them. It reads them again whenever their file changes, so that a Login, a logout or another process's renewal shows, and renews them with its Refresh only once at a time, both in this process and, through the Store's lock, across processes (ADR 0003).

Token on the nil Source returns no access token.

func (*Source) Rejected

func (s *Source) Rejected(ctx context.Context, token, metadata string) (string, error)

Rejected renews the Credentials whose access token the Upstream answered 401 to, at most once for each token, and returns the new access token. metadata is the URL of the Protected Resource Metadata that the 401 named, if any. A *LoginNeededError means that the Credentials can't be used.

func (*Source) Token

func (s *Source) Token(ctx context.Context) (string, error)

Token returns the access token to send, renewed first if it expires within renewBefore, or "" without Credentials. A *LoginNeededError means that they can't be used.

func (*Source) Unauthorized

func (s *Source) Unauthorized() error

Unauthorized returns why the Upstream needs a Login when it answers 401 past Token and Rejected: it has no Credentials, as last read, or rejects them.

type Store

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

Store is the directory holding the Credentials of every Upstream.

func NewStore

func NewStore(lookupEnv config.LookupEnv) (*Store, error)

NewStore returns the Store under $XDG_STATE_HOME/sprut/credentials, or ~/.local/state/sprut/credentials.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, upstream string) (bool, error)

Delete deletes the Credentials of upstream under its lock, and reports whether there were any. The lock file stays. When there are none, it takes no lock, so that it creates no lock file: a Save that is writing them meanwhile would have outlived a locked Delete just as well, had it taken the lock a moment later.

func (*Store) Load

func (s *Store) Load(u config.Upstream) (*Credentials, error)

Load returns the Credentials of u, or nil if it has none, or none issued for u as its Config is now.

func (*Store) Save

func (s *Store) Save(ctx context.Context, upstream string, c *Credentials) error

Save replaces the Credentials of upstream with c, under its lock.

func (*Store) Source

func (s *Store) Source(u config.Upstream, refresh Refresh, log *slog.Logger) *Source

Source returns the Source of u's Credentials, which renews them with refresh and logs to log.

func (*Store) Wait

func (s *Store) Wait()

Wait waits until no renewal that holds the lock is in progress, so that Credentials that an Authorization Server has rotated are saved before the process exits. A renewal that takes the lock once Wait has returned is not waited for. A nil Store has none.

Jump to

Keyboard shortcuts

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