auth

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package auth is the authentication foundation, assembled.

The packages under it are the parts: password hashing, session issue and rotation, API keys, permission resolution, the endpoints, and the Postgres stores behind all of them. Each is separately usable and separately testable, and putting them together is the same fifteen lines in every project.

This package is those fifteen lines. Hand it a pool:

front, err := auth.New(auth.Config{Pool: pool})
if err != nil {
	return nil, err
}

mux := api.Register(api.Handlers{
	Server: api.Server{Auth: front},
	…
})

One field, because the two halves cannot be wired separately without one of them eventually being forgotten: Register takes its GetClaims from it and mounts its routes on the mux it returns. It is typed as an interface the generated package declares, so a project with no authentication does not depend on this module to serve a table.

That is a working login, logout, refresh, password reset, email verification, session list and API key endpoint, over the tables `rig setup-project` wrote, with lockout and rate limits counted in the database. Nothing about it is generated and nothing about it is yours to maintain.

The fields below are the choices an application actually has to make — where the tenant comes from, how long a session lasts, who sends the mail. Anything further is a reason to build the parts yourself: every one of them is exported, this package holds no logic of its own, and Parts returns what it made so that a project can go halfway rather than starting over.

Index

Constants

View Source
const DefaultBasePath = "/auth"

DefaultBasePath is where the endpoints mount when nothing says otherwise.

View Source
const DefaultCacheTTL = 30 * time.Second

DefaultCacheTTL is thirty seconds.

Long enough that somebody clicking through an application is served from memory throughout, and short enough that the window it actually covers — a replica that lost the channel between a revocation and its reconnect — is measured in seconds.

View Source
const GrantsTopic = "grants"

GrantsTopic is the name grant invalidations travel under.

Exported for the reason session.TokenTopic and apikey.KeyTopic are: it is half of an agreement, and somebody reading the channel with psql to find out whether anything is being published needs to know what they are looking at.

View Source
const TenantHeader = "X-Tenant-Id"

TenantHeader is where the default tenant resolver looks.

A request has to say which tenant it is for before there is a session to read it from: the same address may exist in two of them, and which one is being signed into is part of the request. A header is the least surprising default and the easiest to try; a subdomain or a path segment is a Config.Tenant away.

Variables

This section is empty.

Functions

func TenantFromHeader

func TenantFromHeader(r *http.Request) (uuid.UUID, error)

TenantFromHeader is the default tenant resolver.

Types

type Auth

type Auth struct {
	// contains filtered or unexported fields
}

Auth is the assembled foundation.

func New

func New(cfg Config) (*Auth, error)

New assembles the foundation over a pool.

func (*Auth) Claims

func (a *Auth) Claims(r *http.Request) (tenancy.Claims, error)

Claims identifies the caller of a request.

Together with Mount this satisfies the Authenticator interface the generated API package declares, which is how api.Server.Auth takes a *Auth without the generated code importing this module.

This is what to give the generated server as GetClaims. It accepts a session token or an API key, resolves the caller's permissions, and refuses anything else — so a token, a key, and the roles attached to either mean the same thing in a generated handler as they do in the endpoints here.

func (*Auth) Close

func (a *Auth) Close(ctx context.Context) error

Close stops the invalidation channel and waits for it.

Register it with the shutdown, within a timeout — there is nothing in flight worth finishing, so this only needs long enough to close a connection:

app.CloseWithin("auth", 5*time.Second, front.Close)

A project that configured no cache has nothing to close and this returns at once, which is what makes registering it unconditionally the right thing to write.

Forgetting it is not a correctness problem, and that is deliberate. A bus that is not running reports itself as not live, and a cache that is not live reads through and stores nothing — so the failure mode of every mistake around this lifecycle is a slower server, never a server answering out of a cache nobody can withdraw from.

func (*Auth) DispatchMail

func (a *Auth) DispatchMail(ctx context.Context) (account.MailReport, error)

DispatchMail is one pass of the link queue: claim what is due, send it, mark it.

