security

package
v0.49.0 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: Apache-2.0 Imports: 34 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AccessTokenExpires  = time.Minute * 30
	RefreshTokenExpires = time.Hour * 24 * 7
)
View Source
const (
	AuthTypeJWTToken = "jwt_token"
)
View Source
const (
	AuthTypeOpaqueToken = "opaque_token"
)
View Source
const (
	AuthTypePassword = "password"
)
View Source
const (
	AuthTypeRefresh = "refresh"
)
View Source
const AuthTypeSignature = "signature"

AuthTypeSignature is the authentication type for signature-based authentication.

View Source
const AuthTypeTrustCode = "trust_code"

AuthTypeTrustCode is the login mechanism that redeems a trust-login code.

Variables

View Source
var (
	// ErrIPWhitelistNameBlank rejects a whitelist declared under a blank name.
	ErrIPWhitelistNameBlank = errors.New("ip whitelist name must not be blank")
	// ErrIPWhitelistEmpty rejects a whitelist with no usable entries, which
	// could never authenticate a request.
	ErrIPWhitelistEmpty = errors.New("ip whitelist must contain at least one IP or CIDR entry")
	// ErrIPWhitelistEntryInvalid rejects an entry that is neither an IP
	// address nor a CIDR range.
	ErrIPWhitelistEntryInvalid = errors.New("ip whitelist entry is neither an IP address nor a CIDR range")
)

Configuration faults raised while building the config-backed IP whitelist loader; they surface as fx start-up errors, never through the API.

View Source
var (
	// ErrAPIKeyNameBlank rejects an API key declared under a blank name.
	ErrAPIKeyNameBlank = errors.New("api key name must not be blank")
	// ErrAPIKeyValueBlank rejects an API key with a blank key value, which
	// could never authenticate a request.
	ErrAPIKeyValueBlank = errors.New("api key value must not be blank")
	// ErrBasicAccountUsernameBlank rejects a basic account declared under a
	// blank username.
	ErrBasicAccountUsernameBlank = errors.New("basic account username must not be blank")
	// ErrBasicAccountPasswordBlank rejects a basic account with a blank
	// password, which could never authenticate a request.
	ErrBasicAccountPasswordBlank = errors.New("basic account password must not be blank")
)

Configuration faults raised while building the config-backed API key and basic account loaders; they surface as fx start-up errors, never through the API.

View Source
var (
	// ErrTrustLoginExternalAppLoaderMissing rejects an enabled gateway with no
	// security.ExternalAppLoader, which supplies the per-app signing secret
	// every handoff is verified against.
	ErrTrustLoginExternalAppLoaderMissing = errors.New("vef.security.trust_login is enabled but no security.ExternalAppLoader is registered to supply app signing secrets")
	// ErrTrustLoginUserResolutionMissing rejects an enabled gateway that can
	// map no external identifier onto a local user: it needs either a
	// security.TrustUserResolver or a security.UserLoader.
	ErrTrustLoginUserResolutionMissing = errors.New(
		"vef.security.trust_login is enabled but neither a security.TrustUserResolver nor a security.UserLoader is registered to resolve external users",
	)
)

Configuration faults raised while building the trust-login gateway; they surface as fx start-up errors, never through the API.

