security

package module
v0.0.0-...-e253a54 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 9 Imported by: 0

README

Hyperscale security Last release Documentation

Go Report Card

Branch Status Coverage
master Build Status Coveralls

A transport-agnostic authentication and authorization toolkit for Go — HTTP, gRPC and ConnectRPC, OAuth2, JWT, sessions, and a composable Voter-based access model. It is shipped as a multi-module workspace so you import only what you need.

Modules

Module Purpose
github.com/hyperscale-stack/security Core: Authentication, Engine, Manager, Voter, ADM
…/security/http httpsecnet/http middleware + authorization
…/security/grpc grpcsec — unary/stream interceptors
…/security/connectrpc connectrpcsec — ConnectRPC auth + authorize interceptors
…/security/basic HTTP Basic extractor + authenticator
…/security/bearer Bearer extractor + TokenVerifier authenticator
…/security/password BCrypt + Argon2id hashers (NeedsRehash)
…/security/jwt jwtsec — JWT signer/verifier, JWKS
…/security/session Stateless encrypted cookie sessions + CSRF
…/security/oauth2 OAuth2 server: profiles, grants, endpoints
…/security/oauth2/store/sql Production OAuth2 storage on database/sql
…/security/oauth2/store/redis Production OAuth2 storage on Redis

Install

go get github.com/hyperscale-stack/security
go get github.com/hyperscale-stack/security/http   # and any other module you need

Quick start — HTTP Basic

package main

import (
	"net/http"

	"github.com/hyperscale-stack/security"
	"github.com/hyperscale-stack/security/basic"
	httpsec "github.com/hyperscale-stack/security/http"
	"github.com/hyperscale-stack/security/password"
)

func main() {
	// loader is your UserLoader implementation (DB-backed, etc.).
	authenticator := basic.NewAuthenticator(loader, password.NewBCryptHasher(12))

	engine := security.NewEngine(
		security.NewManager(authenticator),
		basic.NewExtractor(),
	)

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		auth, _ := security.FromContext(r.Context())
		w.Write([]byte("hello " + auth.Name()))
	})

	http.ListenAndServe(":8080", httpsec.Middleware(engine)(mux))
}

Add authorization with a Voter and an AccessDecisionManager:

adm := security.NewAffirmativeDecisionManager(voter.HasRole("ADMIN"))
mux.Handle("/admin", httpsec.Authorize(adm, security.Role("ADMIN"))(adminHandler))

Documentation

Development

make sync     # go work sync
make build    # build every module
make test     # race + coverage
make lint     # golangci-lint with the shared config

License

Hyperscale security is licensed under the MIT license.

Documentation

Overview

Example (Engine)

Example_engine shows the canonical pipeline: extractor -> authenticator orchestrated by the Engine, ending with an AccessDecisionManager.

package main

import (
	"context"
	"fmt"

	"github.com/hyperscale-stack/security"
)

// userAuth is an example concrete Authentication produced by a fictional
// authenticator. The point of this example is the Engine wiring, so the type
// is kept minimal.
type userAuth struct {
	sub         string
	roles       []string
	credentials string
	verified    bool
}

func (u userAuth) Principal() security.Principal { return userPrincipal{sub: u.sub} }
func (u userAuth) Credentials() any              { return u.credentials }
func (u userAuth) Authorities() []string         { return u.roles }
func (u userAuth) IsAuthenticated() bool         { return u.verified }
func (u userAuth) Name() string                  { return u.sub }

type userPrincipal struct{ sub string }

func (p userPrincipal) Subject() string { return p.sub }

// staticExtractor returns a fixed userAuth when the "X-Demo-User" header is
// set, and (nil, nil) otherwise.
type staticExtractor struct{}

func (staticExtractor) Extract(_ context.Context, c security.Carrier) (security.Authentication, error) {
	sub := c.Get("X-Demo-User")
	if sub == "" {
		return nil, nil
	}

	return userAuth{sub: sub, credentials: "password"}, nil
}

// staticAuthenticator accepts only "alice" / "password".
type staticAuthenticator struct{}

func (staticAuthenticator) AuthenticatorName() string               { return "static" }
func (staticAuthenticator) Supports(_ security.Authentication) bool { return true }
func (staticAuthenticator) Authenticate(_ context.Context, a security.Authentication) (security.Authentication, error) {
	u, ok := a.(userAuth)
	if !ok {
		return a, security.ErrUnsupportedCredential
	}

	if u.sub != "alice" || u.credentials != "password" {
		return a, security.ErrInvalidCredentials
	}

	u.roles = []string{"ROLE_USER"}
	u.verified = true

	return u, nil
}

// demoCarrier is a tiny Carrier used to drive the example without depending
// on the http sub-module.
type demoCarrier struct{ headers map[string]string }

func (c *demoCarrier) Get(k string) string      { return c.headers[k] }
func (c *demoCarrier) Values(k string) []string { return []string{c.headers[k]} }
func (c *demoCarrier) Set(k, v string)          { c.headers[k] = v }
func (c *demoCarrier) Add(k, v string)          { c.headers[k] = v }

// roleVoter implements [security.Voter] for the example. It supports any
// attribute string starting with "role:" and grants when the principal has
// the matching role.
type roleVoter struct{}

func (roleVoter) Supports(a security.Attribute) bool {
	if a == nil {
		return false
	}
	const prefix = "role:"
	if len(a.String()) < len(prefix) {
		return false
	}

	return a.String()[:len(prefix)] == prefix
}

func (roleVoter) Vote(_ context.Context, auth security.Authentication, attrs []security.Attribute) security.Decision {
	for _, a := range attrs {
		const prefix = "role:"
		if len(a.String()) < len(prefix) || a.String()[:len(prefix)] != prefix {
			continue
		}

		want := a.String()[len(prefix):]
		for _, r := range auth.Authorities() {
			if r == want {
				return security.DecisionGrant
			}
		}
	}

	return security.DecisionDeny
}

type roleAttr string

func (r roleAttr) String() string { return "role:" + string(r) }

func main() {
	engine := security.NewEngine(
		security.NewManager(staticAuthenticator{}),
		staticExtractor{},
	)

	carrier := &demoCarrier{headers: map[string]string{"X-Demo-User": "alice"}}

	ctx, auth, err := engine.Process(context.Background(), carrier)
	if err != nil {
		fmt.Println("auth error:", err)

		return
	}

	fmt.Printf("authenticated=%t subject=%s\n", auth.IsAuthenticated(), auth.Principal().Subject())

	adm := security.NewAffirmativeDecisionManager(roleVoter{})
	if err := adm.Decide(ctx, auth, []security.Attribute{roleAttr("ROLE_USER")}); err != nil {
		fmt.Println("denied:", err)

		return
	}

	fmt.Println("granted")
}
Output:
authenticated=true subject=alice
granted

Index

Examples

Constants

View Source
const (
	// AttrAuthenticated reports whether the resulting Authentication is
	// authenticated. Value: bool.
	AttrAuthenticated = attribute.Key("security.authenticated")

	// AttrPrincipalSubject is the principal subject. Emission is gated by the
	// subject-redaction policy (see SetSubjectAttributeMode) to avoid leaking
	// personal data into trace backends; the default is a hashed prefix.
	AttrPrincipalSubject = attribute.Key("security.principal.subject")

	// AttrExtractorsCount counts the extractors tried by an Engine call.
	// Value: int.
	AttrExtractorsCount = attribute.Key("security.extractors.count")

	// AttrAuthenticatorsCount counts the authenticators tried by a Manager.
	// Value: int.
	AttrAuthenticatorsCount = attribute.Key("security.authenticators.count")

	// AttrAuthenticatorName names the authenticator that produced the final
	// authenticated value, when known. Value: string.
	AttrAuthenticatorName = attribute.Key("security.authenticator.name")

	// AttrStrategy names the AccessDecisionManager strategy that took the
	// final decision. Value: "affirmative" | "consensus" | "unanimous".
	AttrStrategy = attribute.Key("security.strategy")

	// AttrDecision is the final authorization decision.
	// Value: "permit" | "deny" | "abstain".
	AttrDecision = attribute.Key("security.decision")

	// AttrAttributes is the joined String() form of the Attributes considered
	// for an authorization decision. Value: string.
	AttrAttributes = attribute.Key("security.attributes")
)