**This is the guarantee behind every link rig mints, and nothing runs it for you.** Register it as a task an operator's cron invokes — a subcommand rather than a goroutine, so it is a cron job rather than something racing itself in every replica. With Mail.Queue set and no such entry, links are queued and never sent.

An empty report and no error for a project that did not turn the queue on, so registering the task before deciding costs nothing.

func (*Auth) Mount

func (a *Auth) Mount(mux *http.ServeMux)

Mount registers the endpoints on a mux.

It takes the same mux the generated API is registered on, so that one server answers both and the paths cannot collide silently: /auth is separate from the API's base path, and Go's router refuses a duplicate pattern.

Setting api.Server.Auth calls this for you, which is the ordinary way. Call it directly only when the API layer is not rig's — mounting these endpoints beside a hand-written router, for instance.

func (*Auth) Parts

func (a *Auth) Parts() Parts

Parts are the pieces this assembled, for the things an endpoint cannot do for you.

func (*Auth) Providers

func (a *Auth) Providers() []string

Providers are the configured sign-in providers, for a sign-in page to draw a button per name. Empty when none are wired up.

The names are what the routes take: a provider called Google is reached at <BasePath>/oauth/google/start, lowercased.

func (*Auth) PruneLog

func (a *Auth) PruneLog(ctx context.Context) (int, error)

PruneLog deletes authentication log entries older than Config.LogRetention and reports how many went. Zero retention keeps everything and prunes nothing.

A task somebody runs, not a goroutine this starts. Housekeeping that schedules itself inside the server is housekeeping every replica does at once, and this one takes a lock on the table the whole authentication path writes to. `serve.Config.Tasks` makes it a subcommand for a cron job; the generated wiring registers it for a project whose rig.yaml set a window.

func (*Auth) PruneMail

func (a *Auth) PruneMail(ctx context.Context) (int, error)

PruneMail removes deliveries that are done and older than Mail.Retention, in the same task that dispatches them — the way the file sweeper's two rules share one.

Never a pending row, and never a link row: those are the record of who was invited and when.

func (*Auth) ReleaseMailClaims

func (a *Auth) ReleaseMailClaims(ctx context.Context) (int, error)

ReleaseMailClaims gives back every lease this process holds, for the close half.

A lease given back is a row the next dispatcher takes immediately, rather than one that waits out the TTL. The attempts are not given back: the sends this process made were made.

func (*Auth) Session

func (a *Auth) Session(r *http.Request) (*session.Token, error)

Session is the token a request carried, when it carried one.

func (*Auth) StopClaimingMail

func (a *Auth) StopClaimingMail()

StopClaimingMail stops taking new deliveries while the pass in flight finishes, for the drain half of a shutdown.

type CacheOptions

type CacheOptions struct {
	// Enabled turns it on. The zero value reads a row per authenticated
	// request, which is what rig did before this existed and is still the
	// correct choice for a deployment that is not paying for it.
	Enabled bool

	// TTL is how long an answer may be reused when no invalidation arrives.
	//
	// It is the backstop and not the guarantee. The guarantee is the channel: a
	// revocation publishes on the transaction that performed it, so every
	// replica that is listening has forgotten by the time that transaction
	// commits. This is what remains for a replica that was not.
	//
	// Zero takes [DefaultCacheTTL].
	TTL time.Duration

	// Channel is the Postgres channel invalidations travel on. Zero takes
	// [cache.DefaultChannel].
	//
	// It has to be an identifier — it reaches Postgres both quoted in a LISTEN
	// and as a parameter to pg_notify — and [cache.NewBus] panics on one that is
	// not, at startup, rather than failing at the first notification.
	Channel string

	// MaxEntries bounds each cache. Zero takes [cache.DefaultMaxEntries].
	MaxEntries int

	// Logger receives one line when invalidations stop arriving and one when
	// they resume. Zero uses [slog.Default].
	//
	// Worth setting, and worth reading. It is the only way anybody learns that
	// this module has gone back to reading a row per request: the fallback is
	// correct and silent, so nothing else about the server will say so.
	Logger *slog.Logger
}