View Source
var Module = fx.Module(
	"vef:security",
	fx.Decorate(func(cfg *config.SecurityConfig) *config.SecurityConfig {
		if cfg.TokenExpires <= 0 {
			cfg.TokenExpires = RefreshTokenExpires
		}

		if cfg.RefreshNotBefore <= 0 {
			cfg.RefreshNotBefore = AccessTokenExpires / 2
		}

		if cfg.LoginRateLimit <= 0 {
			cfg.LoginRateLimit = 6
		}

		if cfg.RefreshRateLimit <= 0 {
			cfg.RefreshRateLimit = 1
		}

		return cfg
	}),
	fx.Decorate(
		fx.Annotate(
			func(loader security.RolePermissionsLoader, bus event.Bus) security.RolePermissionsLoader {
				if loader == nil {
					return nil
				}

				return security.NewCachedRolePermissionsLoader(loader, bus)
			},
			fx.ParamTags(`optional:"true"`),
		),
	),
	fx.Provide(
		password.NewBcryptEncoder,
		newLoginGuard,
		fx.Annotate(
			newPasswordValidator,
			fx.ParamTags(``, ``, `optional:"true"`),
		),
		newJWT,
		fx.Annotate(
			newTokenAuthenticators,
			fx.ParamTags(``, ``, `optional:"true"`),
			fx.ResultTags(`group:"vef:security:authenticators,flatten"`),
		),
		NewJWTTokenGenerator,
		NewOpaqueTokenGenerator,
		fx.Annotate(
			security.NewSessionRevocationNotifier,
			fx.ParamTags(`group:"vef:security:session_revocation_listeners"`),
		),
		newSessionStore,
		fx.Annotate(
			newNonceStore,
			fx.ParamTags(`optional:"true"`),
		),
		fx.Annotate(
			newTrustCodeStore,
			fx.ParamTags(`optional:"true"`),
		),
		newSessionPolicy,
		newTokenGenerator,
		security.NewJWTChallengeTokenStore,
		fx.Annotate(
			newTrustLoginAuthenticators,
			fx.ResultTags(`group:"vef:security:authenticators,flatten"`),
		),
		fx.Annotate(
			NewTrustLoginMiddleware,
			fx.ResultTags(`group:"vef:app:middlewares"`),
		),
		fx.Annotate(
			NewSignatureAuthenticator,
			fx.ParamTags(`optional:"true"`, `optional:"true"`),
			fx.ResultTags(`group:"vef:security:authenticators"`),
		),
		fx.Annotate(
			NewPasswordAuthenticator,
			fx.ParamTags(`optional:"true"`, `optional:"true"`, `optional:"true"`),
			fx.ResultTags(`group:"vef:security:authenticators"`),
		),
		fx.Annotate(
			NewAuthManager,
			fx.ParamTags(`group:"vef:security:authenticators"`),
		),
		fx.Annotate(
			NewRBACPermissionChecker,
			fx.ParamTags(`optional:"true"`),
		),
		fx.Annotate(
			NewRBACDataPermissionResolver,
			fx.ParamTags(`optional:"true"`),
		),
		fx.Annotate(
			NewAuthResource,
			fx.ResultTags(`group:"vef:api:resources"`),
		),
	),
)

Functions

func NewAuthManager

func NewAuthManager(authenticators []security.Authenticator) security.AuthManager

func NewAuthResource

func NewAuthResource(params AuthResourceParams) api.Resource

NewAuthResource creates a new authentication resource with the provided auth manager and token generator.

func NewConfigAPIKeyLoader added in v0.39.0

func NewConfigAPIKeyLoader(cfg *config.SecurityConfig) (security.APIKeyLoader, error)

NewConfigAPIKeyLoader builds the framework's default security.APIKeyLoader from the static vef.security.api_keys configuration. Because the source is immutable deployment config, every entry is validated eagerly: a blank name or a blank key value fails construction (and therefore application start-up) instead of silently denying every request at runtime.

func NewConfigBasicAccountLoader added in v0.39.0

func NewConfigBasicAccountLoader(cfg *config.SecurityConfig) (security.BasicAccountLoader, error)

NewConfigBasicAccountLoader builds the framework's default security.BasicAccountLoader from the static vef.security.basic_accounts configuration. Because the source is immutable deployment config, every account is validated eagerly: a blank username or a blank password fails construction (and therefore application start-up) instead of silently denying every request at runtime.

func NewConfigIPWhitelistLoader added in v0.34.0

func NewConfigIPWhitelistLoader(cfg *config.SecurityConfig) (security.IPWhitelistLoader, error)

NewConfigIPWhitelistLoader builds the framework's default security.IPWhitelistLoader from the static vef.security.ip_whitelists configuration. Because the source is immutable deployment config, every whitelist is validated eagerly: a blank name, an empty whitelist, or an entry that is neither an IP address nor a CIDR range fails construction (and therefore application start-up) instead of silently denying every request at runtime.

func NewJWTAuthenticator

func NewJWTAuthenticator(jwt *security.JWT) security.Authenticator

func NewJWTRefreshAuthenticator

