roles

package
v1.0.448 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 30 Imported by: 2

Documentation

Overview

Package roles maps authenticated callers to porto roles for both HTTP and gRPC requests.

New builds an IdentityProvider from an IdentityMap. The provider inspects the Authorization header (or gRPC "authorization" metadata) and, depending on the token type, verifies an AWS STS presigned GetCallerIdentity URL ("AWS4"), a DPoP-bound JWT ("DPoP") or a bearer JWT ("Bearer"); it can also fall back to a JWT stored in a cookie (with CSRF double-submit checks for unsafe HTTP methods and every gRPC call) and to a client TLS certificate carrying a SPIFFE URI SAN. The resulting identity.Identity carries the mapped role, subject, tenant and claims; unauthenticated requests receive the guest identity.

For gRPC, DPoP proofs sign POST with an absolute htu of https://<incoming :authority><full method path>. Relative-path proofs fail authentication. A proxy must preserve that authority and method path.

The provider is wired into gserver via Config.IdentityMap, but can be used directly with xhttp/identity:

prov, err := roles.New(&roles.IdentityMap{
	JWT: roles.JWTIdentityMap{
		Enabled:                  true,
		Issuer:                   "https://issuer.example.com",
		Audience:                 "api",
		DefaultAuthenticatedRole: roles.JWTUserRoleName,
		Roles: map[string][]string{"admin": {"alice@example.com"}},
	},
}, jwtParser)
if err != nil {
	return err
}
h := identity.NewContextHandler(next, prov.IdentityFromRequest)

YAML layout of IdentityMap:

identity_map:
  strict: false
  skip_auth: ["/healthz"]
  cookies: {auth: "auth_token", csrf: "csrf_token"}
  tls: {enabled: true, default_authenticated_role: tls_user, roles: {admin: ["spiffe://trust/ns/app"]}}
  jwt: {enabled: true, issuer: "...", audience: "...", roles: {admin: ["alice@example.com"]}}
  jwt_dpop: {enabled: false}
  aws: {enabled: true, allowed_accounts: ["123456789012"], roles: {deployer: ["123456789012:assumed-role/ci"]}}

Index

Constants

View Source
const (
	// GuestRoleName is the role of an unauthenticated caller
	GuestRoleName = "guest"

	// TLSUserRoleName is a suggested DefaultAuthenticatedRole for TLS identities
	TLSUserRoleName = "tls_user"

	// JWTUserRoleName is a suggested DefaultAuthenticatedRole for JWT identities
	JWTUserRoleName = "jwt_user"

	// DPoPUserRoleName is a suggested DefaultAuthenticatedRole for DPoP identities
	DPoPUserRoleName = "dpop_user"

	// AWSUserRoleName is a suggested DefaultAuthenticatedRole for AWS identities
	AWSUserRoleName = "aws_user"

	// DefaultSubjectClaim is the JWT claim used as identity subject when SubjectClaim is unset
	DefaultSubjectClaim = "sub"

	// DefaultRoleClaim is the JWT claim matched against Roles when RoleClaim is unset
	DefaultRoleClaim = "email"

	// DefaultTenantClaim is the JWT claim used as tenant when TenantClaim is unset
	DefaultTenantClaim = "tenant"
)

Variables

This section is empty.

Functions

func ParseSTSTokenExpiration added in v0.27.269

func ParseSTSTokenExpiration(presignedURL string) (*time.Time, string, string, error)

ParseSTSTokenExpiration computes the expiry of an AWS SigV4 presigned URL from its X-Amz-Date and X-Amz-Expires query parameters. It also returns the raw parameter values for logging. An unparsable X-Amz-Expires falls back to credentials.CacheTTL.

func ValidateSTSPresignedURL added in v0.40.416

func ValidateSTSPresignedURL(presignedURL string) error

ValidateSTSPresignedURL checks that a presigned URL supplied in an AWS4 token is an HTTPS GetCallerIdentity request addressed to an AWS STS endpoint (sts.amazonaws.com, sts[-fips].<region>.amazonaws.com[.cn], or an STS VPC endpoint under amazonaws.com). Without this check the server would fetch an attacker-chosen URL and trust the returned account and ARN.