CacheOptions is whether rig holds its own per-request reads in memory, and what that costs.

The reads are the two this module makes on behalf of every caller: resolving a session token, and resolving an API key. Both are rig's end to end — it performs the read and it performs every write that invalidates it — which is the whole reason they are the ones cached. An application supplies no invalidation, registers no hook and has no write path it can forget, because there is none belonging to it.

The read this deliberately does not cover is Config.Grants, which is an application's own function over an application's own tables. rig cannot see those writes, so caching that answer would mean an application publishing its own invalidations — and one forgotten write path there is a withdrawn permission that goes on working. Parts.Cache is the bus for a project that decides to take that on knowingly.

type Config

type Config struct {
	// Pool is the database holding the foundation tables.
	Pool *pgxpool.Pool

	// Tenant resolves which tenant a request belongs to. The default reads the
	// X-Tenant-Id header.
	//
	// Replace it with whatever your deployment actually uses — a subdomain, a
	// path segment, a claim in a proxy's header. It is consulted only where a
	// tenant cannot be known some other way: login, a password reset, the OAuth
	// start. Once there is a session the tenant comes from the token.
	Tenant func(*http.Request) (uuid.UUID, error)

	// BasePath prefixes the endpoints. Default [DefaultBasePath].
	//
	// The provider routes sit under it: BasePath + "/oauth/{provider}/start".
	BasePath string

	// OnError renders a failure. The default is this package's own; pass your
	// API's error mapper so that an authentication failure looks like every
	// other failure.
	OnError func(w http.ResponseWriter, r *http.Request, err error)

	// AccessTTL, RefreshTTL and RememberTTL are how long the two kinds of token
	// last, and how long "remember me" extends the refresh token to. Zero means
	// the session package's defaults: 10 minutes, 12 hours, 30 days.
	AccessTTL   time.Duration
	RefreshTTL  time.Duration
	RememberTTL time.Duration

	// RotationLeeway is how long a refresh token stays usable after it has been
	// exchanged. Zero is [session.DefaultRotationLeeway], thirty seconds.
	//
	// Without one, a dropped response is a logout: the client never received the
	// new pair, retries with the old one, and has its whole family revoked for a
	// network blip. See [session.Config.RotationLeeway].
	RotationLeeway time.Duration

	// Cache holds the reads this package makes on every request — verifying a
	// session, verifying an API key — and withdraws them when they stop being
	// true.
	//
	// The zero value caches nothing, which is a row read per authenticated
	// request and the behaviour rig had before this existed. Setting
	// [CacheOptions.Enabled] is not a time-to-live over authentication: it opens
	// a Postgres channel, and every revocation this package performs publishes
	// on the transaction that performed it, so a permission taken away stops
	// working at the moment the change commits rather than when a timer expires.
	//
	// It is [New]'s to own. The bus is built, registered and started here and
	// closed by [Auth.Close], so there is no cache to wire, no publish to
	// remember, and no write path that can be left out.
	Cache CacheOptions

	// Policy is the password policy. The zero value is a minimum length of 12
	// and no composition rules, which is current advice.
	Policy password.Policy

	// BreachChecker is consulted when somebody sets a password. Nil skips the
	// check. password.HIBP is a k-anonymity client for haveibeenpwned that fails
	// open, which is the right way round: a network problem should not stop
	// somebody changing their password.
	BreachChecker password.BreachChecker

	// Notifier sends the mail a flow needs — a reset link, a verification link,
	// an invitation.
	//
	// Nil means none is sent, and it means that silently: account.NoNotifier is
	// substituted and returns success from every method, so in production nobody
	// can reset a password and nothing anywhere says so. Set one. (The one
	// combination that is refused is Mail.Queue with no notifier, which would
	// write rows nothing could ever send.)
	Notifier account.Notifier

	// Mail is the queue for those links: off by default, and turning it on means
	// running a job.
	//
	// Off, a link is minted in the request that asked for it and handed straight
	// to the Notifier, so a provider having a bad minute fails that request,
	// spends the caller's rate-limit budget, and kills a token that was already
	// minted. On, the link is written to the database in the same transaction and
	// sent by [Auth.DispatchMail] — which nothing runs on its own. Turning it on
	// without a cron entry is turning mail off.
	//
	// The trade in the other direction is latency: a queued reset mail arrives up
	// to one dispatch interval late.
	Mail MailOptions

	// RequireVerifiedEmail refuses a login until the address is verified.
	RequireVerifiedEmail bool

	// Grants answers what an account may do. Nil means nobody holds anything,
	// which makes every endpoint carrying a permission answer 403.
	//
	// This is where an application's authorization model plugs in, and rig ships
	// none. It derives the permission *keys* from the schema — api.Permissions()
	// in the generated package is the whole catalogue — and generates the check
	// that reads them off the claims. Deciding who holds which is yours: a table
	// of roles, a switch on the account's level, a claim on a federated token, a
	// map in a constant. Every product answers it differently and a framework
	// that picked one would be wrong for most of them.
	//
	// examples/auth has a worked implementation over role tables of its own, if a
	// starting point is more useful than a blank page.
	Grants authhttp.Grants

	// OnSessionRefresh replaces a session's payload every time it is refreshed.
	//
	// The payload is the application's own context for one session — which device
	// it is, when step-up authentication was last satisfied, which mode somebody
	// picked. It is set at sign-in through session.IssueInput.Payload, carried
	// forward by every rotation, and reaches a handler as tenancy.Claims.Extra,
	// where [github.com/simonjanss/rig/runtime/tenancy.Extra] decodes it into a
	// type of your own.
	//
	// Nil, the default, carries the previous payload forward unchanged. Set this
	// only to recompute it — the hook is handed the token being exchanged, so the
	// current payload is prev.Payload.
	//
	// Not for permissions. It is written at sign-in and again only at refresh, so
	// anything here outlives its own revocation by up to the refresh lifetime.
	// Grants answers that question, per request.
	OnSessionRefresh func(ctx context.Context, prev *session.Token) (json.RawMessage, error)

	// IdentitySessionTTL is how long the tenant-less credential lasts. Zero is
	// [session.DefaultIdentityTTL], half an hour, which is what picking a
	// tenant takes.
	IdentitySessionTTL time.Duration

	// AllowTenantCreation mounts POST /auth/tenants, where somebody signed in
	// makes one and becomes its Owner.
	//
	// Off by default. rig owns the mechanism — the tenant row, the first account,
	// the slug, the transaction — because that is the same everywhere and getting
	// it half-right leaves a tenant nobody can reach. The policy is Tenants below:
	// who may, what a name is allowed to be, and what else a new one needs.
	AllowTenantCreation bool

	// Tenants is what this application decides about making them. Every field is
	// optional; the zero value lets anybody signed in make one called anything.
	Tenants account.TenantOptions

	// AllowRegistration mounts POST /auth/register, where a stranger creates an
	// account with no tenant and lands in the picker.
	//
	// Off by default. Whether anybody may sign themselves up is a product
	// decision — invite-only and open registration are both ordinary — and rig
	// declines to pick one. Off means the route does not exist, rather than
	// answering 403 to something that is there.
	AllowRegistration bool

	// OnRegistered runs inside the transaction that creates a self-registered
	// identity, and an error rolls the sign-up back. The ordinary body is
	// accounts.Provision with Invite set — an application that starts every
	// newcomer in a demo or sandbox tenant leaves an invitation waiting in the
	// picker they land in. Nil registers the person and nothing else.
	//
	// See [github.com/simonjanss/rig/auth/account.Config.OnRegistered].
	OnRegistered func(ctx context.Context, accounts *account.Service, in account.Registered) error

	// Limits override the rate limits. The zero value is the documented set: 5
	// failed logins per address per 15 minutes, 50 per address range, 5 password
	// resets an hour, and so on.
	Limits throttle.Defaults

	// LogRetention is how long an entry in rig_auth_log is kept, and it is only
	// read by [Auth.PruneLog]. Zero, the default, keeps everything and makes that
	// method a no-op.
	//
	// It cannot be shorter than the longest window in Limits, and [New] refuses a
	// configuration where it is rather than clamping it. Those limits are counted
	// from this table: pruning inside a window deletes the failures a lockout is
	// adding up to, so the limit stops limiting and nothing says so. Refusing at
	// startup is the last place that mistake can be caught cheaply.
	LogRetention time.Duration

	// TrustedProxies are the networks whose X-Forwarded-For may be believed.
	//
	// Empty means none, and that is deliberate: an address read from a header a
	// client controls is an address a client chooses, and a rate limit keyed on
	// one is a rate limit an attacker walks around.
	TrustedProxies []netip.Prefix

	// OAuth wires the provider endpoints. Leave Providers empty and none are
	// mounted.
	OAuth OAuth

	// Now is the clock, for tests.
	Now func() time.Time
}