func NewJWTRefreshAuthenticator(jwt *security.JWT, userLoader security.UserLoader) security.Authenticator

func NewOpaqueTokenAuthenticator added in v0.38.0

func NewOpaqueTokenAuthenticator(store security.SessionStore, policy security.SessionPolicy) security.Authenticator

func NewPasswordAuthenticator

func NewPasswordAuthenticator(
	loader security.UserLoader,
	encoder password.Encoder,
	decryptor security.PasswordDecryptor,
) security.Authenticator

func NewRBACDataPermissionResolver

func NewRBACDataPermissionResolver(loader security.RolePermissionsLoader) security.DataPermissionResolver

NewRBACDataPermissionResolver creates a new RBAC data permission resolver.

func NewSignatureAuthenticator

func NewSignatureAuthenticator(
	loader security.ExternalAppLoader,
	nonceStore security.NonceStore,
) security.Authenticator

NewSignatureAuthenticator creates a new signature authenticator.

A single long-lived Signature verifier is built here and reused across every request. This is what makes server-side replay protection actually work: the nonce store is shared process-wide rather than recreated per request (a fresh per-request in-memory store would always see every nonce as absent, turning replay detection into a no-op and leaking a GC goroutine on each call). The per-request app secret is supplied to VerifyWithSecret at verification time, so the verifier's own bound secret is never used.

func NewTrustLoginMiddleware added in v0.49.0

func NewTrustLoginMiddleware(params TrustLoginMiddlewareParams) (app.Middleware, error)

NewTrustLoginMiddleware creates the trust-login gateway. It returns nil while the feature is disabled, and fails the boot when it is enabled without the collaborators it cannot work without — an unauthenticatable gateway that answers 401 to every handoff is far harder to diagnose than a refused start.

Types

type AuthResource

type AuthResource struct {
	api.Resource
	// contains filtered or unexported fields
}

AuthResource handles authentication-related API endpoints.

func (*AuthResource) GetUserInfo

func (a *AuthResource) GetUserInfo(ctx fiber.Ctx, principal *security.Principal, params api.Params) error

GetUserInfo retrieves user information via UserInfoLoader. Requires a UserInfoLoader implementation to be provided.

func (*AuthResource) Login

func (a *AuthResource) Login(ctx fiber.Ctx, params LoginParams) error

Login authenticates a user and returns a LoginResult. When challenge providers are configured and applicable, the result contains a challenge token and pending challenges instead of auth tokens.

func (*AuthResource) Logout

func (a *AuthResource) Logout(ctx fiber.Ctx) error

Logout revokes the opaque session backing the presented token so it can no longer authenticate. Under the stateless JWT mechanism no session exists, so it is a no-op and clients must drop their stored tokens.

func (*AuthResource) Refresh

func (a *AuthResource) Refresh(ctx fiber.Ctx, params RefreshParams) error

Refresh refreshes the access token using a valid refresh token. User data reload logic is handled by JwtRefreshAuthenticator.

func (*AuthResource) ResolveChallenge

func (a *AuthResource) ResolveChallenge(ctx fiber.Ctx, params ResolveChallengeParams) error

ResolveChallenge validates a user's response to a login challenge. On success, either issues real auth tokens (all challenges resolved) or evaluates the next challenge sequentially.

A ChallengeProvider may reject a response by returning a typed result.Error (e.g. security.ErrOTPCodeInvalid) to control the client-facing code; a bare error is normalized to security.ErrChallengeResolveFailed (code security.ErrCodeChallengeResolveFailed).

type AuthResourceParams

type AuthResourceParams struct {
	fx.In

	AuthManager         security.AuthManager
	TokenGenerator      security.TokenGenerator
	ChallengeTokenStore security.ChallengeTokenStore
	UserInfoLoader      security.UserInfoLoader `optional:"true"`
	LoginGuard          security.LoginGuard     `optional:"true"`
	SessionStore        security.SessionStore
	RevocationNotifier  *security.SessionRevocationNotifier `optional:"true"`
	ChallengeProviders  []security.ChallengeProvider        `group:"vef:security:challenge_providers"`
	Bus                 event.Bus
	SecurityConfig      *config.SecurityConfig
}