Span attribute keys used across the core. They are kept here as typed constants so that documentation in docs/observability.md can be diffed against the source of truth.

Variables

View Source
var (
	// ErrInvalidCredentials indicates that the supplied credentials could not
	// be validated (bad password, unknown user, malformed token). Maps to
	// HTTP 401 / gRPC Unauthenticated.
	ErrInvalidCredentials = newSentinel("security: invalid credentials")

	// ErrClientSecretMismatch indicates that an OAuth2 client presented a
	// secret that did not match the registered value. Maps to HTTP 401.
	ErrClientSecretMismatch = newSentinel("security: oauth2 client secret mismatch")

	// ErrTokenExpired indicates that a valid token has passed its expiry.
	// Maps to HTTP 401.
	ErrTokenExpired = newSentinel("security: token expired")

	// ErrTokenNotFound indicates that the presented token does not exist in
	// the configured storage. Maps to HTTP 401.
	ErrTokenNotFound = newSentinel("security: token not found")

	// ErrUnsupportedCredential indicates that no provider recognized the
	// credential type. Maps to HTTP 400.
	ErrUnsupportedCredential = newSentinel("security: unsupported credential type")

	// ErrNoExtractor indicates that the [Engine] was configured without any
	// [Extractor]. The Engine returns the anonymous authentication and this
	// error so that the caller can distinguish "no extractor" from "all
	// extractors found nothing".
	ErrNoExtractor = newSentinel("security: no extractor configured")

	// ErrAuthenticatorRefused is the umbrella error returned by [Manager]
	// when every supporting [Authenticator] rejected the credential. The
	// individual errors are joined via errors.Join and reachable through
	// errors.Is / errors.As.
	ErrAuthenticatorRefused = newSentinel("security: every authenticator refused the credential")

	// ErrAccessDenied indicates that authorisation voting denied access.
	// Maps to HTTP 403 / gRPC PermissionDenied.
	ErrAccessDenied = newSentinel("security: access denied")

	// ErrInsufficientScope indicates that the principal is authenticated but
	// does not carry the OAuth2 scope required for the resource. Maps to
	// HTTP 403 with the "insufficient_scope" WWW-Authenticate parameter.
	ErrInsufficientScope = newSentinel("security: insufficient scope")
)

Sentinel errors. Wrap them via fmt.Errorf("...: %w", ErrXxx) when adding contextual information so errors.Is keeps working.

Functions

func WithAbstainFallback

func WithAbstainFallback(d Decision) admOption

WithAbstainFallback controls the verdict when every unanimous voter abstains. Default: DecisionDeny.

func WithAuthentication

func WithAuthentication(ctx context.Context, auth Authentication) context.Context

WithAuthentication returns a copy of ctx with auth attached. Subsequent calls overwrite the previous value; this is the expected behavior when an authenticator promotes an unauthenticated value to an authenticated one.

Passing a nil Authentication clears the slot — useful for "logout" middlewares.

func WithTieBreak

func WithTieBreak(d Decision) admOption

WithTieBreak controls the consensus strategy when grant and deny votes are equal in number. Default: DecisionDeny.

Types

type AccessDecisionManager

type AccessDecisionManager interface {
	// Decide returns nil on grant, [ErrAccessDenied] on deny.
	// Wrapping callers add a short message indicating the strategy used.
	Decide(ctx context.Context, auth Authentication, attrs []Attribute) error
}

AccessDecisionManager combines the verdicts of multiple [Voter]s into a single decision. Three strategies are provided, mirroring Spring Security:

  • Affirmative — a single DecisionGrant grants access; everything else denies. Abstentions are ignored. The strictest "fail closed by default" policy.
  • Consensus — the majority wins. Ties default to deny; pass WithTieBreak(DecisionGrant) to flip the policy.
  • Unanimous — every voter that does not abstain MUST grant. A single deny refuses; if every voter abstains, the result depends on WithAbstainFallback.

Implementations are safe for concurrent use.

func NewAffirmativeDecisionManager

func NewAffirmativeDecisionManager(voters ...Voter) AccessDecisionManager

NewAffirmativeDecisionManager returns an AccessDecisionManager that grants access as soon as one voter does, and denies otherwise.

func NewConsensusDecisionManager

func NewConsensusDecisionManager(voters []Voter, opts ...admOption) AccessDecisionManager

NewConsensusDecisionManager returns an AccessDecisionManager that follows majority rule. Pass WithTieBreak to override the default (deny-on-tie) behavior.

func NewUnanimousDecisionManager

func NewUnanimousDecisionManager(voters []Voter, opts ...admOption) AccessDecisionManager

NewUnanimousDecisionManager returns an AccessDecisionManager that refuses on a single deny and otherwise grants when at least one voter grants. Pass WithAbstainFallback to control the all-abstain case.

type Attribute

type Attribute interface {
	// String returns a stable, log-friendly form of the attribute. It is used
	// by [AccessDecisionManager] for OTel attributes; it MUST NOT include
	// any secret or PII.
	String() string
}

Attribute is an opaque authorization predicate carried alongside a request. Voters opt-in via Voter.Supports and inspect the concrete type through type switches. Four concrete attributes are shipped below; applications can define their own (they just need to implement String()).

func Authority

func Authority(name string) Attribute

Authority constructs an AuthorityAttribute.

func Permission

func Permission(name string, predicate func(ctx context.Context, auth Authentication) bool) Attribute

Permission constructs a PermissionAttribute in one call.

func Role

func Role(name string) Attribute

Role constructs a RoleAttribute from a bare role name.

func Scope

func Scope(name string) Attribute

Scope constructs a ScopeAttribute.

type Authentication

type Authentication interface {
	// Principal returns the identity carried by this authentication.
	// MUST return [AnonymousPrincipal] for unauthenticated values and
	// MUST NOT return nil.
	Principal() Principal

	// Credentials returns the raw credentials presented by the principal.
	// For a token-based authentication this is typically the token string;
	// for username/password it is the cleartext password. Implementations
	// SHOULD zero or omit secret material once authentication has succeeded
	// to limit accidental leakage through logging or panics.
	//
	// The return type is intentionally any: typed accessors are provided by
	// each scheme module (basic.Password(), bearer.Token(), ...).
	Credentials() any

	// Authorities returns the authorities (roles, scopes, permissions) the
	// system has granted to this principal. The slice is read-only;
	// implementations SHOULD return the same backing slice across calls.
	Authorities() []string

	// IsAuthenticated reports whether the credentials have been validated by
	// an [Authenticator]. Voters use this to short-circuit denials before
	// inspecting authorities.
	IsAuthenticated() bool

	// Name returns a stable, log-friendly identifier for this authentication.
	// It is typically the principal subject; for client_credentials flows it
	// can be the client ID. It MUST be safe to include in structured logs
	// (no secrets, no high-cardinality values that are not the subject).
	Name() string
}

Authentication is an immutable snapshot of a security context: who is acting (the Principal), what proof was presented (the credentials), what authorities the system has granted them, and whether the proof has been verified.

Authentication values flow through three logical stages during a request:

  1. An Extractor reads raw credentials from a Carrier and constructs an unauthenticated value (IsAuthenticated() == false).
  2. A matching Authenticator validates the credentials and returns a NEW authenticated value (IsAuthenticated() == true). The original value MUST NOT be mutated.
  3. Authorisation [Voter]s inspect the value to grant or deny access.

Implementations MUST be safe for concurrent reads. Because every state change goes through a fresh value, no synchronization is required for callers.

func Anonymous

func Anonymous() Authentication

Anonymous returns the singleton Authentication used when no credential could be extracted from a Carrier. It is safe to call from any goroutine; the returned value is shared and immutable.

Voters that opt-in to anonymous access (see the voter package's Anonymous) match this value; the default policy of AccessDecisionManager is to deny when no voter grants, so anonymous calls fail closed by default.

func FromContext

func FromContext(ctx context.Context) (Authentication, bool)

FromContext returns the Authentication stored in ctx and a boolean indicating whether one was present. When the slot is empty, it returns the anonymous authentication (see Anonymous) so callers can rely on a non-nil value without a nil check.

type Authenticator

type Authenticator interface {
	Supports(auth Authentication) bool
	Authenticate(ctx context.Context, auth Authentication) (Authentication, error)
}

Authenticator validates an Authentication produced by an Extractor and returns a NEW authenticated value. It MUST NOT mutate its input — the Authentication is treated as immutable everywhere in the core.

Two-step contract:

  • Supports reports whether the authenticator recognizes the credential type. Implementations MUST be cheap (a type switch); they MUST NOT perform I/O.
  • Authenticate validates the credential and either returns the new, authenticated value or an error wrapping a security sentinel (ErrInvalidCredentials, ErrTokenExpired, ...). Returning (ErrUnsupportedCredential) is the canonical way to bail out at runtime when Supports returned true but the value was nonetheless out of scope.

Implementations MUST be safe for concurrent use.

type AuthenticatorFunc

type AuthenticatorFunc func(ctx context.Context, auth Authentication) (Authentication, error)

AuthenticatorFunc adapts a function to the Authenticator interface. It reports Supports == true for every input; callers wanting selectivity should write a concrete type instead.

func (AuthenticatorFunc) Authenticate

func (f AuthenticatorFunc) Authenticate(ctx context.Context, auth Authentication) (Authentication, error)

Authenticate implements Authenticator.

func (AuthenticatorFunc) Supports

Supports implements Authenticator.

type AuthorityAttribute

type AuthorityAttribute string

AuthorityAttribute names a free-form authority string. Unlike RoleAttribute it carries no convention — the configured voter compares the value verbatim against Authentication.Authorities.

func (AuthorityAttribute) String

func (a AuthorityAttribute) String() string

String implements Attribute. Output is the bare authority name.

type Carrier

type Carrier interface {
	// Get returns the first value associated with the given key, or the
	// empty string if absent. Keys are case-insensitive in the HTTP sense.
	Get(key string) string

	// Values returns all values associated with the given key, or a nil
	// slice if absent. The caller MUST NOT mutate the returned slice.
	Values(key string) []string

	// Set replaces all values associated with the given key.
	Set(key, value string)

	// Add appends a value to the list associated with the given key.
	Add(key, value string)
}

Carrier abstracts a transport-level message (an HTTP request, gRPC metadata, a queue envelope) from which credentials can be read and security artifacts (challenges, cookies, headers) can be written.

The interface mimics http.Header semantics so that the HTTP adapter is a thin wrapper. For transports that do not naturally support multi-valued keys (e.g. websocket frames), implementations MAY collapse Values() to a single-element slice and treat Add() as Set().

Implementations MUST be safe for concurrent reads but MAY require external synchronization for writes — adapters are expected to wrap a request scope, which is serial by construction.

type Clock

type Clock interface {
	Now() time.Time
}

Clock abstracts time.Now to make time-sensitive code (expiry checks, TTLs, token rotation windows) deterministic in tests. Implementations MUST be safe for concurrent use.

var DefaultClock Clock = SystemClock{}

DefaultClock is the package-level Clock used when none is supplied via configuration. It is a value, not a pointer, so it is safe to copy.

type Decision

type Decision int

Decision is the verdict returned by a Voter for a given authentication and attribute set. Three values are defined:

const (
	DecisionDeny    Decision = -1
	DecisionAbstain Decision = 0
	DecisionGrant   Decision = 1
)

Voting verdicts. The numeric layout (-1/0/1) is deliberate so that algorithms summing decisions remain readable.

func (Decision) String

func (d Decision) String() string

String returns a stable lowercase form ("permit", "deny", "abstain") used for OTel attribute values. "permit" is preferred over "grant" to match the XACML vocabulary widely understood by security teams.

type Engine

type Engine interface {
	// Process runs extractors in order and consults the manager on the first
	// non-empty result. The returned context always carries an Authentication
	// (the anonymous one when nothing was extracted).
	Process(ctx context.Context, c Carrier) (context.Context, Authentication, error)
}

Engine is the high-level entry point: it drives a chain of [Extractor]s against a Carrier, hands the produced Authentication to its Manager, and returns a context enriched with the result so downstream handlers can call FromContext.

Engine is safe for concurrent use.

func NewEngine

func NewEngine(m Manager, extractors ...Extractor) Engine

NewEngine returns an Engine. Passing zero extractors is allowed; the engine will produce the anonymous authentication and return ErrNoExtractor so callers can fail-closed if they wish.

type Extractor

type Extractor interface {
	Extract(ctx context.Context, c Carrier) (Authentication, error)
}

Extractor pulls raw, unauthenticated credentials from a Carrier and returns an Authentication that captures them. The returned value MUST have IsAuthenticated() == false: validation is the Authenticator's job.

Sentinel conventions:

  • Return (nil, nil) when no credentials of the supported scheme are present. The Engine treats this as "this extractor does not apply" and consults the next one.
  • Return (nil, err) wrapping a security sentinel when credentials were present but malformed (e.g. invalid base64 in Basic). The Engine surfaces err to the caller and stops; downstream authenticators are not invoked.

Implementations MUST be safe for concurrent use.

type Manager

type Manager interface {
	Authenticate(ctx context.Context, auth Authentication) (Authentication, error)
}

Manager orchestrates a chain of [Authenticator]s with first-success-wins semantics:

  • Authenticators are consulted in registration order.
  • The first authenticator whose Supports() returns true is invoked.
  • On success, the resulting Authentication is returned immediately; subsequent authenticators are NOT consulted.
  • On error, the next supporting authenticator is tried; if every one fails, the joined error is wrapped in ErrAuthenticatorRefused.
  • If no authenticator supports the credential, ErrUnsupportedCredential is returned. The Engine then surfaces it as a 400 in the HTTP adapter.

Manager is safe for concurrent use.

func NewManager

func NewManager(authenticators ...Authenticator) Manager

NewManager returns a Manager consulting the given authenticators in order. Passing zero authenticators is allowed; the returned manager will always return ErrUnsupportedCredential.

Example

ExampleNewManager illustrates first-success-wins semantics.

package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/hyperscale-stack/security"
)

// userAuth is an example concrete Authentication produced by a fictional
// authenticator. The point of this example is the Engine wiring, so the type
// is kept minimal.
type userAuth struct {
	sub         string
	roles       []string
	credentials string
	verified    bool
}

func (u userAuth) Principal() security.Principal { return userPrincipal{sub: u.sub} }
func (u userAuth) Credentials() any              { return u.credentials }
func (u userAuth) Authorities() []string         { return u.roles }
func (u userAuth) IsAuthenticated() bool         { return u.verified }
func (u userAuth) Name() string                  { return u.sub }

type userPrincipal struct{ sub string }

func (p userPrincipal) Subject() string { return p.sub }

func main() {
	first := security.AuthenticatorFunc(func(_ context.Context, a security.Authentication) (security.Authentication, error) {
		return a, errors.New("first refuses")
	})
	second := security.AuthenticatorFunc(func(_ context.Context, a security.Authentication) (security.Authentication, error) {
		return userAuth{sub: "bob", verified: true}, nil
	})

	m := security.NewManager(first, second)

	auth, err := m.Authenticate(context.Background(), userAuth{sub: "bob"})
	fmt.Println(auth.Name(), err)
}
Output:
bob <nil>

type NamedAuthenticator

type NamedAuthenticator interface {
	AuthenticatorName() string
}

NamedAuthenticator is an optional capability: when an Authenticator implements it, the Manager records the name in the OTel span so observability backends can attribute decisions per provider.

type PermissionAttribute

type PermissionAttribute struct {
	// Name is the human-readable label of the permission. It populates the
	// OTel attributes; keep it stable across deployments.
	Name string
	// Predicate is invoked by the permission voter with the live
	// authentication. A nil predicate is treated as DecisionDeny.
	Predicate func(ctx context.Context, auth Authentication) bool
}

PermissionAttribute carries an arbitrary predicate evaluated by the permission voter. It is the escape hatch for application-specific authorization (ABAC, ownership checks, time-of-day windows, ...). The predicate MUST be pure (no I/O) and safe for concurrent use.

func (PermissionAttribute) String

func (p PermissionAttribute) String() string

String implements Attribute. Output is "permission:<Name>".

type Principal

type Principal interface {
	// Subject returns the stable, unique identifier of the principal. It is
	// the value that authorisation checks key off (`sub` claim, user ID,
	// client ID, ...). Implementations MUST return the same value across
	// calls for the lifetime of a request.
	Subject() string
}

Principal identifies the subject of an Authentication. Implementations represent end users, service clients, devices, or any other authenticatable entity.

The interface is intentionally minimal: any authorisation-specific data (roles, scopes, claims, ...) is carried by Authentication.Authorities or by attaching a concrete implementation via [Authentication.Attribute]. This keeps the core decoupled from any user store schema.

var AnonymousPrincipal Principal = anonymousPrincipal{}

AnonymousPrincipal is the singleton principal returned by the core when no credentials were extracted from a Carrier. Authorisation voters use it to distinguish "no authentication attempt" from "authentication failed".

type RoleAttribute

type RoleAttribute string

RoleAttribute names a role expected on the authenticated principal. Roles use the Spring Security "ROLE_" prefix at the wire level (in OTel attributes and in custom Authorities() slices) but the constructor accepts the bare name to keep usage idiomatic.

func (RoleAttribute) Name

func (r RoleAttribute) Name() string

Name returns the bare role name (without the ROLE_ prefix).

func (RoleAttribute) String

func (r RoleAttribute) String() string

String implements Attribute. Output is "ROLE_<name>" — Spring-compatible for ops tooling that already keys off that convention.

type ScopeAttribute

type ScopeAttribute string

ScopeAttribute names an OAuth2 scope expected on the authenticated principal. Scope names follow the RFC 6749 §3.3 grammar but this type stays format-agnostic.

func (ScopeAttribute) Name

func (s ScopeAttribute) Name() string

Name returns the bare scope name.

func (ScopeAttribute) String

func (s ScopeAttribute) String() string

String implements Attribute. Output is "scope:<name>".

type SecurityError

type SecurityError interface {
	error
	// contains filtered or unexported methods
}

SecurityError is the marker interface implemented by every error returned by this module's public API. Callers SHOULD use errors.Is/errors.As against the sentinel values exported here rather than relying on string matching.

The unexported method securityError() prevents foreign types from accidentally satisfying the interface.

type SystemClock

type SystemClock struct{}

SystemClock is the default Clock returning time.Now().

func (SystemClock) Now

func (SystemClock) Now() time.Time

Now returns the current wall-clock time.

type Voter

type Voter interface {
	Supports(attr Attribute) bool
	Vote(ctx context.Context, auth Authentication, attrs []Attribute) Decision
}

Voter is the unit of authorisation logic. It inspects an Authentication against a set of [Attribute]s and returns a Decision. Voters MUST be pure (no I/O) and safe for concurrent use.

Supports is a fast-path filter: a voter that does not recognize any of the passed attributes SHOULD return false to short-circuit the call. When Supports returns false, the AccessDecisionManager records an abstention for the voter without invoking Vote.

Directories

Path Synopsis
example
oauth2 module
oauth2 module
Package voter ships the catalog of stock security.Voter implementations consumed by security.AccessDecisionManager.
Package voter ships the catalog of stock security.Voter implementations consumed by security.AccessDecisionManager.

Jump to

Keyboard shortcuts

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