Types

type AWSIdentityMap added in v0.17.0

type AWSIdentityMap struct {
	// DefaultAuthenticatedRole specifies role name for identity, if not found in maps
	DefaultAuthenticatedRole string `json:"default_authenticated_role" yaml:"default_authenticated_role"`
	// Enabled enables AWS identities
	Enabled bool `json:"enabled" yaml:"enabled"`
	// Roles maps a role name to a list of caller identities, either the full
	// ARN or the subject form "<account>:<resource-type>/<name>"
	// (e.g. "123456789012:assumed-role/ci").
	Roles map[string][]string `json:"roles" yaml:"roles"`
	// AllowedAccounts is a list of allowed AWS accounts,
	// if empty, all accounts are allowed
	AllowedAccounts []string `json:"allowed_accounts" yaml:"allowed_accounts"`
}

AWSIdentityMap maps AWS STS caller identities ("AWS4" presigned GetCallerIdentity URL tokens) to roles.

type CallerIdentity added in v0.17.0

type CallerIdentity struct {
	// GetCallerIdentityResponse is the response envelope.
	GetCallerIdentityResponse struct {
		GetCallerIdentityResult struct {
			Account string `json:"Account"`
			Arn     string `json:"Arn"`
			UserID  string `json:"UserId"`
		} `json:"GetCallerIdentityResult"`
		ResponseMetadata struct {
			RequestID string `json:"RequestId"`
		} `json:"ResponseMetadata"`
	} `json:"GetCallerIdentityResponse"`

	// Expires is the presigned URL expiry derived by ParseSTSTokenExpiration.
	Expires time.Time `json:"-"`
}

CallerIdentity is the JSON response of the AWS STS GetCallerIdentity API, see https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html. It is cached per presigned URL until Expires.

type CookiesConfig added in v0.37.396

type CookiesConfig struct {
	// Auth specifies the name of the cookie to be used for JWT authentication.
	// If empty, the auth cookie is not used.
	Auth string `json:"auth" yaml:"auth"`
	// CSRF specifies the name of the cookie to be used for CSRF protection:
	// the X-CSRF-Token header (x-csrf-token gRPC metadata) must equal it on
	// unsafe HTTP methods and on every gRPC and gRPC-Web call. It is
	// required when Auth is set and JWT is enabled; New returns an error
	// otherwise.
	CSRF string `json:"csrf" yaml:"csrf"`
	// Domain specifies the domain of the cookie to be used for authentication and CSRF protection.
	// If empty, the cookie domain is not set.
	Domain string `json:"domain" yaml:"domain"`
}

CookiesConfig configures cookie-based JWT authentication, used when no Authorization header (or gRPC "authorization" metadata) is present and JWT is enabled.

type GenericIdentityMap added in v0.17.0

type GenericIdentityMap struct {
	// DefaultAuthenticatedRole specifies role name for identity, if not found in maps
	DefaultAuthenticatedRole string `json:"default_authenticated_role" yaml:"default_authenticated_role"`
	// Enabled enables TLS client certificate identities
	Enabled bool `json:"enabled" yaml:"enabled"`
	// Roles maps a role name to the list of SPIFFE URIs (e.g. "spiffe://trust/ns/svc")
	Roles map[string][]string `json:"roles" yaml:"roles"`
}

GenericIdentityMap maps TLS client certificate identities to roles.

type IdentityMap