AuthResourceParams holds the dependencies for AuthResource construction.

type AuthenticatorAuthManager

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

func (*AuthenticatorAuthManager) Authenticate

func (am *AuthenticatorAuthManager) Authenticate(ctx context.Context, authentication security.Authentication) (*security.Principal, error)

type JWTRefreshAuthenticator

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

func (*JWTRefreshAuthenticator) Authenticate

func (j *JWTRefreshAuthenticator) Authenticate(ctx context.Context, authentication security.Authentication) (*security.Principal, error)

func (*JWTRefreshAuthenticator) Supports

func (*JWTRefreshAuthenticator) Supports(authType string) bool

type JWTTokenAuthenticator

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

func (*JWTTokenAuthenticator) Authenticate

func (ja *JWTTokenAuthenticator) Authenticate(_ context.Context, authentication security.Authentication) (*security.Principal, error)

func (*JWTTokenAuthenticator) Supports

func (*JWTTokenAuthenticator) Supports(authType string) bool

type JWTTokenGenerator

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

func NewJWTTokenGenerator

func NewJWTTokenGenerator(jwt *security.JWT, securityConfig *config.SecurityConfig) *JWTTokenGenerator

func (*JWTTokenGenerator) Generate

type LoginParams

type LoginParams struct {
	api.P

	Type        string `json:"type" validate:"required" label_i18n:"auth_type"`
	Principal   string `json:"principal" validate:"required" label_i18n:"auth_principal"`
	Credentials any    `json:"credentials" validate:"required" label_i18n:"auth_credentials"`
}

LoginParams represents the request parameters for user login.

type OpaqueTokenAuthenticator added in v0.38.0

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

OpaqueTokenAuthenticator validates a stateful opaque token by resolving it to a server-side session, returning the session's principal snapshot. When sliding renewal is enabled it extends the session's idle timeout on each request, so an active session never expires mid-use.

func (*OpaqueTokenAuthenticator) Authenticate added in v0.38.0

func (a *OpaqueTokenAuthenticator) Authenticate(ctx context.Context, authentication security.Authentication) (*security.Principal, error)

func (*OpaqueTokenAuthenticator) Supports added in v0.38.0

func (*OpaqueTokenAuthenticator) Supports(authType string) bool

type OpaqueTokenGenerator added in v0.38.0

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

OpaqueTokenGenerator issues stateful opaque tokens: it opens a server-side session (enforcing the per-account concurrency policy) and returns a random token whose hash keys that session. It carries no refresh token, since the session renews itself on use.

func NewOpaqueTokenGenerator added in v0.38.0

func NewOpaqueTokenGenerator(store security.SessionStore, policy security.SessionPolicy, notifier *security.SessionRevocationNotifier) *OpaqueTokenGenerator

func (*OpaqueTokenGenerator) Generate added in v0.38.0

type PasswordAuthenticator

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

PasswordAuthenticator verifies username/password credentials against a UserLoader. When a PasswordDecryptor is configured, the transmitted credential is treated as transport-encrypted (e.g. RSA-encrypted by the client before transmission) and decrypted to plaintext before verification; the password Encoder stays a plain KDF used identically for storage and comparison, so registration and reset flows that hash a server-side plaintext are unaffected.

func (*PasswordAuthenticator) Authenticate

func (p *PasswordAuthenticator) Authenticate(ctx context.Context, authentication security.Authentication) (*security.Principal, error)

func (*PasswordAuthenticator) Supports

func (*PasswordAuthenticator) Supports(authType string) bool

type RBACDataPermissionResolver

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

RBACDataPermissionResolver implements role-based data permission resolution.

func (*RBACDataPermissionResolver) ResolveDataScope

func (r *RBACDataPermissionResolver) ResolveDataScope(
	ctx context.Context,
	principal *security.Principal,
	permission string,
) (security.DataScope, error)

ResolveDataScope resolves the applicable DataScope for the given principal and permission token. When a user has multiple roles with the same permission token but different data scopes, the scope with the highest priority wins. Returns nil if no matching permission is found.

type RBACPermissionChecker

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

func (*RBACPermissionChecker) HasPermission