Config is what an application chooses. Only Pool is required.

type GrantsCache

type GrantsCache struct {
	// contains filtered or unexported fields
}

GrantsCache is the map NewGrantsCache builds and the publisher that withdraws from it.

A nil *GrantsCache is usable and does nothing: GrantsCache.Wrap hands back the function it was given and the rest are no-ops. So is one that was never served. Both mean a project that decided against this, or has no `cache:` block, and neither needs a condition at the call sites that withdraw grants — the writes look the same either way.

func NewGrantsCache

func NewGrantsCache(cfg GrantsCacheConfig) *GrantsCache

NewGrantsCache builds a cache for an application's authhttp.Grants function, not yet attached to anything.

**rig does not turn this on and cannot.** CacheOptions covers the two reads rig owns on both sides; this one is over an application's own tables, written to by an application's own code, and a cache whose invalidations somebody has to remember to publish is only as correct as the write path they forgot. That is why this is a call rather than a key in rig.yaml: the decision belongs next to the writes it is a promise about.

What it is for is the part that is the same for everybody. Grants runs on every authenticated request — that is what makes a role taken away stop working on the next call rather than at the next sign-in — and it is usually the most expensive read on that path. Caching it is six decisions, all of them already made next door in session.TokenCache, and all of them easy to get wrong once:

  • the key is the tenant *and* the account, because permissions are per tenant and one account can be in several;
  • the two slices are copied on the way out, because they reach a handler as tenancy.Claims fields and one caller appending to them would widen what every other request in the window may do;
  • an error is never held, so a role table that was briefly unreachable does not answer "no permissions" for a lifetime;
  • an empty answer *is* held, because somebody holding nothing is a real answer and not a miss;
  • a replica that has lost the channel stops answering from memory altogether, rather than serving permissions it can no longer withdraw;
  • and an invalidation is published on the caller's transaction, so it is delivered when the change commits and discarded if it rolls back.

