Documentation
¶
Overview ¶
Package auth implements passwordless, allowlist-closed authentication for team mode: magic-link issuance/verification and server-side cookie sessions. It is a library — no HTTP types cross its boundary; the gateway adapts it to endpoints.
Index ¶
- Constants
- Variables
- type Config
- type GitHubProvider
- type GoogleProvider
- type Principal
- type Service
- func (s *Service) Authenticate(ctx context.Context, rawSessionToken string) (*Principal, error)
- func (s *Service) CompleteSSOLogin(ctx context.Context, provider identity.Provider, ...) (sessionToken string, u *ent.User, err error)
- func (s *Service) LoginWithPassword(ctx context.Context, email, plaintext, userAgent, ip string) (sessionToken string, u *ent.User, err error)
- func (s *Service) Logout(ctx context.Context, rawSessionToken string) error
- func (s *Service) RequestMagicLink(ctx context.Context, email, clientIP string) error
- func (s *Service) SetPassword(ctx context.Context, actor team.Actor, userID uuid.UUID, ...) error
- func (s *Service) VerifyMagicLink(ctx context.Context, rawToken string) (sessionToken string, u *ent.User, err error)
Constants ¶
const ( DefaultTokenTTL = 15 * time.Minute DefaultSessionTTL = 30 * 24 * time.Hour )
Defaults for token and session lifetimes.
const MinPasswordLen = 8
MinPasswordLen is the minimum accepted password length.
Variables ¶
var ErrAccountDisabled = errors.New("account is disabled")
ErrAccountDisabled is returned when an SSO login resolves to a disabled user.
var ErrInvalidCredentials = errors.New("invalid email or password")
ErrInvalidCredentials is returned for any password-login failure — unknown email, no password set, disabled account, or wrong password — deliberately indistinguishable so the endpoint cannot be used to enumerate accounts.
var ErrInvalidSession = errors.New("invalid or expired session")
ErrInvalidSession is returned for an unknown or expired session.
var ErrInvalidToken = errors.New("invalid or expired token")
ErrInvalidToken is returned for any magic-link failure (unknown, expired, or already consumed) — deliberately indistinguishable.
var ErrNotAllowed = errors.New("email not allowed")
ErrNotAllowed is returned when an SSO login resolves a verified email that is not allowlisted. No user or identity row is created — SSO never bypasses the allowlist.
var ErrWeakPassword = errors.New("password too short")
ErrWeakPassword is returned when a password fails the minimum-length policy.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// BaseURL is the externally visible origin; magic links are built as
// BaseURL + "/api/auth/verify?token=...".
BaseURL string
// AppName is shown in the login email.
AppName string
// TokenTTL is the magic-link lifetime (default 15m).
TokenTTL time.Duration
// SessionTTL is the cookie session lifetime (default 30d).
SessionTTL time.Duration
// Logger defaults to slog.Default().
Logger *slog.Logger
}
Config configures the auth service.
type GitHubProvider ¶ added in v0.17.0
type GitHubProvider struct {
// contains filtered or unexported fields
}
GitHubProvider drives GitHub OAuth2 sign-in. Satisfies the gateway package's SSOProvider interface structurally (no import of gateway here).
func NewGitHubProvider ¶ added in v0.17.0
func NewGitHubProvider(clientID, clientSecret, redirectURL string) *GitHubProvider
NewGitHubProvider builds a GitHub OAuth2 client. Unlike Google, no discovery call is made — this never fails.
func (*GitHubProvider) AuthURL ¶ added in v0.17.0
func (g *GitHubProvider) AuthURL(state, _ string) string
AuthURL returns the GitHub consent-screen redirect URL for state (CSRF). GitHub's plain OAuth2 flow has no nonce/ID-token concept; nonce is unused.
type GoogleProvider ¶ added in v0.17.0
type GoogleProvider struct {
// contains filtered or unexported fields
}
GoogleProvider drives Google OIDC sign-in. Satisfies the gateway package's SSOProvider interface structurally (no import of gateway here).
func NewGoogleProvider ¶ added in v0.17.0
func NewGoogleProvider(ctx context.Context, clientID, clientSecret, redirectURL string) (*GoogleProvider, error)
NewGoogleProvider performs OIDC discovery against accounts.google.com — a real network call. Bound it with a context deadline; a discovery failure should be treated as a fatal startup error (an operator who configured Google SSO intends it to work, not silently never offer the button).
func (*GoogleProvider) AuthURL ¶ added in v0.17.0
func (g *GoogleProvider) AuthURL(state, nonce string) string
AuthURL returns the Google consent-screen redirect URL for state (CSRF) and nonce (OIDC replay protection).
type Principal ¶
type Principal struct {
UserID uuid.UUID
Username string
Email string
Superadmin bool
Disabled bool
}
Principal is the authenticated identity resolved from a session.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service performs authentication over the team store.
func NewService ¶
func NewService(st *store.Store, teamSvc *team.Service, mailer mail.Mailer, cfg Config) (*Service, error)
NewService creates the auth service.
func (*Service) Authenticate ¶
Authenticate resolves a session cookie token to a Principal, sliding the expiry when it is stale. Returns ErrInvalidSession for unknown/expired sessions or disabled users.
func (*Service) CompleteSSOLogin ¶ added in v0.17.0
func (s *Service) CompleteSSOLogin(ctx context.Context, provider identity.Provider, subject, verifiedEmail, userAgent, ip string) (sessionToken string, u *ent.User, err error)
CompleteSSOLogin resolves a provider's verified identity to a user and opens a session, mirroring VerifyMagicLink's session-issuance shape. subject is the provider's stable id (OIDC sub / GitHub numeric account id); verifiedEmail must already be provider-verified — this trusts it.
A returning identity (provider, subject already linked) logs into its user directly, with no allowlist re-check — they are already a member. A first-time identity is only linked after IsEmailAllowed passes; a non-allowlisted email is rejected before any user or identity row is created (ErrNotAllowed). Because linking resolves the user by email via team.Service.EnsureUser, a magic-link user who later signs in via SSO with the same email lands in their existing account.
func (*Service) LoginWithPassword ¶ added in v0.18.0
func (s *Service) LoginWithPassword(ctx context.Context, email, plaintext, userAgent, ip string) (sessionToken string, u *ent.User, err error)
LoginWithPassword verifies an email+password credential and, on success, opens a session (mirroring VerifyMagicLink's shape). Every failure — unknown email, no password set, disabled account, or a wrong password — returns ErrInvalidCredentials so callers cannot enumerate accounts. Login is inherently allowlist-gated: a password only exists on an already-created (therefore allowlisted) user.
func (*Service) Logout ¶
Logout revokes the session identified by its raw cookie token. Unknown tokens are a no-op (idempotent logout).
func (*Service) RequestMagicLink ¶
RequestMagicLink issues and emails a login link when the email is allowed.
The response is uniform by contract: this returns nil for both allowed and non-allowed emails (doing nothing in the latter case), so callers cannot enumerate the allowlist. Only malformed input yields an error. Internal failures (token store, mail delivery) are logged, not surfaced — the operator sees them, the requester does not.
func (*Service) SetPassword ¶ added in v0.18.0
func (s *Service) SetPassword(ctx context.Context, actor team.Actor, userID uuid.UUID, current, newPlain string) error
SetPassword sets or replaces a user's password. Authorization mirrors the team user mutations: a user may set their own (userID == actor.UserID), or a superadmin may set anyone's. When a user changes their own password and one is already set, the current password must be supplied and verified; a first-time self-set (no existing password, e.g. after magic-link) and any superadmin set skip that check. newPlain must meet MinPasswordLen.
func (*Service) VerifyMagicLink ¶
func (s *Service) VerifyMagicLink(ctx context.Context, rawToken string) (sessionToken string, u *ent.User, err error)
VerifyMagicLink consumes a token and returns a new session's raw token (to be set as the cookie) plus the resolved user. Any failure returns ErrInvalidToken. A disabled user is rejected.