auth

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: BSD-3-Clause-Clear Imports: 32 Imported by: 0

README

Auth Package

This package handles authentication (authn) and authorization (authz) for the OpenTDF platform.

Package Structure

auth/
├── authn.go           # Authentication middleware and token validation
├── config.go          # Configuration types
├── discovery.go       # OIDC discovery
└── authz/             # Authorization interfaces and implementations
    ├── authorizer.go  # Authorizer interface and factory
    ├── resolver.go    # Resolver registry and resource dimension context
    └── casbin/        # Casbin authorizers
        ├── v1/        # Legacy path + action authorization
        └── v2/        # RPC + dimensions authorization

Authz v1

Authz v1 is the legacy Casbin model. Its policy rows have four fields:

p, subject, resource, action, effect

The enforcement request is (subject, resource, action). For Connect/gRPC requests, resource is the RPC procedure path normalized for v1 policy compatibility: gRPC-style resources omit the leading slash, while HTTP paths keep it. The action field matches the policy action column, typically read, write, delete, unsafe, or *.

Authz v2

Authz v2 is the Casbin RPC + dimensions model. It authorizes (subject, rpc, dimensions):

  • subject: extracted from JWT roles, client ID, and username. Roles and clients are typed as role:<name> and client:<id>.
  • rpc: full Connect/gRPC procedure path such as /policy.kasregistry.KeyAccessServerRegistryService/GetKey.
  • dimensions: a serialized ResolverContext resource such as kas_uri=https://kas-a.example.com, or * when no dimensions are available.

The interceptor calls a service-registered resolver only when the selected authorizer supports resource authorization. If no resolver is registered for the RPC, v2 evaluates the request with wildcard dimensions (*). Policies that require a concrete dimension then deny; policies using wildcard dimensions may still allow.

Services register resolvers through their scoped ScopedResolverRegistry during service startup. A resolver may return one or more resources, and every non-empty resource must be allowed for the request to pass.

Moving From Authz v1 to v2 Policy

When moving policy from v1 to v2:

  • Change policy rows from p, subject, resource, action, effect to p, subject, rpc, dimensions, effect.
  • Use the full Connect/gRPC RPC path in the rpc column, including the leading slash, for example /policy.kasregistry.KeyAccessServerRegistryService/GetKey.
  • Remove the separate action column. In v2, the RPC method represents the operation.
  • Use * for dimensions when the policy is only subject/RPC scoped.
  • Use concrete dimensions only for RPCs with registered resolvers. Today, GetKey and ListKeys can use kas_uri=<value>.

For example, a v1 read rule:

p, role:standard, kasregistry.*, read, allow

becomes a v2 RPC-scoped rule:

p, role:standard, /policy.kasregistry.KeyAccessServerRegistryService/Get*, *, allow

and a v2 dimension-scoped GetKey rule:

p, role:kas-reader, /policy.kasregistry.KeyAccessServerRegistryService/GetKey, kas_uri=https://kas.example.com, allow
Current v2 Dimension Coverage

The table lists production resolver coverage in this workspace. The v2 authorizer supports arbitrary dimension keys supplied by resolvers, but only the RPCs below currently have registered production resolvers.

RPC Available dimensions
/policy.kasregistry.KeyAccessServerRegistryService/GetKey kas_uri
/policy.kasregistry.KeyAccessServerRegistryService/ListKeys kas_uri
All other RPCs none (*)
KAS URI Policy Encoding

When writing kas_uri dimension values in v2 Casbin policy, encode only the characters that conflict with dimension parsing:

  • % -> %25
  • & -> %26
  • literal * -> %2A (Only if you want to match exactly on *)

Other URI characters such as :, /, ?, =, and + can stay readable. For example, a KAS URI with query parameters should encode the query separator & in policy:

p, role:kas-reader, /policy.kasregistry.KeyAccessServerRegistryService/GetKey, kas_uri=https://kas.example.com?foo=bar%26baz=qux, allow

[!NOTE] This escaping only covers authz v2 dimension parsing. Policy rows are still Casbin CSV, so if a URI contains a literal comma, quote the policy field using valid CSV/Casbin syntax. Quotes and newlines should not appear unescaped in valid KAS URIs.

The embedded v2 default policy grants:

  • role:admin full access.
  • role:standard read access to policy Get*, List*, and Match* RPCs, access to KAS, health, discovery, and authorization services.
  • role:unknown access to /kas.AccessService/Rewrap.

Security Guidelines

Never Log Sensitive Authentication Data

DO NOT log the following:

  1. JWT Tokens - Never log full tokens, even at DEBUG level

    • Tokens can be replayed if logs are compromised
    • Tokens may contain PII in claims
    • Large tokens can be used for DoS attacks (disk/memory exhaustion)
    • Unsanitized token content can enable log injection attacks
  2. Credentials - Never log passwords, API keys, or secrets

  3. Full UserInfo responses - May contain PII

Safe to log:

  • Claim names (e.g., which claim was missing)
  • Extracted role/group names (after validation)
  • Subject identifiers (if not sensitive in your context)
  • Error types and messages (without embedding tokens)