Three calls, in this order, because the generated wiring needs the Grants function before it can hand back the bus it built:

grants := auth.NewGrantsCache(auth.GrantsCacheConfig{})
front, err := api.New(pool, api.Hooks{Grants: grants.Wrap(authz.Grants(pool))})
grants.Serve(front.Parts().Cache)

Every step of that is fail-safe. A cache that was never served holds nothing, a bus that is not running reports itself as not live, and a map that is not live reads through and stores nothing — so forgetting the third line, or leaving the `cache:` block out of rig.yaml entirely, costs latency rather than correctness.

func (*GrantsCache) Forget

func (c *GrantsCache) Forget(tenantID, accountID uuid.UUID)

Forget withdraws one answer in this process only.

For a caller with no database handle to publish on. It leaves every other replica holding what it had, so it is the wrong tool for a revocation and the right one for a test.

func (*GrantsCache) Invalidate

func (c *GrantsCache) Invalidate(
	ctx context.Context, db dbx.Conn, tenantID, accountID uuid.UUID,
) error

Invalidate withdraws one account's grants in one tenant, everywhere.

Call it from the transaction that changed the roles, passing that transaction — `dbx.Tx(ctx)` inside a hook, or the pool for a write that has none. Postgres delivers a notification when the transaction issuing it commits and discards it when that transaction rolls back, so an invalidation published this way is atomic with the change causing it and reaches every replica.

