Documentation
¶
Overview ¶
Package authn provides an engine-agnostic Kratos middleware dispatcher for authentication. The main package is a pure dispatcher: it knows nothing about credential carriers (Bearer token, mTLS PeerCertificate, API-Key header, signature, etc.). Engine sub-packages (e.g. `security/authn/jwt`) are responsible for credential I/O.
Dispatcher form (P0-4b):
authn.Server(
authn.Multi(
authn.Named(jwt.Scheme, jwt.NewAuthenticator(...)),
authn.Named(apikey.Scheme, apikey.NewAuthenticator(...)),
),
authn.WithRulesFuncs(examplev1.AuthnRules, iamv1.AuthnRules),
)
`authn.Server` itself is engine-agnostic: it consumes the annotation table (`Rules`) generated by `protoc-gen-servora-authn` to decide for each RPC path whether to skip (PublicMethods), restrict to a subset of schemes (MethodSchemes), or fail-open across all engines (unannotated).
`authn.Multi` is the standard decorator over multiple `NamedAuthenticator` instances; first-success-wins. The successful scheme is reported back to `authn.Server` via a package-private mutable holder ctx channel — the `Authenticator` interface stays unchanged (still single-method).
The middleware writes a `*auditpb.AuthnDetail` to ctx via `audit.WithAuthnResult`; emission is the responsibility of the transport-tail `audit.Collector` middleware. The authn package therefore has zero coupling to the audit emission pipeline (only to the neutral auditpb schema package).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Server ¶
func Server(a Authenticator, opts ...Option) middleware.Middleware
Server returns a Kratos middleware that dispatches authentication to the supplied `Authenticator` (typically a `Multi` decorator).
Per-request behavior:
- Resolve operation: `transport.FromServerContext(ctx).Operation()`.
- Chain short-circuit: if a non-anonymous actor already sits in ctx (a previous middleware authenticated), passthrough — do not call the engine, do not write `AuthnDetail`.
- PublicMethods passthrough: if op ∈ `Rules.PublicMethods`, passthrough — do not call the engine, do not write `AuthnDetail`.
- Build the allowed-schemes set: if op ∈ `Rules.MethodSchemes`, install `allowed = set(rules.MethodSchemes[op])` into ctx. Otherwise install nil (fail-open / unannotated).
- Install a mutable `schemeHolder` into ctx — `Multi` writes the successful scheme back via `holder.set(scheme)`.
- Call `a.Authenticate(ctx)`.
- Success → write `AuthnDetail{Method: holder.get(), Success: true}`, inject the actor, call the handler.
- Failure: - If err implements `SchemeAttemptsErr` → method = "multi", reason = serialized attempts ("jwt: …; apikey: …"). - Otherwise → method = holder.get() (may be empty), reason = err.Error(). Write `AuthnDetail{Success: false, …}` to ctx, then either invoke `WithErrorHandler` (if set) or return `errors.Unauthorized("AUTHN_FAILED", reason)`.
The detail is always written BEFORE returning so an OUTER-mounted `audit.Collector` observes it post-handler even when authn short-circuits.
Types ¶
type Authenticator ¶
Authenticator is the interface for authenticating incoming requests.
CONTRACT: this interface intentionally contains ONLY behavior body (`Authenticate`). Engine metadata (the scheme string used in audit detail) belongs to the wrapper layer via `authn.Named(scheme, a)` — NOT on the interface itself.
What MUST NOT live on this interface:
- Engine metadata (e.g. `Method() string`) — supplied by `authn.Named` when wiring `authn.Multi`; framework main package is agnostic to the scheme string.
- Hooks / callbacks (e.g. `OnSuccess`) — caller responsibility.
- Injection (logger / tracer) — container responsibility.
- Infra probes (e.g. `Health`) — separate sibling interface.
This single-method shape prevents interface bloat as new engines (mTLS, API-Key, AK+SK, Passkey, etc.) are added: each engine is described to the dispatcher by `Named` at wiring time, and orchestration is the `Multi` decorator's responsibility.
func Multi ¶ added in v0.4.8
func Multi(named ...NamedAuthenticator) Authenticator
Multi composes multiple `NamedAuthenticator` engines into a single `Authenticator`. First-success-wins: engines are tried in injection order (NOT in `allowedSchemes` order — business decides the precedence at wiring time). On any success, the winning scheme is written back to the package-private holder ctx channel installed by `Server`, which then becomes `AuthnDetail.Method`.
If the `allowedSchemes` set installed by `Server` (from `Rules.MethodSchemes`) is non-nil, engines whose scheme is absent from the set are skipped silently. If nil (unannotated method), every engine participates.
Wrapping a single engine via `Multi(Named(...))` is a supported and recommended pattern: it gives the dispatcher the scheme name without requiring a separate `WithMethod` option, and makes future expansion to more engines a one-line change.
type NamedAuthenticator ¶ added in v0.4.8
type NamedAuthenticator struct {
// contains filtered or unexported fields
}
NamedAuthenticator is an opaque scheme + Authenticator pair produced by `Named`. Fields are package-private; consumers can only construct via `Named` and consume via `Multi`.
func Named ¶ added in v0.4.8
func Named(scheme string, a Authenticator) NamedAuthenticator
Named pairs a scheme string with an `Authenticator` for `Multi`. The scheme is later written into `AuthnDetail.Method` when this engine is the one that succeeded.
The scheme string is opaque to the framework — any value is accepted. Engine sub-packages typically expose a `Scheme` constant (e.g. `jwt.Scheme = "jwt"`) so business code can write `authn.Named(jwt.Scheme, jwt.NewAuthenticator(...))`.
type Option ¶
type Option func(*serverConfig)
Option configures the Server middleware.
func WithErrorHandler ¶
WithErrorHandler installs a custom error transformer invoked when authentication fails. The detail has already been written to ctx by the dispatcher BEFORE this hook is called, so handlers can rely on `audit.AuthnResultFrom(ctx)` for structured access. If the underlying error implements `SchemeAttemptsErr` (i.e. came from `Multi`), the hook can type-assert to retrieve per-scheme attempts.
func WithRulesFuncs ¶ added in v0.4.8
WithRulesFuncs registers one or more rule-table generator functions. `protoc-gen-servora-authn` emits one `AuthnRules() Rules` per .proto package; business code passes them as variadic args to merge across modules. Nil entries are skipped (no panic) so business code can pass optional generators conditionally.
Merge semantics:
- PublicMethods slices are appended in argument order.
- MethodSchemes entries from later generators overwrite earlier ones. RPC paths are fully qualified service names; collisions are rare in practice and the last-wins rule lets business code intentionally override a downstream module's annotation if needed.
type Rules ¶ added in v0.4.8
Rules is the aggregate authentication-rules table consumed by `Server`. It is produced by `protoc-gen-servora-authn` per generated package (e.g. `examplev1.AuthnRules()`) and merged across modules by `WithRulesFuncs`.
Two disjoint subsets:
- PublicMethods — RPC paths annotated `MODE_PUBLIC`; the dispatcher short-circuits before calling the engine.
- MethodSchemes — RPC paths annotated `MODE_REQUIRED`, mapped to the accepted scheme list (e.g. `["jwt", "apikey"]`); the dispatcher installs an `allowedSchemes` ctx channel so `Multi` can filter.
An RPC path absent from BOTH maps is unannotated; the dispatcher treats it as fail-open (no allowed-set installed; `Multi` tries every engine in injection order).
type SchemeAttempt ¶ added in v0.4.8
SchemeAttempt records one engine's outcome inside a `Multi` dispatch. Both fields are public so `WithErrorHandler` consumers can render per-scheme diagnostics (logging, metrics labels, custom RPC errors).
type SchemeAttemptsErr ¶ added in v0.4.8
type SchemeAttemptsErr interface {
error
SchemeAttempts() []SchemeAttempt
}
SchemeAttemptsErr is the public interface satisfied by the package-private error type returned from `Multi.Authenticate` when every allowed engine failed. Business code obtains structured access via type assertion:
authn.WithErrorHandler(func(ctx context.Context, err error) error {
if as, ok := err.(authn.SchemeAttemptsErr); ok {
for _, a := range as.SchemeAttempts() {
log.Errorw("authn attempt failed",
"scheme", a.Scheme, "reason", a.Reason)
}
}
return err
})
`Server` itself uses this interface to detect Multi-failures and tag `AuthnDetail.Method = "multi"` with a serialized aggregate FailureReason.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package apikey provides an API-key authentication skeleton for the engine-agnostic authn dispatcher.
|
Package apikey provides an API-key authentication skeleton for the engine-agnostic authn dispatcher. |
|
Package jwt provides a generic Bearer JWT authentication skeleton for the engine-agnostic authn dispatcher.
|
Package jwt provides a generic Bearer JWT authentication skeleton for the engine-agnostic authn dispatcher. |
|
Package noop provides a no-op Authenticator that always returns an anonymous actor.
|
Package noop provides a no-op Authenticator that always returns an anonymous actor. |