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 ¶
- func WithOrganization(ctx context.Context, value OrganizationContext) context.Context
- type CreateUserInput
- type CredentialResolver
- type HTTPService
- type IssueTokenInput
- type MFAService
- type OrganizationConfigurator
- type OrganizationContext
- 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 ¶
func WithOrganization ¶ added in v0.4.0
func WithOrganization(ctx context.Context, value OrganizationContext) context.Context
WithOrganization returns a context that carries the active organization.
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 OrganizationConfigurator ¶ added in v0.4.0
type OrganizationConfigurator interface {
// EnableOrganizations makes the credential read load the membership.
EnableOrganizations() error
}
OrganizationConfigurator turns the organization read on in the core. The organizations plugin calls it during registration, so a credential read loads the active organization and the membership in the same round trip.
type OrganizationContext ¶ added in v0.4.0
type OrganizationContext struct {
// Organization is the active organization of the session.
Organization *store.Organization
// Membership is the membership of that organization.
Membership *store.Membership
// Permissions holds the extra statements that the credential read
// resolved, for a custom role and for every team role of the member.
Permissions []string
}
OrganizationContext carries the active organization of one request. The core puts it in the request context after it resolves the credential, so a plugin and a host handler read it with no store access.
func OrganizationFrom ¶ added in v0.4.0
func OrganizationFrom(ctx context.Context) (OrganizationContext, bool)
OrganizationFrom returns the active organization of the context. The second result is false when no organization is active, which is default deny.
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
// Organization is the active organization of the request. It is nil when
// no organization is active.
Organization *store.Organization
// Membership is the membership of the active organization. It is nil when
// no organization is active. Its Permissions field holds the statements
// that the credential read resolved, for a custom role and for every team
// role of the member.
Membership *store.Membership
}
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.