type IdentityMap struct {
	// DebugLogs enables extra debug logs of incoming gRPC metadata.
	DebugLogs bool `json:"debug_logs" yaml:"debug_logs"`
	// Strict returns an error when an applicable auth method fails; without
	// it the remaining methods are tried and the request falls back to guest.
	// Cookie-based JWT failures never return an error.
	Strict bool `json:"strict" yaml:"strict"`

	// Cookies configuration
	Cookies CookiesConfig `json:"cookies" yaml:"cookies"`
	// SkipAuthPaths lists exact HTTP paths for which authentication is skipped
	// and the guest identity is returned (HTTP only; not applied to gRPC).
	SkipAuthPaths []string `json:"skip_auth" yaml:"skip_auth"`
	// TLS maps client certificate SPIFFE URIs to roles.
	TLS GenericIdentityMap `json:"tls" yaml:"tls"`
	// JWT maps bearer JWT claims to roles.
	JWT JWTIdentityMap `json:"jwt" yaml:"jwt"`
	// DPoP maps DPoP-bound JWT claims to roles.
	DPoP JWTIdentityMap `json:"jwt_dpop" yaml:"jwt_dpop"`
	// AWS maps AWS STS caller identities to roles.
	AWS AWSIdentityMap `json:"aws" yaml:"aws"`
}

IdentityMap configures the authentication methods and role mappings used by the IdentityProvider returned by New. Every method is disabled by default.

func (*IdentityMap) GetCookiesConfig added in v0.37.396

func (i *IdentityMap) GetCookiesConfig() CookiesConfig

GetCookiesConfig returns the cookie configuration; safe on a nil receiver.

type IdentityProvider

type IdentityProvider interface {
	// ApplicableForRequest returns true if the request carries credentials
	// (Authorization header, auth cookie or client certificate) for an enabled method.
	ApplicableForRequest(*http.Request) bool
	// IdentityFromRequest returns the identity of the HTTP caller, or the
	// guest identity when no enabled method matches. An error is returned
	// only for failed authentication in Strict mode.
	IdentityFromRequest(*http.Request) (identity.Identity, error)

	// ApplicableForContext returns true if the gRPC incoming metadata or peer
	// carries credentials for an enabled method.
	ApplicableForContext(ctx context.Context) bool
	// IdentityFromContext returns the identity of the gRPC caller for the
	// given full method path, or the guest identity. DPoP proofs must name
	// POST https://<incoming :authority><full method path>.
	IdentityFromContext(ctx context.Context, uri string) (identity.Identity, error)
}

IdentityProvider extracts the caller identity from HTTP requests and gRPC contexts. IdentityFromRequest and IdentityFromContext are the mappers to pass to identity.NewContextHandler and identity.NewAuthUnaryInterceptor.

func New

func New(config *IdentityMap, jwt jwt.Parser) (IdentityProvider, error)

New returns an IdentityProvider for the given map. jwt is required when JWT or DPoP is enabled and may be nil otherwise; cookies.csrf is required when JWT is enabled with cookies.auth. Missing claim names default to DefaultSubjectClaim, DefaultRoleClaim and DefaultTenantClaim. The provider is safe for concurrent use.

type JWTIdentityMap

type JWTIdentityMap struct {
	// DefaultAuthenticatedRole specifies role name for identity, if not found in maps
	DefaultAuthenticatedRole string `json:"default_authenticated_role" yaml:"default_authenticated_role"`
	// Enabled enables JWT identities; requires a jwt.Parser to be passed to New
	Enabled bool `json:"enabled" yaml:"enabled"`
	// Issuer specifies the expected token issuer; empty disables the check
	Issuer string `json:"issuer" yaml:"issuer"`
	// Audience specifies the expected token audience; empty disables the check
	Audience string `json:"audience" yaml:"audience"`
	// SubjectClaim specifies claim name to be used as Subject,
	// by default it's `sub`, but can be changed to `email` etc
	SubjectClaim string `json:"subject_claim" yaml:"subject_claim"`
	// RoleClaim specifies claim name to be used for role mapping,
	// by default it's `email`, but can be changed to `sub` etc
	RoleClaim string `json:"role_claim" yaml:"role_claim"`
	// TenantClaim specifies claim name to be used for tenant mapping,
	// by default it's `tenant`, but can be changed to `org` etc
	TenantClaim string `json:"tenant_claim" yaml:"tenant_claim"`
	// Roles maps a role name to a list of RoleClaim values (by default emails)
	Roles map[string][]string `json:"roles" yaml:"roles"`
}

JWTIdentityMap maps JWT claims to roles; it is used for both bearer JWT and DPoP-bound JWT authentication.

Jump to

Keyboard shortcuts

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