Example: What NOT to do
// BAD - logs full token (security risk)
e.logger.Debug("processing token", slog.Any("token", token))

// BAD - token in error message
e.logger.Error("auth failed", slog.String("token", tokenString))
Example: Safe logging
// GOOD - no sensitive data
e.logger.Debug("extracting roles from token")

// GOOD - only logs claim name, not value
e.logger.Warn("claim not found", slog.String("claim", claimName))

// GOOD - logs extracted, bounded data
e.logger.Debug("roles extracted", slog.Int("count", len(roles)))
Log Injection Prevention

Even when logging "safe" data extracted from tokens, be aware that:

  • Claims can contain newlines (fake log entries)
  • Claims can contain ANSI escape codes
  • Claims can be arbitrarily large

Consider truncating or sanitizing any user-controlled data before logging.

Documentation

Index

Constants

View Source
const (
	ActionRead   = "read"
	ActionWrite  = "write"
	ActionDelete = "delete"
	ActionUnsafe = "unsafe"
	ActionOther  = "other"
)
View Source
const (
	// DiscoveryPath is the path to the discovery endpoint
	DiscoveryPath = "/.well-known/openid-configuration"
)

Variables

This section is empty.

Functions

func IPCMetadataClientInterceptor added in v0.11.0

func IPCMetadataClientInterceptor(log *logger.Logger) connect.UnaryInterceptorFunc

IPCMetadataClientInterceptor transfers gRPC outgoing metadata to Connect request headers for IPC calls

Types

type AccessTokenVerifier added in v0.15.0

type AccessTokenVerifier interface {
	VerifyAccessToken(ctx context.Context, tokenRaw string) (jwt.Token, error)
}

AccessTokenVerifier validates raw access tokens.

type AuthNConfig

type AuthNConfig struct {
	// Deprecated: use DPoP.Enforce (server.auth.dpop.enforce) instead. Still honored
	// during the migration window: DPoP is enforced when either field is true.
	EnforceDPoP  bool                       `mapstructure:"enforceDPoP" json:"enforceDPoP" default:"false"`
	Issuer       string                     `mapstructure:"issuer" json:"issuer"`
	Audience     string                     `mapstructure:"audience" json:"audience"`
	Policy       internalauthz.PolicyConfig `mapstructure:"policy" json:"policy"`
	CacheRefresh string                     `mapstructure:"cache_refresh_interval" json:"cache_refresh_interval"`
	DPoPSkew     time.Duration              `mapstructure:"dpopskew" json:"dpopskew" default:"1h"`
	TokenSkew    time.Duration              `mapstructure:"skew" json:"skew" default:"1m"`
	DPoP         DPoPConfig                 `mapstructure:"dpop" json:"dpop"`
}

AuthNConfig is the configuration need for the platform to validate tokens

type Authentication

type Authentication struct {
	// contains filtered or unexported fields
}

Authentication holds a jwks cache and information about the openid configuration

func NewAuthenticator

func NewAuthenticator(ctx context.Context, cfg Config, logger *logger.Logger, wellknownRegistration func(namespace string, config any) error, opts ...AuthenticatorOption) (*Authentication, error)

Creates new authN which is used to verify tokens for a set of given issuers

func (*Authentication) AccessTokenVerifier added in v0.15.0

func (a *Authentication) AccessTokenVerifier() AccessTokenVerifier

AccessTokenVerifier returns the authenticator's shared access-token verifier.

func (Authentication) ConnectAuthNInterceptor added in v0.17.0

func (a Authentication) ConnectAuthNInterceptor() connect.UnaryInterceptorFunc

ConnectAuthNInterceptor authenticates Connect requests and enriches the request context with configured token claims needed by later middleware.

func (Authentication) ConnectAuthZInterceptor added in v0.17.0

func (a Authentication) ConnectAuthZInterceptor() connect.UnaryInterceptorFunc

ConnectAuthZInterceptor authorizes Connect requests using token and configured claims already stored in the request context.

func (Authentication) IPCUnaryServerInterceptor added in v0.5.0

func (a Authentication) IPCUnaryServerInterceptor() connect.UnaryInterceptorFunc

IPCUnaryServerInterceptor is a grpc interceptor that: 1. translates known IPC Connect request headers back to incoming metadata 2. reauthorizes routes that are configured for IPC reauth 3. rehydrates auth context from propagated incoming metadata without revalidating it

func (Authentication) MuxHandler

func (a Authentication) MuxHandler(handler http.Handler) http.Handler

verifyTokenHandler is a http handler that verifies the token

type AuthenticatorOption added in v0.18.0

type AuthenticatorOption func(*Authentication)

AuthenticatorOption is a functional option for configuring Authentication.

func WithAuthzResolverRegistry added in v0.18.0

func WithAuthzResolverRegistry(registry *internalauthz.ResolverRegistry) AuthenticatorOption

WithAuthzResolverRegistry sets the authorization resolver registry. When set, the interceptors will call resolvers to extract authorization dimensions.

type Config

