Skip to content

Authentication

echo.labstack.com/pro/auth verifies API keys and bearer tokens before calling your Echo handler. The task guide shows setup; the reference lists fields and limits. Handlers read the verified principal with auth.From(ctx) or auth.FromEcho(c).

This route requires an API key with invoice:read. ring holds retained peppers; store looks up issued key records.

cfg := auth.Config{
Schemes: map[string]auth.Scheme{"key": auth.APIKey{
Namespace: "billing", Prefix: "demo", Peppers: ring, Store: store,
Header: "X-API-Key",
}},
Fallback: auth.Require(map[string][]string{"key": {"invoice:read"}}),
}
requireKey, err := cfg.ToMiddleware()
if err != nil {
panic(err)
}
e := echo.New()
render := echoadapter.ErrorHandlerConfig{}
e.HTTPErrorHandler, err = render.ToErrorHandler(e.HTTPErrorHandler)
if err != nil {
panic(err)
}
e.Group("/v1", requireKey).GET("/invoices", func(c *echo.Context) error {
p, _ := auth.FromEcho(c)
return c.String(http.StatusOK, p.ID()+" tenant="+p.TenantRestriction())
})

Use API keys for credentials your application issues and revokes. Use bearer tokens for access tokens from your configured issuer. The package does not implement browser sign-in or cookie sessions. Tenant middleware runs after authentication. Quota uses the selected tenant and verified principal.

IssueKey returns the credential and its StoredKey verifier record. Deliver the credential once through a secure channel. Store the record. Never store the credential. Keep peppers in your secret manager, separate from those records.

The first PepperRing version issues new credentials. Retained versions verify existing records. If you remove a pepper version, keys issued with it stop working. Setting StoredKey.RevokedAt revokes that credential. MemoryKeyStore supports concurrent calls but loses records at process exit. Your persistent store must return current revocation data and honor lookup cancellation. The persistent lookup example shows the required KeyStore method. The library does not create an API-key database table.

Give each authentication scheme its own namespace. A simple subject produces an ID such as billing:invoice-worker. Other subjects use a bounded ID containing a SHA-256 hash. Changing the namespace changes the IDs used by quota and execution. Rotating a key preserves the principal ID when namespace and subject stay the same.

The principal copies scopes and retains only allowlisted string claims. NewPrincipal creates a principal for trusted application mappings and tests. It does not verify a credential or change request context. A client header cannot establish a verified principal. TenantRestriction fixes the one tenant the credential can select. Unrestricted users still need your application’s membership check.

NewBearer(ctx, cfg) validates configuration and loads signing keys before returning. Set the exact issuer and API audiences. Set exactly one of JWKSURL or DiscoveryURL; both require HTTPS. NewBearer fetches the discovery document once, at construction. With empty AllowedJWKSHosts, the discovered jwks_uri must use the discovery host. With a nonempty list, its host must match an exact entry, including any port. The package never fetches a URL taken from a token header.

The default profile requires typ: at+jwt or typ: application/at+jwt. Subject, audience and expiration are required. Symmetric HS algorithms and none are rejected. ProfileJWT accepts typ: JWT when explicitly configured. Keep API audiences separate from ID-token audiences. TrustTenantClaim defaults to false. Enable it only for an issuer allowed to restrict your tenants.

Concurrent refreshes share one fetch. Cancelling a waiter does not cancel that shared fetch. A bad signature for a known key does not trigger refresh. When the key cache expires, the package refetches the key set. Keys the issuer removed then stop working. Failed refreshes back off. Last good keys remain usable only for the configured maximum age. Bearer starts no persistent worker and needs no Close. See the JWKS limits for sizes, timeouts and refresh intervals.

Config.Fallback is required. Deny() rejects requests. Anonymous() ignores credentials. Require(...) accepts named schemes with required scopes. Each alternative names one scheme. You cannot require two schemes on the same request. Multiple credentials, duplicate credential headers and malformed credentials return 401. Failed verification never tries a different scheme. A verified credential missing required scopes returns 403.

If you pass an openapi report, operations marked public ignore supplied credentials. Optional authenticated operations allow absent credentials but return 401 for invalid credentials. If you pass a Report, construction checks its scheme names and scope alternatives. An invalid runtime policy returns 500 if startup validation did not catch it.

The problem adapter renders authentication failures before tenant selection, quota or your handler runs. WWW-Authenticate contains a scheme-only challenge for missing credentials. Invalid bearer tokens get invalid_token. Insufficient scopes get insufficient_scope.

You can pass your own *slog.Logger. A nil logger produces no logs. Refusal logs contain the scheme and category, without raw credentials. The package registers no global metrics collector. The combined tutorial runs authentication, tenant selection and quota together.