auth

package
v0.0.0-...-ee11cfc Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultClientID = "appwrite-cli"

DefaultClientID is the OAuth2 client the CLI identifies as.

View Source
const OAuth2Scopes = "openid email profile all"

OAuth2Scopes are the scopes the CLI requests.

Variables

View Source
var ErrDeviceAuthorizationExpired = errors.New("device authorization expired before it was approved")

ErrDeviceAuthorizationExpired means the user did not approve in time.

View Source
var ErrSessionExpired error = sessionExpired{}

ErrSessionExpired is returned when no usable credential remains.

View Source
var Trace func(format string, arguments ...any)

Trace receives one line per credential-store read when --verbose is on.

A package variable for the same reason client.RequestLog is one: the store is reached deep inside the auth path and diagnostics should not have to be threaded through every caller. Set during start-up, before any read.

View Source
var Warn func(format string, arguments ...any)

Warn receives a message the user should see whether or not --verbose is on.

Separate from Trace because falling back to plaintext is not diagnostics: it changes where the user's refresh token is stored, and staying quiet about it meant a headless Linux box or a locked keychain wrote a long-lived credential to disk with nothing said. Wired the same way Trace is, for the same reason.

Functions

func DecodeIDToken

func DecodeIDToken(idToken string) (email, name, subject string)

DecodeIDToken pulls the profile claims out of an OIDC ID token.

The signature is not verified: the token arrived over TLS from the endpoint the user just authenticated against, and it is used only to label the stored session. Ports decodeIdToken().

Types

type Authenticator

type Authenticator struct {
	Global     *config.Global
	Store      *TokenStore
	SDKVersion string

	// Now is injectable so expiry logic is testable without sleeping.
	Now func() time.Time
}

Authenticator resolves a usable access token for the active session.

func NewAuthenticator

func NewAuthenticator(global *config.Global, sdkVersion string) *Authenticator

NewAuthenticator wires an authenticator over the given preferences.

func (*Authenticator) AccessToken

func (a *Authenticator) AccessToken(forceRefresh bool) (string, error)

AccessToken returns a valid access token, refreshing it when necessary.

forceRefresh renews even a token that still looks valid, which the caller uses after the API rejects one.

type DeviceAuthorization

type DeviceAuthorization struct {
	DeviceCode              string      `json:"device_code"`
	UserCode                string      `json:"user_code"`
	VerificationURI         string      `json:"verification_uri"`
	VerificationURIComplete string      `json:"verification_uri_complete"`
	ExpiresIn               json.Number `json:"expires_in"`
	Interval                json.Number `json:"interval"`
}

DeviceAuthorization is the server's response to a device authorization request.

func (DeviceAuthorization) Lifetime

func (d DeviceAuthorization) Lifetime() time.Duration

Lifetime is how long the authorization is valid for.

An explicit zero means already expired and is honoured as such, ending the loop immediately. Only a missing or unparseable value falls back to a default, which stops a malformed response becoming an instant failure.

func (DeviceAuthorization) PollInterval

func (d DeviceAuthorization) PollInterval() time.Duration

PollInterval is how long to wait between token requests.

func (DeviceAuthorization) VerificationURL

func (d DeviceAuthorization) VerificationURL() string

VerificationURL is the URL to show the user, preferring the one that embeds the code so they do not have to type it.

type DeviceFlow

type DeviceFlow struct {
	Endpoint   string
	ClientID   string
	SDKVersion string
	// SelfSigned accepts a self-signed certificate on the endpoint, for a
	// self-hosted instance behind its own. Set from the stored preference by the
	// caller, which is the only place that reads preferences.
	SelfSigned bool

	// Sleep and Now are injectable so the poll loop is testable without
	// waiting real seconds.
	Sleep func(time.Duration)
	Now   func() time.Time
}

DeviceFlow runs RFC 8628 device authorization against one endpoint.

func NewDeviceFlow

func NewDeviceFlow(endpoint, sdkVersion string) *DeviceFlow

NewDeviceFlow wires a device flow against an endpoint.

func (*DeviceFlow) Authorize

func (f *DeviceFlow) Authorize() (DeviceAuthorization, error)

Authorize requests a device code and user code.

func (*DeviceFlow) Poll

func (f *DeviceFlow) Poll(authorization DeviceAuthorization) (*TokenSet, error)

Poll requests a token until the user approves, the authorization expires, or the server returns a real error.

Returns ErrDeviceAuthorizationExpired when the window closes. Pending and empty-body responses are retried; `slow_down` additionally widens the interval per RFC 8628 section 3.5.

type MissingRefreshToken

type MissingRefreshToken struct {
	SessionID string
	Store     string
	// StoreErr is what the credential store said. A not-found error means it
	// answered; anything else means it failed, which is worth wording
	// differently because unlocking a keyring and signing in again are
	// different actions.
	StoreErr  error
	PrefsPath string
}

MissingRefreshToken reports that a session has no refresh token, and says where the CLI looked.

The where is the point: "session expired" is a conclusion, and it is wrong whenever the CLI is looking somewhere other than where the token is -- a redirected HOME points macOS at a different login keychain and a good session reads as expired. That case cannot be told apart from a genuinely absent entry by the error alone, since `security` exits 44 for both, so naming the store and session id is the only honest thing available.

func (*MissingRefreshToken) Error

func (e *MissingRefreshToken) Error() string

func (*MissingRefreshToken) Unwrap

func (e *MissingRefreshToken) Unwrap() error

Unwrap exposes the store's own error, so a caller can still match on it.

type SessionRejectedError

type SessionRejectedError struct {
	Session string
	// contains filtered or unexported fields
}

SessionRejectedError reports a refresh token the server refused.

It says rejected rather than expired because invalid_grant covers a session revoked elsewhere and a rotation the CLI lost a race on as well as a genuine expiry, and expiry is the one of the three the CLI cannot confirm. It names the session for the reason cannotRefresh does: preferences hold one per environment, and "the session" identifies none of them.

The server's own description stays in the unwrap chain rather than the sentence. On this endpoint it is a fixed string -- "Invalid refresh token provided." -- that only restates the rejection, and --verbose prints it.

func (*SessionRejectedError) Error

func (e *SessionRejectedError) Error() string

func (*SessionRejectedError) Is

func (e *SessionRejectedError) Is(target error) bool

func (*SessionRejectedError) Unwrap

func (e *SessionRejectedError) Unwrap() error

type TokenSet

type TokenSet struct {
	AccessToken  string
	RefreshToken string
	IDToken      string
	ExpiresAt    time.Time
}

TokenSet is a completed device authorization.

type TokenStore

type TokenStore struct {
	Global *config.Global
}

TokenStore reads and writes refresh tokens, preferring the OS keyring and falling back to the preferences file.

The fallback is not a convenience: headless Linux and CI containers have no secret service, and a CLI that refuses to hold a session there is a CLI that cannot be scripted.

func (*TokenStore) DeleteRefresh

func (s *TokenStore) DeleteRefresh(sessionID string) error

DeleteRefresh removes a refresh token from both stores.

A missing or unavailable keyring must not block local cleanup, so the keyring error is ignored and the prefs copy is removed regardless.

func (*TokenStore) Refresh

func (s *TokenStore) Refresh(sessionID string) (string, error)

Refresh returns the stored refresh token for a session.

The failed lookup is described rather than reduced to "": "no token here" and "the store would not answer" are different advice. See MissingRefreshToken.

A token in prefs wins over the store's opinion -- it is the fallback SetRefresh writes to when the keyring is unavailable, so the store not answering does not matter.

func (*TokenStore) SetRefresh

func (s *TokenStore) SetRefresh(sessionID, token string) error

SetRefresh stores a refresh token, preferring the keyring.

On success the prefs copy is removed, so a token never lingers in plaintext after the keyring starts working. On failure it is written to prefs instead, and the user is told -- the fallback is the documented behaviour for headless Linux and CI, but which store holds a long-lived credential is theirs to know.

Jump to

Keyboard shortcuts

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