Published rather than dropped locally, and that distinction is the whole feature: a role removed on one replica has to stop working on all of them.

func (*GrantsCache) InvalidateAll

func (c *GrantsCache) InvalidateAll(ctx context.Context, db dbx.Conn) error

InvalidateAll withdraws every held answer, everywhere.

For the writes that change what a role *means* rather than who holds it — seeding a tenant's roles, syncing the permission catalogue after a deploy, editing the grants on a role somebody else holds. Working out which accounts those reach is a query, and the answer is "clear it" for a map that refills itself on the next request.

func (*GrantsCache) Serve

func (c *GrantsCache) Serve(bus *cache.Bus)

Serve attaches the cache to the invalidation channel, which is Parts.Cache.

Nothing is held until this runs, and nothing is held at all when bus is nil — a project with no `cache:` block. Both are the same rule: an answer that cannot be withdrawn is not one this will keep.

It panics if the bus already serves GrantsTopic, which is the wiring mistake of attaching two caches to one bus.

func (*GrantsCache) ServeLocally

func (c *GrantsCache) ServeLocally()

ServeLocally attaches the cache to nothing, so it holds answers and forgets them in this process alone.

One process, no replicas, and staleness is the caller's problem — which is a real posture for a single-instance deployment, and the state a test wants without a database behind it. GrantsCache.Serve is what a deployment with replicas uses.

func (*GrantsCache) Wrap

Wrap returns the function to hand to Config.Grants.

Safe to call on a nil *GrantsCache, which returns g unchanged — so a project that decided against this needs no condition at the call site.

type GrantsCacheConfig

type GrantsCacheConfig struct {
	// TTL is how long an answer may be reused with no invalidation arriving.
	// Zero takes [DefaultCacheTTL].
	//
	// The backstop and not the guarantee, exactly as [CacheOptions.TTL] is —
	// with one difference worth being honest about. rig publishes every
	// invalidation for the reads it caches itself, so that TTL covers only a
	// replica that was not listening. Here the invalidations are yours, so it
	// also covers every write path you did not publish from. It is the cost of
	// a mistake as much as the cost of an outage.
	TTL time.Duration

	// MaxEntries bounds the map. Zero takes [cache.DefaultMaxEntries].
	MaxEntries int

	// Now is the clock, for tests.
	Now func() time.Time
}

GrantsCacheConfig tunes a GrantsCache.

type MailOptions

type MailOptions struct {
	// Queue writes links to rig_identity_verification_delivery and sends them
	// from [Auth.DispatchMail] instead of from the request.
	Queue bool

	// ClaimTTL is how long one dispatcher's claim is honoured, and SendTimeout
	// bounds one call into the Notifier. SendTimeout has to be the shorter of
	// the two, and rig refuses the pair rather than explaining it.
	ClaimTTL    time.Duration
	SendTimeout time.Duration

	// MaxAttempts, BackoffBase and BackoffCap are the retry arithmetic. The
	// defaults span about eight hours.
	MaxAttempts int
	BackoffBase time.Duration
	BackoffCap  time.Duration

	// Retention is how long a sent, failed or skipped delivery is kept before
	// [Auth.PruneMail] removes it. Zero keeps them forever, which is what every
	// other table in rig currently does and is not a recommendation.
	//
	// The link rows themselves are never pruned by this: they are the record of
	// who was invited and when.
	Retention time.Duration
}

MailOptions is the mail queue's configuration.

Queue is the switch; everything else has a default, and the defaults are notify's, so an operator tuning one dispatcher does not have to learn a second set of arithmetic for the other. See account.MailOptions for what each number does and which pairs are refused.

type OAuth

