Documentation
¶
Overview ¶
Package identity extracts the caller's identity (role, subject, tenant, claims, auth method) and connection details (client IP, user agent, target) from HTTP requests or gRPC contexts and exposes them through the request context.
Servers install NewContextHandler (HTTP) or NewAuthUnaryInterceptor / NewStreamServerInterceptor (gRPC) with an identity mapper; handlers then read the result:
h := identity.NewContextHandler(next, myMapper) // ProviderFromRequest
func handler(w http.ResponseWriter, r *http.Request) {
rc := identity.FromRequest(r)
role := rc.Identity().Role()
ip := rc.ClientIP() // string, "" when unknown
addr := rc.ClientAddr() // netip.Addr, IsValid false when unknown
}
When no identity is present a guest identity (role "guest") is returned, never nil. GuestIdentityMapper / GuestIdentityForContext are the default mappers. By default client IPs come from the socket peer. A server may opt in to forwarding headers with ParseTrustedProxies and NewTrustedProxyHandler (HTTP) or WithTrustedProxies (gRPC context). The configured proxies must overwrite incoming forwarding headers. NewTrustedProxyHandler resolves the client IP once per request and ClientIPFromRequest returns that value in later handlers:
trust, err := identity.ParseTrustedProxies([]string{"10.2.0.0/16"})
if err != nil {
return err
}
h := identity.NewTrustedProxyHandler(identity.NewContextHandler(next, myMapper), trust)
ClientIPFromRequest and ClientIPFromGRPC return a netip.Addr. A caller without an IP socket peer, such as a request built with http.NewRequest and served in process or a Unix socket peer, has no client IP: they return the zero Addr. Format it with IPString, which returns "" for it, rather than Addr.String, which returns "invalid IP":
remote := identity.IPString(identity.ClientIPFromRequest(r))
Index ¶
- Constants
- func AddToContext(ctx context.Context, rq *RequestContext) context.Context
- func BasicAuthFromRequest(r *http.Request) (id string, secret string, err error)
- func ClientIPFromGRPC(ctx context.Context) netip.Addr
- func ClientIPFromRequest(r *http.Request) netip.Addr
- func ForwardedProto(r *http.Request) string
- func IPString(addr netip.Addr) string
- func NewAuthUnaryInterceptor(identityMapper ProviderFromContext, trusted ...*TrustedProxies) grpc.UnaryServerInterceptor
- func NewContextHandler(delegate http.Handler, identityMapper ProviderFromRequest) http.Handler
- func NewStreamServerInterceptor(identityMapper ProviderFromContext, trusted ...*TrustedProxies) grpc.StreamServerInterceptor
- func NewTrustedProxyHandler(next http.Handler, trust *TrustedProxies) http.Handler
- func WithTestIdentity(r *http.Request, identity Identity) *http.Request
- func WithTrustedProxies(ctx context.Context, trust *TrustedProxies) context.Context
- type AuthMethod
- type Context
- type Identity
- type ProviderFromContext
- type ProviderFromRequest
- type RequestContext
- type TrustedProxies
Constants ¶
const GuestRoleName = "guest"
GuestRoleName is default role name for guest
Variables ¶
This section is empty.
Functions ¶
func AddToContext ¶
func AddToContext(ctx context.Context, rq *RequestContext) context.Context
AddToContext returns a child context carrying rq as the request context, as later returned by FromContext.
func BasicAuthFromRequest ¶
BasicAuthFromRequest parses the "Authorization: Basic base64(id:secret)" header. It returns empty values and a nil error when the header is absent or uses another scheme (callers must check id), an httperror.InvalidRequest when the base64 is malformed, and an empty secret when no ":" is present. The scheme prefix is matched case-sensitively.
func ClientIPFromGRPC ¶ added in v0.36.373
ClientIPFromGRPC returns the peer IP unless it is a trusted proxy; trusted peers may supply x-forwarded-for or x-real-ip metadata, resolved as in ClientIPFromRequest. A call without a peer, or whose peer is not an IP (a Unix socket peer), has no client IP: the result is the zero Addr.
func ClientIPFromRequest ¶
ClientIPFromRequest returns the socket peer's IP unless that peer is in the request context's TrustedProxies. It then walks X-Forwarded-For from right to left and returns the first untrusted address, or the leftmost address when every hop is trusted. Invalid forwarding data falls back to the socket peer. IPv4-mapped IPv6 addresses are returned as IPv4. A request whose RemoteAddr is not an IP (empty, as for one built with http.NewRequest and served in process, or "@" for a Unix socket peer) has no client IP: the result is the zero Addr, whose IsValid is false. Use IPString, not Addr.String, to log or key on it, so that the missing IP is "" rather than "invalid IP". Behind NewTrustedProxyHandler it returns the IP resolved there.
func ForwardedProto ¶ added in v1.0.438
ForwardedProto returns a trusted proxy's http or https scheme, or an empty string when the peer or header is untrusted or invalid.
func IPString ¶ added in v1.0.448
IPString returns addr in its netip.Addr.String form, or "" for the zero Addr that ClientIPFromRequest and ClientIPFromGRPC return when a caller has no client IP; netip.Addr.String would return "invalid IP", which as a rate limit key or log value would group every such caller together.
func NewAuthUnaryInterceptor ¶
func NewAuthUnaryInterceptor(identityMapper ProviderFromContext, trusted ...*TrustedProxies) grpc.UnaryServerInterceptor
NewAuthUnaryInterceptor returns a grpc.UnaryServerInterceptor that calls identityMapper with the full method name and stores the RequestContext (identity, client IP from a trusted proxy or the peer address, user agent) in the context. A mapper error is logged and fails the call with codes.Unauthenticated and the generic "invalid identity" message, unless it is (or wraps) an *httperror.Error with an HTTP status and a gRPC mapping, which is returned as is. Panics in the handler are recovered, logged and returned as an Internal gRPC error with message "unhandled exception". The optional trusted policy (only the first value is used) is stored with WithTrustedProxies; a nil or omitted policy keeps any policy already in the context.
func NewContextHandler ¶
func NewContextHandler(delegate http.Handler, identityMapper ProviderFromRequest) http.Handler
NewContextHandler returns middleware that calls identityMapper for each request and stores the resulting RequestContext (identity, client IP, path, user agent) in the request context for later handlers. A mapper error is logged and answered with a JSON 401 unauthorized response whose message is the generic "invalid identity", unless the error is (or wraps) an *httperror.Error with an HTTP status, which is written as is; the request is not forwarded. For non-guest identities tenant/user/email/role are also added to the xlog context fields. A RequestContext already present in the context (for example from WithTestIdentity) is left untouched.
func NewStreamServerInterceptor ¶ added in v0.33.319
func NewStreamServerInterceptor(identityMapper ProviderFromContext, trusted ...*TrustedProxies) grpc.StreamServerInterceptor
NewStreamServerInterceptor returns the streaming counterpart of NewAuthUnaryInterceptor; the stream is wrapped with streamctx.WithContext so the handler sees the enriched context. It does not recover panics.
func NewTrustedProxyHandler ¶ added in v1.0.438
func NewTrustedProxyHandler(next http.Handler, trust *TrustedProxies) http.Handler
NewTrustedProxyHandler stores trust in each request's context and resolves the client IP once, so that ClientIPFromRequest in later handlers (rate limiter, identity, request logger) returns the cached value instead of walking the forwarding headers again. Later changes to those headers do not change the cached IP; a request whose RemoteAddr differs from the one seen here is resolved again.
func WithTestIdentity ¶
WithTestIdentity returns a copy of r whose context carries the given identity (with the local IP as client IP), for use in unit tests.
func WithTrustedProxies ¶ added in v1.0.438
func WithTrustedProxies(ctx context.Context, trust *TrustedProxies) context.Context
WithTrustedProxies returns a context that uses trust when resolving proxy headers. It replaces any policy, and any client IP cached by NewTrustedProxyHandler, already in ctx.
Types ¶
type AuthMethod ¶ added in v0.37.390
type AuthMethod int
AuthMethod identifies how the caller was authenticated.
const ( // MethodNone means the caller was not authenticated (guest). MethodNone AuthMethod = iota // MethodCertificate means a TLS client certificate was used. MethodCertificate // MethodAWS means AWS request signing was used. MethodAWS // MethodDPoP means a DPoP-bound access token was used. MethodDPoP // MethodJWT means a bearer JWT was used. MethodJWT // MethodJWTCookie means a JWT carried in a cookie was used. MethodJWTCookie )
Authentication methods reported by Identity.AuthMethod.
func (AuthMethod) String ¶ added in v0.37.390
func (m AuthMethod) String() string
String returns the method name ("Certificate", "AWS", "DPoP", "JWT", "JWTCookie"), or "None" for MethodNone and unknown values.
type Context ¶
type Context interface {
// Identity returns the caller's identity; never nil (guest when unknown).
Identity() Identity
// ClientIP returns the client IP as determined by ClientIPFromRequest or
// ClientIPFromGRPC, formatted by IPString: "" when there is none.
ClientIP() string
// ClientAddr returns the client IP that ClientIP formats, for callers
// that need the parsed address (prefix checks, location lookups); the
// zero Addr when there is none.
ClientAddr() netip.Addr
// Target of the request, e.g. HTTP path or gRPC method
Target() string
// UserAgent returns the User-Agent header (or user-agent / x-user-agent
// gRPC metadata), or "".
UserAgent() string
}
Context is the read-only view of RequestContext.
type Identity ¶
type Identity interface {
// String returns the identity as a single string value
// in the format of {tenant/}subject{:role}
String() string
// Role returns the role used for authorization (see restserver/authz).
Role() string
// Subject returns the caller's subject: a certificate CommonName, a JWT
// "email" claim, or similar.
Subject() string
// Tenant returns the tenant the identity belongs to, or "".
Tenant() string
// Claims returns a copy of the application-specific claims.
Claims() jwt.MapClaims
// AccessToken returns the raw access token presented, or "".
AccessToken() string
// TokenType returns the token type (e.g. "Bearer", "DPoP"), or "".
TokenType() string
// AuthMethod returns how the caller was authenticated.
AuthMethod() AuthMethod
}
Identity contains information about the identity of an API caller. Implementations are immutable values; create them with NewIdentity.
func GuestIdentityForContext ¶
GuestIdentityForContext is a ProviderFromContext that always returns the guest role with an empty subject.
func GuestIdentityMapper ¶
GuestIdentityMapper is a ProviderFromRequest that always returns the guest role, with the subject set to the TLS client certificate CommonName when one was presented, or "unknown". It is restserver's default mapper.
func NewIdentity ¶
func NewIdentity(role, subject, tenant string, claims map[string]any, accessToken, tokenType string, authMethod AuthMethod) Identity
NewIdentity returns an immutable Identity with the given attributes. claims may be nil; when provided they are copied.
type ProviderFromContext ¶
ProviderFromContext maps a gRPC request context and full method name to the caller's Identity. It is used by the gRPC interceptors; returning an error rejects the call with a generic codes.Unauthenticated status (or the *httperror.Error the error wraps), returning a nil Identity yields the guest identity.
type ProviderFromRequest ¶
ProviderFromRequest maps an HTTP request to the caller's Identity. It is used by NewContextHandler; returning an error rejects the request with a generic 401 (or the *httperror.Error the error wraps), returning a nil Identity yields the guest identity.
type RequestContext ¶
type RequestContext struct {
// contains filtered or unexported fields
}
RequestContext is the per-request value stored in the context by NewContextHandler and the gRPC interceptors: the caller's Identity, client IP, target (HTTP path or gRPC method) and user agent. It implements Context. Correlation IDs live in xhttp/correlation, not here.
func FromContext ¶
func FromContext(ctx context.Context) *RequestContext
FromContext returns the RequestContext stored in ctx. When none exists it returns a new RequestContext with the guest identity, never nil.
func FromRequest ¶
func FromRequest(r *http.Request) *RequestContext
FromRequest returns the RequestContext for r (guest when none was stored), filling in target, client IP and user agent from the request when they are empty. Note that it updates the stored RequestContext in place, unless NewContextHandler stored it: that one already holds r's values.
func NewRequestContext ¶
func NewRequestContext(id Identity, target string) *RequestContext
NewRequestContext creates a request context with a specific identity and target; client IP and user agent are left empty. Store it with AddToContext.
func (*RequestContext) ClientAddr ¶ added in v1.0.448
func (c *RequestContext) ClientAddr() netip.Addr
ClientAddr returns the request's client IP as a netip.Addr, or the zero Addr (IsValid false) when unknown.
func (*RequestContext) ClientIP ¶
func (c *RequestContext) ClientIP() string
ClientIP returns the request's client IP, or "" when unknown.
func (*RequestContext) Identity ¶
func (c *RequestContext) Identity() Identity
Identity returns the request's identity; never nil.
func (*RequestContext) Target ¶ added in v0.36.373
func (c *RequestContext) Target() string
Target returns the request's target: the HTTP path or gRPC full method.
func (*RequestContext) UserAgent ¶ added in v0.36.373
func (c *RequestContext) UserAgent() string
UserAgent returns the request's user agent, or "".
type TrustedProxies ¶ added in v1.0.438
type TrustedProxies struct {
// contains filtered or unexported fields
}
TrustedProxies is an immutable set of proxy networks allowed to supply forwarding headers. An empty or nil set trusts no proxy.
func ParseTrustedProxies ¶ added in v1.0.438
func ParseTrustedProxies(cidrs []string) (*TrustedProxies, error)
ParseTrustedProxies validates CIDR ranges for immediate and intermediate proxies.