authn

package
v0.4.8 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: MIT Imports: 10 Imported by: 0

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:

  1. Resolve operation: `transport.FromServerContext(ctx).Operation()`.
  2. 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`.
  3. PublicMethods passthrough: if op ∈ `Rules.PublicMethods`, passthrough — do not call the engine, do not write `AuthnDetail`.
  4. Build the allowed-schemes set: if op ∈ `Rules.MethodSchemes`, install `allowed = set(rules.MethodSchemes[op])` into ctx. Otherwise install nil (fail-open / unannotated).
  5. Install a mutable `schemeHolder` into ctx — `Multi` writes the successful scheme back via `holder.set(scheme)`.
  6. Call `a.Authenticate(ctx)`.
  7. Success → write `AuthnDetail{Method: holder.get(), Success: true}`, inject the actor, call the handler.
  8. 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

type Authenticator interface {
	Authenticate(ctx context.Context) (actor.Actor, error)
}

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

func WithErrorHandler(h func(ctx context.Context, err error) error) Option

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

func WithRulesFuncs(fns ...func() Rules) Option

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

type Rules struct {
	PublicMethods []string
	MethodSchemes map[string][]string
}

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

type SchemeAttempt struct {
	Scheme string
	Reason string
}

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.

Jump to

Keyboard shortcuts

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