type OAuth struct {
	// Providers are the identity providers to offer, from the oauth package:
	// oauth.Google(id, secret), oauth.Microsoft(id, secret), and so on.
	Providers []oauth.Provider

	// BaseURL is this application's own address, because the callback URL it
	// registers with a provider has to be absolute.
	BaseURL string

	// Origin overrides BaseURL for one request, for an application served at
	// several origins — a tenant per subdomain, most often. See
	// [oauth.Config.Origin], including what a provider will and will not
	// register.
	Origin func(r *http.Request) string

	// SigningKey signs the state parameter. Required, and at least 32 bytes —
	// [oauth.New] refuses anything shorter.
	//
	// It has to be the same key in every replica: a callback may arrive at a
	// different one than the one that started the flow, and a key invented per
	// process is a sign-in that fails whenever a load balancer is doing its job.
	SigningKey []byte

	// StateTTL bounds how long a sign-in round trip may take. Zero is
	// [oauth.DefaultStateTTL], ten minutes: generous for a redirect, short
	// enough that a stolen state parameter is useless.
	StateTTL time.Duration

	// AllowProvisioning creates an account the first time somebody signs in
	// with a provider. Without it a provider sign-in only works for an address
	// that already has an account.
	AllowProvisioning bool

	// AllowedReturnTo are the paths a sign-in may return to. An open redirect
	// is the classic mistake here.
	AllowedReturnTo []string

	// Insecure allows the state cookie over plain HTTP, for local development
	// where a browser would refuse the __Host- prefixed one. Never anywhere real.
	Insecure bool

	// OnSignIn finishes a provider sign-in. Nil issues a session and answers
	// with the same body a login does, which is [authhttp.Handler.SignIn].
	//
	// A browser flow usually wants a cookie and a redirect instead, and that is
	// the sort of decision this package cannot make: it depends on whether the
	// client is a single-page application, a server-rendered one, or a mobile
	// app catching a deep link.
	OnSignIn func(w http.ResponseWriter, r *http.Request, in oauth.SignIn) error
}

OAuth is what a provider sign-in needs beyond a provider list.

type Parts

type Parts struct {
	Accounts *account.Service
	Sessions *session.Manager
	// Identities issues and verifies the credential somebody holds between
	// signing in and choosing a tenant.
	Identities *session.IdentityManager
	APIKeys    *apikey.Manager
	Limiter    *throttle.Limiter
	Stores     *authpg.Stores

	// Cache is the invalidation channel rig's own caches run over, or nil when
	// the project configured none.
	//
	// Here for the one thing rig cannot do on an application's behalf. rig
	// caches what it owns end to end — it makes the read and it makes every
	// write that withdraws it — and the read it does not own is Grants, over
	// role tables belonging to the application. A project that wants that answer
	// cached serves a topic of its own on this bus and publishes on it wherever
	// its roles change; see runtime/cache. Nothing in rig will do it for you,
	// because nothing in rig can see those writes.
	Cache *cache.Bus
}

Parts are the pieces New built, for a project that needs to reach past the endpoints: issuing a session after somebody signs in another way, minting a key from an admin screen, checking a permission in a service rule.

Directories

Path Synopsis
Package account implements the sign-in flows.
Package account implements the sign-in flows.
Package apikey mints and verifies machine-to-machine credentials.
Package apikey mints and verifies machine-to-machine credentials.
Package authhttp mounts the authentication endpoints.
Package authhttp mounts the authentication endpoints.
Package authlog records what happened to an authentication attempt.
Package authlog records what happened to an authentication attempt.
Package authpg implements the auth stores over the tables `rig setup-project` creates.
Package authpg implements the auth stores over the tables `rig setup-project` creates.
Package foundation carries the schema the auth module's tables need.
Package foundation carries the schema the auth module's tables need.
Package oauth signs people in through an external provider.
Package oauth signs people in through an external provider.
Package password hashes and verifies passwords with argon2id.
Package password hashes and verifies passwords with argon2id.
Package session issues, verifies, and rotates opaque session tokens.
Package session issues, verifies, and rotates opaque session tokens.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL