Documentation
¶
Overview ¶
Package auth — HTTP client for nSelf auth server operations.
Package auth — device code polling loop for the CLI login flow.
Package auth provides CLI authentication storage and client utilities. ~/.nself/auth.json is the credential store with 0600 permissions.
Index ¶
- Constants
- Variables
- func ActivateLicense(ctx context.Context, accessToken, licenseID string) error
- func AuthServerURL() string
- func CLIAuthBaseURL() string
- func DeleteAuthFile() error
- func GenerateJWTKey() (string, error)
- func GetAuthFilePath() string
- func InviteTeamMember(ctx context.Context, accessToken, email string) error
- func IsLoggedIn() bool
- func LastRotationTime(logPath string) (time.Time, error)
- func RemoveTeamMember(ctx context.Context, accessToken, email string) error
- func RevokeDevice(ctx context.Context, accessToken, deviceID string) error
- func RevokeSession(ctx context.Context, accessToken string, all bool) error
- func RotationHardDays() int
- func RotationLogPath() string
- func RotationWindowDays() int
- func SetTeamMemberRole(ctx context.Context, accessToken, email, role string) error
- func TransferLicense(ctx context.Context, accessToken, licenseID, toEmail string) error
- func WriteAuthFile(af *AuthFile) error
- type AccountInfo
- type AuthAPIError
- type AuthFile
- type DeviceCodeResponse
- type DeviceEntry
- type LicenseInfo
- type PollResult
- type RotateResult
- type TeamMember
- type TokenResponse
Constants ¶
const ( // DefaultRotationWindowDays is the maximum age of a JWT key before // nself doctor --deep reports a warning. Override with // NSELF_JWT_ROTATION_WINDOW_DAYS. DefaultRotationWindowDays = 90 // DefaultRotationHardDays is the age at which JWT-ROT-01 escalates from // warn to fail. Defaults to 2× the rotation window. Override with // NSELF_JWT_ROTATION_HARD_DAYS. DefaultRotationHardMultiplier = 2 // DefaultRotationLogPath is where rotation events are recorded. // Falls back to XDG_STATE_HOME (or ~/.local/state) when not writable. // Override with NSELF_JWT_ROTATION_LOG. DefaultRotationLogPath = "/var/lib/nself/jwt-rotation.log" // GracePeriodHours is the window during which the old and new keys are // both accepted. This allows running tokens signed by the old key to // expire naturally without invalidating active sessions. GracePeriodHours = 12 )
Variables ¶
var ErrNotLoggedIn = errors.New("not logged in — run 'nself login' to authenticate")
ErrNotLoggedIn is returned when no auth.json exists or token is missing.
var ErrPollTimeout = fmt.Errorf("authorization timed out — the login URL has expired. Run 'nself login' to try again")
ErrPollTimeout is returned when the device code polling window expires.
Functions ¶
func ActivateLicense ¶
ActivateLicense activates a license key on the current device.
func AuthServerURL ¶
func AuthServerURL() string
AuthServerURL is the base URL of the auth server. Overridable via NSELF_AUTH_SERVER_URL for testing.
func CLIAuthBaseURL ¶
func CLIAuthBaseURL() string
CLIAuthBaseURL is the web UI for the CLI device auth page.
func DeleteAuthFile ¶
func DeleteAuthFile() error
DeleteAuthFile removes ~/.nself/auth.json if it exists. Returns nil if the file does not exist (idempotent).
func GenerateJWTKey ¶ added in v1.0.14
GenerateJWTKey returns a securely-random hex-encoded key suitable for use as the HASURA_GRAPHQL_JWT_SECRET key material (HS256/HS512).
func GetAuthFilePath ¶
func GetAuthFilePath() string
GetAuthFilePath returns the path for use in help text (never reads content).
func InviteTeamMember ¶
InviteTeamMember sends a team invitation to the given email.
func IsLoggedIn ¶
func IsLoggedIn() bool
IsLoggedIn returns true if ~/.nself/auth.json exists and has a token.
func LastRotationTime ¶ added in v1.0.14
LastRotationTime reads the rotation log and returns the timestamp of the most recent successful rotation. Returns the zero time.Time if no rotation has ever been recorded or if the log cannot be read.
func RemoveTeamMember ¶
RemoveTeamMember removes a member from the account's team.
func RevokeDevice ¶
RevokeDevice revokes a specific device session.
func RevokeSession ¶
RevokeSession calls POST /auth/signout to revoke the session server-side.
func RotationHardDays ¶ added in v1.0.14
func RotationHardDays() int
RotationHardDays returns the age at which JWT-ROT-01 escalates to fail (not just warn). Defaults to 2× RotationWindowDays. Override with NSELF_JWT_ROTATION_HARD_DAYS.
func RotationLogPath ¶ added in v1.0.14
func RotationLogPath() string
RotationLogPath returns the effective rotation log path, honouring the NSELF_JWT_ROTATION_LOG environment variable override. When the primary path is not writable (e.g. /var/lib/nself/ on a user workstation), it falls back to ${XDG_STATE_HOME:-$HOME/.local/state}/nself/jwt-rotation.log.
func RotationWindowDays ¶ added in v1.0.14
func RotationWindowDays() int
RotationWindowDays returns the effective rotation window, honouring the NSELF_JWT_ROTATION_WINDOW_DAYS environment variable override. Returns an error on malformed input.
func SetTeamMemberRole ¶
SetTeamMemberRole updates a member's role on the account's team.
func TransferLicense ¶
TransferLicense transfers a license from the current account to another email.
func WriteAuthFile ¶
WriteAuthFile writes auth credentials to ~/.nself/auth.json with 0600 perms. Creates ~/.nself/ directory with 0700 if it does not exist.
Types ¶
type AccountInfo ¶
type AccountInfo struct {
Authenticated bool `json:"authenticated"`
Account struct {
ID string `json:"id"`
Email string `json:"email"`
DisplayName string `json:"display_name"`
AvatarURL string `json:"avatar_url"`
Tier string `json:"tier"`
EmailVerified bool `json:"email_verified"`
MFAEnabled bool `json:"mfa_enabled"`
} `json:"account"`
}
AccountInfo is the response from GET /auth/session.
func GetSession ¶
func GetSession(ctx context.Context, accessToken string) (*AccountInfo, error)
GetSession returns the account info for the given access token.
type AuthAPIError ¶
AuthAPIError is returned when the auth server returns a non-2xx response.
func (*AuthAPIError) Error ¶
func (e *AuthAPIError) Error() string
type AuthFile ¶
type AuthFile struct {
// SessionToken is the opaque session token for server-side session lookup.
SessionToken string `json:"session_token"`
// AccessToken is the RS256 JWT for subapp requests.
AccessToken string `json:"access_token"`
// Email is the account email (display only — not used for auth).
Email string `json:"email"`
// Tier is the account tier (display only).
Tier string `json:"tier"`
// DisplayName is the account display name (display only).
DisplayName string `json:"display_name,omitempty"`
// Bundles are the unlocked plugin bundles (display only).
Bundles []string `json:"bundles,omitempty"`
// ExpiresAt is the access token expiry as RFC3339 string.
ExpiresAt string `json:"expires_at,omitempty"`
}
AuthFile holds the persisted auth credentials.
func ReadAuthFile ¶
ReadAuthFile reads and parses ~/.nself/auth.json. Returns ErrNotLoggedIn if file does not exist.
type DeviceCodeResponse ¶
type DeviceCodeResponse struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"` // XXXX-YYYY format shown to user
VerificationURL string `json:"verification_url"` // nself.org/auth/cli?code=...
ExpiresInSec int `json:"expires_in"`
IntervalSec int `json:"interval"`
}
DeviceCodeResponse is returned by the device authorization endpoint.
func DeviceAuthorize ¶
func DeviceAuthorize(ctx context.Context) (*DeviceCodeResponse, error)
DeviceAuthorize initiates the device code flow. Returns a DeviceCodeResponse with the code to display to the user.
type DeviceEntry ¶
type DeviceEntry struct {
ID string `json:"id"`
Name string `json:"name"`
OS string `json:"os"`
LastActive string `json:"last_active"`
IsCurrent bool `json:"is_current"`
}
DeviceEntry represents one registered device for an account.
func GetDevices ¶
func GetDevices(ctx context.Context, accessToken string) ([]DeviceEntry, error)
GetDevices returns the list of registered devices for the account.
type LicenseInfo ¶
type LicenseInfo struct {
ID string `json:"id"`
Product string `json:"product"`
Tier string `json:"tier"`
Bundles []string `json:"bundles"`
SeatsIncluded int `json:"seats_included"`
SeatsUsed int `json:"seats_used"`
IsActive bool `json:"is_active"`
ActivatedAt string `json:"activated_at"`
ExpiresAt string `json:"expires_at"`
}
LicenseInfo represents a single license entry.
func GetLicenses ¶
func GetLicenses(ctx context.Context, accessToken string) ([]LicenseInfo, error)
GetLicenses returns the list of active licenses for the account.
type PollResult ¶
type PollResult struct {
Token *TokenResponse
}
PollResult holds the outcome of a completed device code poll.
func PollDeviceCode ¶
func PollDeviceCode(ctx context.Context, deviceCode string, onPoll func(elapsed time.Duration)) (*PollResult, error)
PollDeviceCode polls the auth server until the user authorizes the device code, the timeout expires, or the context is cancelled.
onPoll is called before each poll attempt (use for progress indicators). Pass nil to skip the callback.
Returns ErrPollTimeout if the poll window expires without user action. Returns context.Canceled/context.DeadlineExceeded if ctx is cancelled.
type RotateResult ¶ added in v1.0.14
type RotateResult struct {
NewKey string
OldKey string // raw hex key, NOT the JSON envelope
GraceUntil time.Time
LogEntry string
}
RotateResult holds the outcome of a rotation attempt.
OldKey always holds the raw hex key string that was previously in use (not the JSON envelope). The env var HASURA_GRAPHQL_JWT_SECRET contains that key wrapped in a JSON envelope: {"type":"HS256","key":"<OldKey>"}. Callers that need the envelope format must construct it themselves.
func RotateJWTKey ¶ added in v1.0.14
func RotateJWTKey(currentKey string) (*RotateResult, error)
RotateJWTKey generates a new JWT key, writes a rotation log entry, and returns the result. It does NOT modify any env file — callers are responsible for writing the new key into .env.secrets and reloading Hasura.
currentKey must be the raw hex key (not the JSON envelope). If currentKey is empty, RotateJWTKey returns an error rather than silently storing an empty OldKey — an empty old key most likely indicates the caller did not read the current value before rotating.
The dual-key grace period is signalled via RotateResult.GraceUntil. During [now, GraceUntil] both the old key and the new key are valid for verifying tokens. After GraceUntil, only the new key should be used.
This function NEVER reads from or writes to production secrets files — it only generates the key material and records the event in the rotation log.
type TeamMember ¶
type TeamMember struct {
Name string `json:"name"`
Email string `json:"email"`
Role string `json:"role"`
JoinedAt string `json:"joined_at"`
}
TeamMember represents one member of an account's team.
func GetTeamMembers ¶
func GetTeamMembers(ctx context.Context, accessToken string) ([]TeamMember, error)
GetTeamMembers returns the list of team members for the account.
type TokenResponse ¶
type TokenResponse struct {
AccessToken string `json:"access_token"`
SessionToken string `json:"session_token"`
Email string `json:"email"`
Tier string `json:"tier"`
DisplayName string `json:"display_name,omitempty"`
Bundles []string `json:"bundles,omitempty"`
ExpiresAt string `json:"expires_at"`
}
TokenResponse is returned when the device code is exchanged for a token.
func PollToken ¶
func PollToken(ctx context.Context, deviceCode string) (*TokenResponse, error)
PollToken polls the auth server for the device code exchange result. Returns (nil, nil) if the user hasn't authorized yet (authorization_pending). Returns an error on timeout or other failure.
func RefreshToken ¶
func RefreshToken(ctx context.Context, accessToken string) (*TokenResponse, error)
RefreshToken exchanges an existing session for a new access token.