type Config struct {
	Enabled      bool     `mapstructure:"enabled" json:"enabled" default:"true"`
	PublicRoutes []string `mapstructure:"-" json:"-"`
	// Used for re-authentication of IPC connections
	IPCReauthRoutes []string `mapstructure:"-" json:"-"`
	AuthNConfig     `mapstructure:",squash"`

	// Programmatic role provider overrides (not loaded from config)
	RoleProvider          platformauthz.RoleProvider                   `mapstructure:"-" json:"-"`
	RoleProviderFactories map[string]platformauthz.RoleProviderFactory `mapstructure:"-" json:"-"`
}

AuthConfig pulls AuthN and AuthZ together

type DPoPConfig added in v0.18.0

type DPoPConfig struct {
	// Enforce requires access tokens to be DPoP-bound. Replaces the deprecated
	// top-level server.auth.enforceDPoP field.
	Enforce         bool          `mapstructure:"enforce" json:"enforce" default:"false"`
	RequireNonce    bool          `mapstructure:"require_nonce" json:"require_nonce" default:"false"`
	NonceExpiration time.Duration `mapstructure:"nonce_expiration" json:"nonce_expiration" default:"5m"`
	// StrictHTU requires the htu claim in DPoP JWTs to include the origin
	// (scheme + host). When false (default), a path-only htu is accepted as
	// long as the path matches, easing SDK skew during rollout.
	StrictHTU bool `mapstructure:"strict_htu" json:"strict_htu" default:"false"`
}

func (DPoPConfig) Validate added in v0.18.0

func (c DPoPConfig) Validate() error

type DPoPNonceError added in v0.18.0

type DPoPNonceError struct {
	Message string
}

DPoPNonceError indicates a missing or expired nonce that the client should retry with a fresh one.

func (*DPoPNonceError) Error added in v0.18.0

func (e *DPoPNonceError) Error() string

type DPoPNonceMalformedError added in v0.18.0

type DPoPNonceMalformedError struct {
	Message string
}

DPoPNonceMalformedError indicates the nonce claim was present but had an invalid type or format. Unlike DPoPNonceError, this is not retryable — the client sent a malformed proof.

func (*DPoPNonceMalformedError) Error added in v0.18.0

func (e *DPoPNonceMalformedError) Error() string

type DPoPProofError added in v0.19.0

type DPoPProofError struct {
	// contains filtered or unexported fields
}

DPoPProofError marks a non-retryable DPoP proof rejection (tampered htu/htm, bad ath, replayed jti, malformed nonce). Handlers translate it into a WWW-Authenticate: DPoP error="invalid_dpop_proof" challenge per RFC 9449 §7.1.

func (*DPoPProofError) Error added in v0.19.0

func (e *DPoPProofError) Error() string

func (*DPoPProofError) Unwrap added in v0.19.0

func (e *DPoPProofError) Unwrap() error

type OIDCConfiguration

type OIDCConfiguration struct {
	Issuer                           string   `json:"issuer"`
	AuthorizationEndpoint            string   `json:"authorization_endpoint"`
	TokenEndpoint                    string   `json:"token_endpoint"`
	UserinfoEndpoint                 string   `json:"userinfo_endpoint"`
	JwksURI                          string   `json:"jwks_uri"`
	ResponseTypesSupported           []string `json:"response_types_supported"`
	SubjectTypesSupported            []string `json:"subject_types_supported"`
	IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
	RequireRequestURIRegistration    bool     `json:"require_request_uri_registration"`
}

OIDCConfiguration holds the openid configuration for the issuer. Currently only required fields are included (https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata)

func DiscoverOIDCConfiguration

func DiscoverOIDCConfiguration(ctx context.Context, issuer string, logger *logger.Logger) (*OIDCConfiguration, error)

DiscoverOPENIDConfiguration discovers the openid configuration for the issuer provided

type TokenVerifier added in v0.15.0

type TokenVerifier struct {
	// contains filtered or unexported fields
}

TokenVerifier validates access tokens against the platform's configured IdP.

func NewTokenVerifier added in v0.15.0

func NewTokenVerifier(ctx context.Context, cfg AuthNConfig, log *logger.Logger) (*TokenVerifier, error)

NewTokenVerifier creates a reusable verifier backed by the IdP JWKS endpoint.

func (*TokenVerifier) VerifyAccessToken added in v0.15.0

func (v *TokenVerifier) VerifyAccessToken(ctx context.Context, tokenRaw string) (jwt.Token, error)

VerifyAccessToken validates the provided raw JWT and returns the parsed token on success.

Directories

Path Synopsis
Package authz provides the authorization interface and types for the OpenTDF platform.
Package authz provides the authorization interface and types for the OpenTDF platform.
casbin
Package casbin registers the Casbin authorization engine and dispatches to the configured versioned implementation.
Package casbin registers the Casbin authorization engine and dispatches to the configured versioned implementation.
casbin/v1
Package v1 provides the legacy path-based Casbin authorization implementation.
Package v1 provides the legacy path-based Casbin authorization implementation.
casbin/v2
Package v2 provides the resource/dimension-based Casbin authorization implementation.
Package v2 provides the resource/dimension-based Casbin authorization implementation.

Jump to

Keyboard shortcuts

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