Documentation
¶
Overview ¶
Package auth is togo's base authentication system: JWT token auth, bcrypt passwords, a self-contained users store (via the ORM), multi-guard, and roles/permissions (RBAC). It's the default auth driver; Supabase/Firebase/ OAuth/WorkOS ship as driver plugins that depend on this package.
Install: `togo install togo-framework/auth` (blank-import registers it).
Index ¶
- Constants
- Variables
- func RegisterLoginMethod(m LoginMethod)
- func RegisterSecondFactor(f SecondFactor)
- func RegisterSessionStore(name string, f func(k *togo.Kernel) (SessionStore, error))
- type Authenticator
- type Guard
- type Identity
- type LoginMethod
- type SecondFactor
- type Service
- func (s *Service) Authenticate(r *http.Request) (*Identity, error)
- func (s *Service) CompleteLogin(w http.ResponseWriter, ctx context.Context, userID string)
- func (s *Service) CreateUser(ctx context.Context, email, password string, roles []string) (*Identity, error)
- func (s *Service) FindByEmail(ctx context.Context, email string) (*Identity, error)
- func (s *Service) FindOrCreateByEmail(ctx context.Context, email string) (*Identity, error)
- func (s *Service) Guard(name string) *Guard
- func (s *Service) IssueLoginChallenge(userID string) (string, error)
- func (s *Service) IssueSession(w http.ResponseWriter, id Identity) (string, error)
- func (s *Service) IssueToken(id Identity) (string, error)
- func (s *Service) Middleware(next http.Handler) http.Handler
- func (s *Service) RegisterGuard(name string, a Authenticator)
- func (s *Service) RequirePermission(perm string) func(http.Handler) http.Handler
- func (s *Service) RequireRole(role string) func(http.Handler) http.Handler
- func (s *Service) SecondFactorRequired(ctx context.Context, userID string) bool
- func (s *Service) SetPassword(ctx context.Context, userID, password string) error
- func (s *Service) SetRoles(ctx context.Context, userID string, roles []string) error
- func (s *Service) UserByID(ctx context.Context, id string) (*Identity, error)
- func (s *Service) Verify(token string) (*Identity, error)
- func (s *Service) VerifyContext(ctx context.Context, token string) (*Identity, error)
- func (s *Service) VerifyLoginChallenge(token string) (string, error)
- type SessionStore
- type User
Constants ¶
const ( EventRegistered = "auth.registered" EventLogin = "auth.login" EventLogout = "auth.logout" EventPasswordChanged = "auth.password_changed" EventLoginFailed = "auth.login_failed" // EventPasswordResetRequested carries {user_id, email, token, expires_at}. // A mail/SMS/notifications listener delivers the reset link; the token is // never returned over HTTP. EventPasswordResetRequested = "auth.password_reset_requested" EventPasswordReset = "auth.password_reset" // EventLoginChallenged fires when a password login is held for 2FA. EventLoginChallenged = "auth.login_challenged" // Admin user-management events. Every payload is a map[string]string that // names the acting administrator (actor_id) and never carries a secret: no // token, link or password. EventUserCreated = "auth.user_created" // actor_id, target_id, user_id, email EventUserUpdated = "auth.user_updated" // actor_id, target_id, user_id, fields (names only) EventUserDeleted = "auth.user_deleted" // actor_id, target_id, user_id, email // EventUserImpersonated: actor_id, target_id, at, expires_at. EventUserImpersonated = "auth.user_impersonated" // EventImpersonationEnded: actor_id, target_id, at. EventImpersonationEnded = "auth.impersonation_ended" // EventMagicLinkIssued: actor_id, user_id, expires_at. The link itself goes // only to the administrator who asked for it, in the HTTP response. EventMagicLinkIssued = "auth.magic_link_issued" // EventMagicLinkRedeemed: actor_id (the issuing admin), target_id, at. EventMagicLinkRedeemed = "auth.magic_link_redeemed" // EventAdminCrossControl is fired for every operation one administrator // performs on another under the AUTH_ADMIN_CROSS_CONTROL exception: // actor_id, target_id, operation, policy ("AUTH_ADMIN_CROSS_CONTROL"). EventAdminCrossControl = "auth.admin_cross_control" // EventAdminResetLinkIssued: actor_id, user_id, expires_at (no link). EventAdminResetLinkIssued = "auth.admin_reset_link_issued" // EventAdminPromoted fires when an account becomes an administrator through // the admin API: actor_id, target_id, tainted (comma-joined fields whose // email/password another administrator set, empty if none), accepted // ("true" when the promoter explicitly accepted that, else "false"). EventAdminPromoted = "auth.admin_promoted" // EventAdminDemoted fires when an administrator loses the role through the // admin API: actor_id, target_id. EventAdminDemoted = "auth.admin_demoted" // EventCredentialRefused fires when an admin-issued credential (a reset link // redemption or an admin-set password) is refused because the account is, or // became, an administrator or changed under the operation: type ("reset" or // "set-password"), issuer (the administrator), target_id. Never the token. EventCredentialRefused = "auth.credential_refused" //#nosec G101 -- an event name, not a credential )
Auth lifecycle events fired on the kernel hook bus. Apps subscribe via k.Hooks.On(auth.EventLogin, 50, fn) to inject behavior — audit logging, welcome mail, post-login/redirect decisions, etc. Listeners run in priority order.
const EventOTPRequested = "auth.otp.requested"
EventOTPRequested carries {subject, code, purpose} for delivery plugins.
const LoginChallengeTTL = 5 * time.Minute
LoginChallengeTTL is how long a user has to enter their code.
const MagicLinkTTL = 15 * time.Minute
MagicLinkTTL is how long an admin-issued magic link stays valid.
const PasswordResetTTL = 30 * time.Minute
PasswordResetTTL is how long a reset link stays valid.
const SessionCookie = "togo_session"
SessionCookie is the name of the HttpOnly cookie holding the session token.
Variables ¶
var ErrInvalidCredentials = errors.New("invalid credentials")
ErrInvalidCredentials is returned when email/password don't match.
var ErrUserNotFound = errors.New("user not found")
ErrUserNotFound is returned when an account operation names no account.
Functions ¶
func RegisterLoginMethod ¶ added in v0.8.0
func RegisterLoginMethod(m LoginMethod)
RegisterLoginMethod advertises a sign-in method (call from a plugin init/provider).
func RegisterSecondFactor ¶ added in v0.9.0
func RegisterSecondFactor(f SecondFactor)
RegisterSecondFactor adds a check consulted at login (auth-mfa registers one).
func RegisterSessionStore ¶ added in v0.6.0
func RegisterSessionStore(name string, f func(k *togo.Kernel) (SessionStore, error))
RegisterSessionStore registers a server-side session driver (call from init()).
Types ¶
type Authenticator ¶
type Authenticator interface {
Attempt(ctx context.Context, email, password string) (*Identity, error)
ByID(ctx context.Context, id string) (*Identity, error)
}
Authenticator verifies credentials and loads identities. Drivers (supabase, oauth, …) implement this; the default is DB + bcrypt.
type Guard ¶
type Guard struct {
Name string
Auth Authenticator
}
Guard pairs a name with an Authenticator — enabling multi-guard setups.
type Identity ¶
type Identity struct {
ID string `json:"id"`
Email string `json:"email"`
Roles []string `json:"roles"`
Permissions []string `json:"permissions"`
Guard string `json:"guard"`
// Impersonator is the id of the administrator acting as this identity. It is
// empty for every real login, and set only on tokens issued through
// POST /api/auth/admin/users/{id}/impersonate (the token's `act.sub` claim).
Impersonator string `json:"impersonator,omitempty"`
// TokenID is the token's jti. It is set only on impersonation tokens, which
// are revocable.
TokenID string `json:"-"`
}
Identity is the authenticated principal exposed to the app.
func IdentityFrom ¶
IdentityFrom returns the authenticated identity from the request context.
type LoginMethod ¶ added in v0.8.0
type LoginMethod struct {
Name string `json:"name"` // e.g. "google", "dev"
Label string `json:"label"` // button text
Type string `json:"type"` // "oauth" | "dev" | "sso"
URL string `json:"url"` // endpoint to hit/redirect
}
LoginMethod is an alternative sign-in option (OAuth provider, dev login, SSO …) advertised to the frontend so it only renders methods that are actually configured. Plugins register theirs via RegisterLoginMethod.
type SecondFactor ¶ added in v0.9.0
SecondFactor reports whether a user must pass an extra factor at login.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the auth runtime stored on the kernel (k.Get("auth")).
func FromKernel ¶
FromKernel fetches the auth service from the kernel container.
func New ¶
New builds the service, ensures the users table exists, and registers the default DB-backed guard. It fails closed in production when no strong secret is configured.
func (*Service) Authenticate ¶ added in v0.9.1
Authenticate resolves the request's identity (bearer JWT, personal access token, or session cookie) without writing a response. Apps that layer their own policy on top of auth — public and protected routes on one router, or roles mapped to app actors — use it instead of Middleware, which answers 401.
func (*Service) CompleteLogin ¶ added in v0.9.0
CompleteLogin issues the session for a user who has passed every factor. Exported so second-factor plugins finish logins the same way.
func (*Service) CreateUser ¶ added in v0.9.1
func (s *Service) CreateUser(ctx context.Context, email, password string, roles []string) (*Identity, error)
CreateUser adds an account with a password and roles, for seeders and bootstrap steps that must not go through public registration. It fires EventRegistered like registration does.
func (*Service) FindByEmail ¶ added in v0.9.0
FindByEmail returns the identity for an existing account, or nil if there is none. Unlike FindOrCreateByEmail it never creates an account, so it is the one to use for flows a stranger can trigger (magic links, OTP login).
func (*Service) FindOrCreateByEmail ¶ added in v0.5.0
FindOrCreateByEmail returns the identity for email, creating a passwordless account if none exists (used by SSO/OAuth where there's no local password).
func (*Service) IssueLoginChallenge ¶ added in v0.9.0
IssueLoginChallenge mints a signed, short-lived challenge for a user who has passed their password but still owes a second factor. It is not a session.
func (*Service) IssueSession ¶ added in v0.5.0
IssueSession issues a JWT for id, sets the session cookie, and fires EventLogin. Returns the token (drivers may also redirect).
func (*Service) IssueToken ¶
IssueToken signs a JWT for an identity.
func (*Service) Middleware ¶
Middleware authenticates the request from its bearer token and stores the Identity in context. 401 if the token is missing/invalid.
func (*Service) RegisterGuard ¶
func (s *Service) RegisterGuard(name string, a Authenticator)
RegisterGuard adds a named guard (multi-guard support).
func (*Service) RequirePermission ¶
RequirePermission guards a route by permission.
func (*Service) RequireRole ¶
RequireRole guards a route by role.
func (*Service) SecondFactorRequired ¶ added in v0.9.0
SecondFactorRequired reports whether the user must complete 2FA before a session is issued: core TOTP enabled, or any registered second factor.
func (*Service) SetPassword ¶ added in v0.9.3
SetPassword replaces an account's password, for an admin resetting a user who cannot receive a reset link. The password policy applies, and EventPasswordChanged fires as it does for a self-service change.
func (*Service) SetRoles ¶ added in v0.9.1
SetRoles replaces an account's roles. Registration creates accounts with no roles, so this is how an app grants them (an admin screen, a seeder, or a bootstrap step).
func (*Service) UserByID ¶ added in v0.9.1
UserByID loads an account's current identity from the store. Roles and permissions in a JWT are a snapshot from sign-in; apps that must honour a role change or revocation immediately re-read them here.
func (*Service) Verify ¶
Verify parses a token into an Identity. Enforces HS256, a required expiry, and the issuer — rejecting alg-confusion, unexpiring, and foreign tokens.
func (*Service) VerifyContext ¶ added in v0.10.0
VerifyContext parses and validates a token, then revalidates it against the database. It is the one authoritative verification path; Verify delegates to it.
Strict session revalidation (base driver): the token proves who the caller is and that it was issued by this service; the database decides what the caller may do now. The account is loaded by id on every call, a missing account is refused, and the identity's email, roles and permissions are replaced by the stored values. Every consumer of the identity (RequireRole, RequirePermission, IdentityFrom, requireAdmin, apps using Authenticate) is therefore database-authoritative without any change on their side. An impersonation or magic-link session additionally needs its jti unrevoked and its administrator still an administrator.
type SessionStore ¶ added in v0.6.0
type SessionStore interface {
Put(ctx context.Context, sid, token string, ttl time.Duration) error
Get(ctx context.Context, sid string) (string, bool, error)
Delete(ctx context.Context, sid string) error
}
SessionStore persists a token under a session id with a TTL.
type User ¶
type User struct {
ID string `db:"id" json:"id"`
Email string `db:"email" json:"email"`
PasswordHash string `db:"password_hash" json:"-"`
Roles string `db:"roles" json:"roles"`
Permissions string `db:"permissions" json:"permissions"`
CreatedAt string `db:"created_at" json:"created_at"`
}
User is the stored account. All columns are TEXT for cross-driver portability.