Repository navigation
v1.1.0
Security hardening from audit run 2 and impersonation as a library construct. Some changes break code or configuration that relied on the old behaviour; each is listed below and in docs/migration.md under "After v1.0.0". Decisions D42-D60 in docs/design.md.
New: impersonation (session.Impersonator, D60)
session.NewImpersonator(&session.ImpersonatorOptions{Guard, Load, ...})is the only way to start or stop an impersonation:POST /auth/impersonation {"subject"}andDELETE /auth/impersonationbehind the Guard, orStart/Stopin your own handlers.Actor(defaultauthorization.IsAdmin()) decides who may impersonate,Target(defaultNot(IsAdmin())) who may be impersonated. Always enforced: no nesting, active impersonator and target, no self-impersonation, a swap against the identity the Guard read.- An impersonation ends when stopped, with the session, or after
MaximumDuration(default 1 h); the next request runs as the impersonator again. - JWTs of an impersonating identity carry the impersonator as the RFC 8693
actclaim;Verifyreturns it asIdentity.Impersonator(without roles). - New client codes
already_impersonating,not_impersonating,unknown_impersonation_target,self_impersonation; outcomesimpersonation_start,impersonation_stop; wide-event keysauth.impersonation.*andauth.impersonated_subject.
Breaking changes
- session:
SaveIdentityandSwapIdentityreturn an error for an identity or impersonator of another provider or subject than the session holds, and a login cannot save an impersonating identity. Replace code that setIdentity.Impersonatoritself withsession.Impersonator; impersonations stored the old way end at their next request (D60). - jwt:
Signrefuses an identity whose impersonator is inactive;authorization.Real(...)on a verified token now sees theactimpersonator (D60).VerifierOptions.Audienceis required (D54).JWKSURLmust behttps(plainhttpon loopback only) unlessInsecureJWKSURL: true(D54). NewMaximumKeyAge(default 24 h): keys are no longer trusted forever while the JWKS is unreachable (D55). - oidc:
Issuer: oidc.GoogleIssuerneedsAllowedEmailDomains,OnLoginorAllowAnyAccount: true; with Google, a non-consumer allowed domain also requires thehdclaim, so only Workspace accounts of that domain pass (D56). - password: sessions are bound to a credential stamp; a password change or TOTP enable/disable ends the user's other sessions, and every password user logs in once more after the upgrade (D45).
- session:
ParseKeyandNewStorerefuse an encryption key that repeats one byte (all zero included) (D44);SaveIdentityandSwapIdentityno longer reset the Guard's refresh clock (D44). - authorization: every check except
IsAuthenticatedrefuses an identity whose impersonator is inactive (D47). - forwardauth image:
JWT_AUDIENCErequired withJWT_ENABLED=true(D54); Google needsOIDC_ALLOWED_EMAIL_DOMAINS, an enforcedKEYCLOAK_REQUIRED_*list orOIDC_ALLOW_ANY_ACCOUNT=true(D56); percent-encoded route variants answer 404 and/auth/merefuses inactive identities (D50); the checks refuse an identity whose impersonator is inactive (D60);request.client_ipis the peer address unlessTRUSTED_PROXIESlists it (D51); a malformedSESSION_REDIS_ADDRESSfails validation at boot.
Security fixes (audit run 2)
- Memory backend evicts through an expiry heap (O(log n)), so login floods no longer stall every request (D42).
- Redis backend: an evicted session index reads as logged out, so a logout always takes effect (D43).
- Guard: application saves no longer postpone the refresh; a failed refresh claim calls the provider only when it committed (D44).
- OIDC access-token roles only for the same
sub; raw Keycloak role claims are not copied into JWTs (D48); logout work bounded at 10 s; the Prometheus adapter never panics on invalid label values (D49). - GitHub OAuth tokens are revoked whenever the Guard or the provider ends a session (
session.Revoker, D57).
Forward-auth image
- Redis Sentinel (
SESSION_REDIS_SENTINEL_*), TLS including mTLS (SESSION_REDIS_TLS_*) and an ACL username (SESSION_REDIS_USERNAME) (D52, D53). TRUSTED_PROXIES(default none) decides whoseX-Forwarded-Forthe logged client IP trusts (D51).- Published only after lint, tests, e2e and vulncheck passed, with a signed build provenance attestation;
:latest,:1and:1.1move only for the newest stable tag (D59).
Image: ghcr.io/govengers/forwardauth:1.1.0.