Documentation
¶
Overview ¶
Package plugin is the public extension surface of Auth-All.
A plugin contributes HTTP routes, schema tables, lifecycle hooks, and OpenAPI operations. A plugin reaches Auth-All only through the Services interface. The official Magic Link plugin uses this package and nothing else, so a third-party plugin has the same capabilities.
Index ¶
- type CreateUserInput
- type CredentialResolver
- type HTTPService
- type IssueTokenInput
- type MFAService
- type PasswordService
- type Plugin
- type Principal
- type PrincipalService
- type PrincipalServices
- type ProtectService
- type Registry
- func (r *Registry) ComponentSchemas() map[string]*openapi.Schema
- func (r *Registry) Extend(e schema.Extension)
- func (r *Registry) Extensions() []schema.Extension
- func (r *Registry) Hooks() *hook.Hooks
- func (r *Registry) OpenAPISchema(name string, s *openapi.Schema)
- func (r *Registry) PluginID() string
- func (r *Registry) Resolver(c CredentialResolver)
- func (r *Registry) Resolvers() []CredentialResolver
- func (r *Registry) Route(rt Route)
- func (r *Registry) Routes() []Route
- func (r *Registry) Schema(t schema.Table)
- func (r *Registry) Services() Services
- func (r *Registry) Tables() []schema.Table
- func (r *Registry) Unit(u schema.Unit)
- func (r *Registry) Units() []schema.Unit
- type RoleConfigurator
- type RoleService
- type RoleServices
- type Route
- type SchemaService
- type Services
- type SessionService
- type TokenService
- type UserService
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CreateUserInput ¶
type CreateUserInput struct {
Email string
DisplayName string
ImageURL string
// EmailVerified marks the address as proven by the calling flow.
EmailVerified bool
}
CreateUserInput describes a new user.
type CredentialResolver ¶ added in v0.3.0
type CredentialResolver interface {
// Claims reports whether the resolver owns the bearer value. It must look
// at the shape of the value only, and it must make no database call.
Claims(bearer string) bool
// Resolve returns the principal of the bearer value. It returns an error
// when the credential does not authenticate.
Resolve(ctx context.Context, bearer string) (*Principal, error)
}
CredentialResolver turns a bearer value into a principal. A plugin registers one with Registry.Resolver.
Auth-All asks each resolver in registration order. When no resolver claims the value, Auth-All treats it as a session token, so every v1 bearer client keeps working.
type HTTPService ¶
type HTTPService interface {
// CheckOrigin rejects a state-changing request from an untrusted origin.
CheckOrigin(r *http.Request) error
// DecodeJSON reads a JSON request body.
DecodeJSON(r *http.Request, dst any) error
// WriteJSON writes a JSON response.
WriteJSON(w http.ResponseWriter, status int, body any)
// WriteError writes the public error envelope.
WriteError(w http.ResponseWriter, err error)
// SafeRedirect returns candidate when it points at a trusted origin, and
// fallback otherwise.
SafeRedirect(candidate, fallback string) string
// ClientIP returns the request IP for rate-limit keys.
ClientIP(r *http.Request) string
}
HTTPService exposes the request helpers of Auth-All.
type IssueTokenInput ¶
type IssueTokenInput struct {
// Kind separates token namespaces, for example "magic-link".
Kind string
// UserID is optional. A flow for an unknown address leaves it nil.
UserID *string
// Identifier is the subject of the token, normally a normalized email.
Identifier string
// TTL is the token lifetime.
TTL time.Duration
// ReplaceExisting removes outstanding tokens of the same kind and
// identifier before it issues the new token.
ReplaceExisting bool
}
IssueTokenInput describes a one-time token.
type MFAService ¶ added in v0.2.0
type MFAService interface {
// Challenge reports whether the user must pass a second factor, and
// returns a single-use challenge token when so. The caller must issue no
// session while required is true.
Challenge(ctx context.Context, user *store.User) (token string, required bool, err error)
// SetCookie writes the challenge into the short-lived challenge cookie. A
// redirect flow uses it, because a token in a query parameter reaches the
// browser history, the server log, and any leaked Referer header.
SetCookie(w http.ResponseWriter, token string)
// MarkRedirect adds the marker that tells the application to ask for a
// code. The marker names no token, so it is safe in a URL.
MarkRedirect(target string) string
}
MFAService is the second-factor gate.
A plugin that authenticates a user calls Challenge before it issues a session. A user with a live second factor must reach no session until they prove one code, so a plugin that skips this leaves a bypass of the gate.
type PasswordService ¶ added in v0.3.0
type PasswordService interface {
// CheckPassword reports whether a password meets the configured policy.
CheckPassword(password string) error
// HashPassword returns the argon2id hash of a password.
HashPassword(password string) (string, error)
}
PasswordService applies the password policy and the hash parameters of Auth-All, so a plugin writes the same credential as a core route.
type Plugin ¶
type Plugin interface {
// ID returns the stable plugin identifier.
ID() string
// Register contributes routes, schema, hooks, and OpenAPI operations.
Register(r *Registry) error
}
Plugin is one Auth-All extension.
type Principal ¶ added in v0.3.0
type Principal struct {
// User is the owner of the credential. It must not be nil.
User *store.User
// Session is nil for a credential that is no session.
Session *store.Session
// APIKey is nil for a session credential.
APIKey *store.APIKey
// Role is the effective role of the request.
Role string
// Method names the authentication method, for example "api_key".
Method string
}
Principal is the authenticated caller of one request. A credential resolver returns it. Auth-All copies it into the request context.
type PrincipalService ¶ added in v0.3.0
type PrincipalService interface {
// Current returns the principal of the request context. It returns nil
// when no middleware authenticated the request.
Current(ctx context.Context) *Principal
}
PrincipalService reads the principal of one request.
type PrincipalServices ¶ added in v0.3.0
type PrincipalServices interface {
Principals() PrincipalService
}
PrincipalServices exposes the principal service.
type ProtectService ¶ added in v0.3.0
type ProtectService interface {
// Protect refuses a request with no principal, and it refuses an unsafe
// cross-site request that a cookie authenticated.
Protect(next http.Handler) http.Handler
}
ProtectService wraps a handler with the Auth-All authentication middleware. The wrapped handler runs the origin check of the host routes.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry receives the contributions of one plugin.
func NewRegistry ¶
NewRegistry returns a registry for one plugin. Auth-All calls this during construction.
func (*Registry) ComponentSchemas ¶
ComponentSchemas returns the contributed component schemas.
func (*Registry) Extend ¶ added in v0.3.0
Extend adds columns and indexes to a table that another owner declared. The plugin also declares the migration unit that adds them to a database that exists.
func (*Registry) Extensions ¶ added in v0.3.0
Extensions returns the contributed table extensions.
func (*Registry) OpenAPISchema ¶
OpenAPISchema contributes one reusable component schema.
func (*Registry) Resolver ¶ added in v0.3.0
func (r *Registry) Resolver(c CredentialResolver)
Resolver contributes one credential resolver.
func (*Registry) Resolvers ¶ added in v0.3.0
func (r *Registry) Resolvers() []CredentialResolver
Resolvers returns the contributed credential resolvers.
type RoleConfigurator ¶ added in v0.3.0
type RoleConfigurator interface {
// SetRoles installs the ordered hierarchy and the default role.
SetRoles(names []string, defaultRole string) error
}
RoleConfigurator installs a role hierarchy in the core. The roles plugin calls it during registration.
type RoleService ¶ added in v0.3.0
type RoleService interface {
// Names returns the roles from the lowest to the highest.
Names() []string
// Default returns the role of a user whose role is empty.
Default() string
// Rank returns the position of a role. A role that the configuration does
// not name ranks below every role, so its rank is negative.
Rank(role string) int
// AtLeast reports whether role ranks equal to or above min.
AtLeast(role, min string) bool
}
RoleService reads the configured role hierarchy.
type RoleServices ¶ added in v0.3.0
type RoleServices interface {
Roles() RoleService
}
RoleServices exposes the role service.
type Route ¶
type Route struct {
// Method is the HTTP method.
Method string
// Path is relative to the configured Auth-All base path and starts with /.
Path string
// Handler serves the route.
Handler http.Handler
// Operation documents the route. A route without an operation stays out of
// the OpenAPI document and out of the generated client.
Operation *openapi.Operation
}
Route is one HTTP route contributed by a plugin.
type SchemaService ¶ added in v0.3.0
type SchemaService interface {
// SchemaOptions returns the physical options of the effective schema.
SchemaOptions() schema.Options
}
SchemaService reports the physical schema options of the instance. A plugin that owns a table uses it, so the table takes the host table prefix.
type Services ¶
type Services interface {
// Store returns the configured storage adapter.
Store() store.Store
// Email returns the configured email sender.
Email() email.Sender
// Events returns the observability emitter.
Events() *events.Emitter
// Now returns the configured clock.
Now() time.Time
// BasePath returns the mounted base path, for example /api/auth.
BasePath() string
// BaseURL returns the absolute public base URL of the application.
BaseURL() string
// RateLimiter returns the configured limiter. It is never nil.
RateLimiter() ratelimit.Limiter
// Logger returns the configured logger.
Logger() *slog.Logger
// Users exposes user operations that run the configured hooks.
Users() UserService
// Sessions exposes session operations.
Sessions() SessionService
// Tokens exposes one-time token operations.
Tokens() TokenService
// HTTP exposes the request helpers Auth-All uses for its own routes.
HTTP() HTTPService
// MFA exposes the second-factor gate. A plugin that authenticates a user
// must consult it before it issues a session.
MFA() MFAService
}
Services is everything Auth-All exposes to a plugin. A plugin gets no other access to Auth-All internals.
type SessionService ¶
type SessionService interface {
// Issue creates a session for the user and writes the session cookie.
// Method names the authentication method for hooks and events.
Issue(ctx context.Context, w http.ResponseWriter, r *http.Request, user *store.User, method string) (*store.Session, error)
// Current resolves the session of a request. It returns nil values when no
// valid session exists.
Current(ctx context.Context, r *http.Request) (*store.Session, *store.User, error)
// Revoke deletes one session.
Revoke(ctx context.Context, sessionID string) error
// RevokeAll deletes every session of one user and returns the count.
RevokeAll(ctx context.Context, userID string) (int, error)
// Clear removes the session cookie.
Clear(w http.ResponseWriter)
}
SessionService exposes session operations.
type TokenService ¶
type TokenService interface {
Issue(ctx context.Context, in IssueTokenInput) (plaintext string, token *store.Token, err error)
// Consume atomically consumes a token. Two concurrent calls for the same
// token produce at most one success.
Consume(ctx context.Context, kind, plaintext string) (*store.Token, error)
// Peek returns a token and consumes nothing. It reports an invalid token
// for a value that is missing, expired, or already consumed.
//
// A confirmation page calls Peek, so a repeated page load and a mail
// scanner that pre-fetches a link do not destroy the token.
Peek(ctx context.Context, kind, plaintext string) (*store.Token, error)
}
TokenService exposes one-time token operations. The plaintext token exists only in the return value of Issue. Auth-All stores only its hash.
type UserService ¶
type UserService interface {
ByID(ctx context.Context, id string) (*store.User, error)
// ByEmail looks a user up by the normalized form of the address.
ByEmail(ctx context.Context, address string) (*store.User, error)
// Create inserts a user and runs the user creation hooks.
Create(ctx context.Context, in CreateUserInput) (*store.User, error)
// MarkEmailVerified records proven ownership of the user email address. It
// changes no other row. A passwordless flow calls ProveEmailOwnership
// instead.
MarkEmailVerified(ctx context.Context, userID string) error
// DeleteCredential removes the password credential of a user. It succeeds
// for a user that has no password credential.
DeleteCredential(ctx context.Context, userID string) error
// ProveEmailOwnership records proven control of the address of a user.
//
// A passwordless flow calls it after the flow proves that the person
// controls the address. When the address was not verified yet, somebody can
// have set a password and started a session before the proof. The method
// therefore deletes the password credential of the user, revokes every
// session of the user, and marks the address verified. It performs the
// three steps in one transaction.
//
// The method does nothing for a user whose address is already verified, so
// a normal repeat sign-in keeps its password and its sessions.
//
// A plugin that proves control of an address must call this method before
// it issues a session.
ProveEmailOwnership(ctx context.Context, userID string) error
}
UserService exposes user operations.