69 lines
2.4 KiB
Go
69 lines
2.4 KiB
Go
// Package authctx carries the authenticated identity of a request.
|
|
//
|
|
// It is the successor to the development identity that used to be injected by
|
|
// httpserver.devOrgMiddleware. The difference is not the shape — both put a
|
|
// value on the request context — but the provenance: everything here was read
|
|
// out of a server-side session row, and nothing in it can be influenced by the
|
|
// request that carries it.
|
|
//
|
|
// That is the whole point of the package existing separately from the handlers.
|
|
// A handler that wants to know who is calling has exactly one place to ask, and
|
|
// that place cannot be reached from a request body, a query string or a header.
|
|
// There is deliberately no setter that takes a user id from a client.
|
|
package authctx
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
type key struct{}
|
|
|
|
// ErrNoIdentity means a protected operation was reached without an
|
|
// authenticated identity. That is a routing or middleware bug rather than a
|
|
// client error: an unauthenticated request should have been refused before it
|
|
// got this far.
|
|
var ErrNoIdentity = errors.New("no authenticated identity in context")
|
|
|
|
// Identity is who the request is, as resolved from the session row.
|
|
//
|
|
// Role is carried because Phase 3D will need it, and because carrying it now
|
|
// means the middleware reads it once per request instead of every future
|
|
// authorization check re-querying the user. It is NOT consulted anywhere in
|
|
// Phase 3C: authentication only.
|
|
type Identity struct {
|
|
UserID string
|
|
OrgID string
|
|
Email string
|
|
FullName string
|
|
Role string
|
|
AccountType string
|
|
Status string
|
|
|
|
// SessionID is the row this identity came from, so logout and per-session
|
|
// diagnostics do not have to re-hash the cookie.
|
|
SessionID string
|
|
// ExpiresAt is the session's sliding deadline as of this request.
|
|
ExpiresAt time.Time
|
|
}
|
|
|
|
// With returns a context carrying the authenticated identity.
|
|
func With(ctx context.Context, id Identity) context.Context {
|
|
return context.WithValue(ctx, key{}, id)
|
|
}
|
|
|
|
// From reads the identity, reporting whether one was present.
|
|
func From(ctx context.Context) (Identity, bool) {
|
|
v, ok := ctx.Value(key{}).(Identity)
|
|
return v, ok && v.UserID != ""
|
|
}
|
|
|
|
// MustFrom reads the identity or returns ErrNoIdentity.
|
|
func MustFrom(ctx context.Context) (Identity, error) {
|
|
if v, ok := From(ctx); ok {
|
|
return v, nil
|
|
}
|
|
return Identity{}, ErrNoIdentity
|
|
}
|