func (c *RBACPermissionChecker) HasPermission(
	ctx context.Context,
	principal *security.Principal,
	permissionToken string,
) (bool, error)

HasPermission uses sequential role loading rather than parallel to optimize for common case (1-3 roles).

type RefreshParams

type RefreshParams struct {
	api.P

	RefreshToken string `json:"refreshToken" validate:"required" label_i18n:"auth_refresh_token"`
}

RefreshParams represents the request parameters for token refresh operation.

type ResolveChallengeParams

type ResolveChallengeParams struct {
	api.P

	ChallengeToken string `json:"challengeToken" validate:"required" label_i18n:"auth_challenge_token"`
	Type           string `json:"type" validate:"required" label_i18n:"auth_challenge_type"`
	Response       any    `json:"response" validate:"required" label_i18n:"auth_challenge_response"`
}

ResolveChallengeParams represents the request for resolving a login challenge.

type SignatureAuthenticator

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

SignatureAuthenticator validates HMAC-based signatures for external app authentication.

func (*SignatureAuthenticator) Authenticate

func (a *SignatureAuthenticator) Authenticate(ctx context.Context, authentication security.Authentication) (*security.Principal, error)

func (*SignatureAuthenticator) Supports

func (*SignatureAuthenticator) Supports(authType string) bool

type TrustCodeAuthenticator added in v0.49.0

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

TrustCodeAuthenticator redeems the one-time code the trust-login gateway handed to the browser, completing the second leg of the handoff.

It is an ordinary Authenticator on purpose: redeeming the code is the only thing specific to trust login, and routing it through security/auth.login means the handoff inherits the whole pipeline — brute-force guard, the full challenge chain (a forced password change or department selection still runs; the external system authenticated the user, it did not satisfy the application's own login policy), token issuance under the configured mechanism, session concurrency, and the login audit event.

The authentication identifier is the app ID that initiated the handoff, checked here against the one the gateway recorded — so what reaches the lockout counter and the audit trail is a value the framework verified, not one the client asserted.

func NewTrustCodeAuthenticator added in v0.49.0

func NewTrustCodeAuthenticator(store security.TrustCodeStore, cfg config.TrustLoginConfig) *TrustCodeAuthenticator

NewTrustCodeAuthenticator creates the trust-login code authenticator.

func (*TrustCodeAuthenticator) Authenticate added in v0.49.0

func (a *TrustCodeAuthenticator) Authenticate(ctx context.Context, authentication security.Authentication) (*security.Principal, error)

func (*TrustCodeAuthenticator) Supports added in v0.49.0

func (*TrustCodeAuthenticator) Supports(authType string) bool

type TrustLoginMiddleware added in v0.49.0

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

TrustLoginMiddleware mounts the trust-login gateway: the browser-facing route an external system links to, carrying a signed user identifier, which trades the handoff for a one-time code the SPA then redeems through the ordinary login endpoint.

Two legs rather than one is what keeps the signed URL harmless. The signature never reaches the browser's own code, an invalid one is refused at the gateway where a person can still be shown why, and the URL that does land in browser history carries only a code that is single-use and expires in seconds.

func (*TrustLoginMiddleware) Apply added in v0.49.0

func (m *TrustLoginMiddleware) Apply(router fiber.Router)

func (*TrustLoginMiddleware) Name added in v0.49.0

func (*TrustLoginMiddleware) Name() string

func (*TrustLoginMiddleware) Order added in v0.49.0

func (*TrustLoginMiddleware) Order() int

Order places the gateway alongside the framework's other real routes, after the API engine and before the SPA fallback.

type TrustLoginMiddlewareParams added in v0.49.0

type TrustLoginMiddlewareParams struct {
	fx.In

	Apps     security.ExternalAppLoader `optional:"true"`
	Resolver security.TrustUserResolver `optional:"true"`
	Users    security.UserLoader        `optional:"true"`
	Codes    security.TrustCodeStore
	Nonces   security.NonceStore `optional:"true"`
	Security *config.SecurityConfig
}

TrustLoginMiddlewareParams contains dependencies for the trust-login gateway.

Jump to

Keyboard shortcuts

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