Skip to content

Releases: glincker/theauth-go

v2.5.0-rc.1

23 Jun 04:27
4b9cfe6

Choose a tag to compare

v2.5.0-rc.1 Pre-release
Pre-release

Changelog

New features

  • : feat(hooks): Config.LifecycleHooks surface (OnSignup, OnSignin, ...) + UserByID (#76, #77) (#83) (@thegdsks)
  • : feat(hooks): wire OnSignup + OnSignin at OAuth callback (#76) (#86) (@thegdsks)
  • : feat(hooks): wire OnSignup + OnSignin at magic-link consume (#76) (#85) (@thegdsks)
  • : feat(tenancy): Config.Tenancy auto-provision personal org on signup (#77) (#87) (@thegdsks)

Bug fixes

  • : fix(middleware): RequireAuth emits RFC 7807 problem+json (#78) (#82) (@thegdsks)

Other changes

  • : chore(release): prepare v2.5.0-rc.1 (#88) (@thegdsks)
  • : ci: bench workflow to manual + weekly (was per-PR, ~15min burn) (#75) (@thegdsks)
  • : docs(readme): replace primitive ASCII with ANSI Shadow logo (#72) (@thegdsks)
  • : refactor(root): consolidate root files for cleaner repo home page (#71) (@thegdsks)

Verify this release

# Download the SBOM and its Sigstore signature from the release assets, then:
cosign verify-blob \
  --certificate-identity "https://github.com/glincker/theauth-go/.github/workflows/release.yml@refs/tags/v2.5.0-rc.1" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --signature theauth-go-v2.5.0-rc.1.tar.gz.sbom.json.sig \
  --certificate theauth-go-v2.5.0-rc.1.tar.gz.sbom.json.cert \
  theauth-go-v2.5.0-rc.1.tar.gz.sbom.json

Use this library: go get github.com/glincker/theauth-go@v2.5.0-rc.1

v2.4.0

23 Jun 00:01
4e760df

Choose a tag to compare

theauth-go v2.4.0

The enterprise security profile, supply chain hardening, and storage portability release.
v2.4 closes the FAPI 2.0 baseline by combining PAR (RFC 9126), JAR (RFC 9101), and
JWT-Bearer client authentication (RFC 7523). A new MySQL 8.x backend, public storagetest
contract suite, CIBA backchannel authentication (RFC 9509), and a Cognito/Auth0 CLI
migration tool round out the release. All additions are fully additive: downstream
code compiles unchanged.

Highlights

FAPI 2.0 baseline (PAR + JAR + JWT-Bearer)

PAR (#64) pushes the full authorization request to the AS over a back-channel before
the browser redirect, eliminating parameter exposure in browser history. JAR (#64) wraps
the parameters in a client-signed JWT, preventing tampering even over the back-channel.
JWT-Bearer client authentication (#65) replaces client_secret with a signed JWT assertion,
making client credentials tamper-evident and eliminating the need to distribute shared
secrets. Together these three features satisfy the FAPI 2.0 Security Profile baseline.

CIBA backchannel authentication (RFC 9509, #66)

Decouples the consumption device from the authentication device. IoT appliances, voice
assistants, call center terminals, and TV apps can now trigger authentication without a
browser redirect. The user approves on their phone. Supports Poll and Ping delivery modes.
Implement the AuthenticationDevice interface to connect any push notification system.

MySQL 8.x storage backend (#62)

Full parity with the postgres adapter. storage/mysql satisfies both theauth.Storage
and OAuthServerStorage. Enable contract testing with THEAUTH_MYSQL_CONTRACT=1. Use
on PlanetScale, AWS RDS MySQL, or any MySQL 8.x host.

Cognito + Auth0 migration CLI (#63)

cmd/theauth-migrate cognito and cmd/theauth-migrate auth0 export user data into an
auditable intermediate JSON bundle, validate it, and apply it to any theauth-go storage
backend. Auth0 users keep their bcrypt password hashes; theauth-go re-hashes with Argon2id
transparently on the next login (controlled by PasswordPolicy.AllowLegacyBcrypt).

Signed releases (goreleaser + cosign + SLSA, #57)

Every release artifact is now signed with Sigstore keyless signing via the GitHub Actions
OIDC identity and comes with a CycloneDX SBOM and SLSA level-3 provenance attestation.
Verify with:

cosign verify-blob \
  --certificate-identity "https://github.com/glincker/theauth-go/.github/workflows/release.yml@refs/tags/v2.4.0" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --signature theauth-go-v2.4.0.tar.gz.sig \
  --certificate theauth-go-v2.4.0.tar.gz.cert \
  theauth-go-v2.4.0.tar.gz

gh attestation verify theauth-go-v2.4.0.tar.gz --repo glincker/theauth-go

Public storagetest contract suite (#58)

storagetest.Run(t, func() theauth.Storage { return mystorage.New() })

Twelve functional areas. Both in-tree adapters run it in CI.

Upgrading from v2.3

All additions are opt-in via new nil-by-default config fields. Existing code compiles
and runs unchanged. See the v2.3 to v2.4 migration guide
for opt-in instructions.

go get github.com/glincker/theauth-go@v2.4.0
go mod tidy
go build ./...

Full changelog

See CHANGELOG.md for
the complete entry, including per-PR change details, RFC citations, and notes on the
storagetest contract coverage.

What is NOT changing

  • mcpresource module gains zero new transitive dependencies.
  • theauth.Storage and OAuthServerStorage interfaces are unchanged.
  • All existing config structs compile unchanged.
  • Public API is byte-stable.

v2.3.0 - MCP wedge deepening and enterprise feature parity

22 Jun 18:19
520da3c

Choose a tag to compare

The "MCP wedge deepening and enterprise feature parity" release. Three major features close the gap with Auth0, Clerk, and Better-Auth.

Public API is additive with v2.2.0. Root go.mod gains zero new transitive deps; mcpresource untouched.

Highlights

Account linking + identity merge (#55)

Users can bind a new auth method to an existing account, or merge two accounts into one, behind mandatory step-up auth. Closes a gap every major auth library has.

  • LinkOAuthToCurrentUser / LinkPasswordToCurrentUser / MergeAccounts
  • HTTP endpoints under /account/identities (link, callback, password, merge, delete)
  • New errors: ErrIdentityConflict, ErrStepUpRequired, ErrLastAuthMethod
  • New audit events: identity.linked, identity.unlinked, account.merged
  • 1,545 LOC, end-to-end tested

+8 OAuth providers, 12 total (#54)

Goth supports 35, Auth0 supports 50, Clerk supports 25. We just doubled.

  • Apple (ES256 JWT client secret from .p8 key)
  • Facebook, Slack, GitLab (self-hosted via BaseURL), Bitbucket, Twitch, LinkedIn, X (PKCE-mandatory)
  • Each ships with its own examples/oauth-<provider>/ minimal demo
  • 4,127 LOC across 36 files

SIEM audit streaming sinks (#53)

SIEM integration is a procurement requirement. We now ship three sink implementations with a stable AuditSink interface.

  • audit/sinks/otlp -- OTLP/HTTP logs (own go.mod; root gains zero new deps)
  • audit/sinks/splunkhec -- Splunk HEC envelope + token auth
  • audit/sinks/webhook -- generic CloudEvents 1.0 POST with HMAC-SHA256 signature
  • Per-sink WithRedactor option for stricter PII stripping
  • Best-effort contract: failed sinks NEVER block storage writes
  • New stats field Stats.AuditSinkFailed

See CHANGELOG.md for the full entry.

v2.2.0 — production-grade observability and audit closure

22 Jun 17:26
3ce6131

Choose a tag to compare

The "production-grade observability and audit closure" release. Three RFC-level features (CIMD, DPoP, observability adapters), four security closures, four perf items, +175 percentage points of direct handler coverage on two highest-blast-radius packages, and an architectural cleanup that brings theauth.go from 1,171 LOC under the 500 LOC ceiling.

Public API stays byte-stable with v2.1.0. Every addition is additive; downstream code compiles unchanged.

Highlights

Spec features

  • CIMD (#42) — OAuth Client ID Metadata Documents per MCP spec 2025-11-25. Clients identify by HTTPS URL; metadata cached with TTL; DenyAll trust policy default (fail-closed).
  • DPoP (#43) — RFC 9449 sender-constrained access tokens. Stolen tokens cannot be replayed without the holder's private key. Wire via Config.AuthorizationServer.DPoP and mcpresource.WithDPoPVerification(...). mcpresource gains zero new transitive deps.
  • Observability adapters (#44) — pluggable Tracer + Metrics interfaces with 10 spans + 10 metrics live. Real-dep example bridges to OTel and Prometheus live in examples/observability-{otel,prom}/ with their own go.mod; root and mcpresource go.mod gain zero new deps.

Security

  • Lows L1-L3 + L5 (#45) — magic-link rate limit, PKCE constant-time compare, RevokeToken walks refresh family, Config.RequireState knob.
  • Mediums M3-M5 (#48)SecureCookie deprecation warning ahead of v3.0 default flip, JWKS rotation transactional with partial unique index, mcpresource.Validator.Diagnostics() for misconfiguration warnings.
  • N1 regression guardTestSuspendAgentBustsClientAuthCache confirms revoked agents cannot re-authenticate via cached Argon2 entries within the 5-minute TTL.

Performance (#46)

  • SCIM auth uses a single storage lookup.
  • keyedLimiter uses sync.RWMutex plus atomic lastUsed (5-10x read-heavy throughput improvement under contention).
  • 5-second chain-walk cache eliminates 3+ storage calls per 3-deep chain.
  • Audit redactor uses precomputed lowercase key set with strings.EqualFold.

Tests

  • internal/saml/handlers: 0% to 91.4% statement coverage (#47).
  • internal/webauthn/handlers: 0% to 84.5% (#47).
  • internal/organizations/handlers: 0% to 94.0% (#49).
  • internal/admin/handlers: 0% to 81.1% (#49).

Architecture (#50)

  • theauth.go: 1,171 LOC to 383 LOC.
  • Wiring extracted to wiring.go, storage interface to storage.go, config sub-structs to config.go.
  • OAuth provider state machine moved to internal/oauth/service.go.
  • internal/account/handlers/ and internal/admin/handlers/ parent packages collapsed.

Fixes

  • InsertOAuthClient coerces nil text[] slices to empty arrays (#40), fixing SQLSTATE 23502 on fresh schemas.
  • postgres.Migrate(ctx, pool) runtime migration helper (#32) so downstream consumers can delete their forked migration code.

See CHANGELOG.md for the full entry.

v2.1.0: internal package reorg + security/perf/reliability hardening

22 Jun 01:50
e9b4b2a

Choose a tag to compare

Internal architecture reorganization plus the v2.0 security audit followups.
The public API is byte-stable with v2.0: every exported type, function,
and method on *theauth.TheAuth keeps the same identifier, signature, and
method set. Downstream consumers compile unchanged. Anyone relying on
unexported symbols via //go:linkname or unsafe reflection may break (we
deleted several dead unexported root methods and consolidated forwarders).

Internal package reorganization (PRs #20 through #28)

The 1.9k-line monolithic root grew to 49 non-test root files at v2.0; PRs
#20 through #28 extracted feature-by-feature implementations into
internal/<flow> subpackages while the root kept the public surface as
thin forwarders. PR G (this release) collapsed the remaining one-line
forwarders into four grouped files and removed the dead bridges that
earlier extractions left behind.

  • New internal packages added across the refactor: internal/models,
    internal/as, internal/as/handlers, internal/agent,
    internal/agent/handlers, internal/delegation, internal/session,
    internal/password, internal/password/handlers, internal/totp,
    internal/totp/handlers, internal/webauthn, internal/webauthn/handlers,
    internal/magiclink, internal/oauth/handlers, internal/saml,
    internal/saml/handlers, internal/scim, internal/scim/handlers,
    internal/organizations, internal/organizations/handlers,
    internal/rbac, internal/audit, internal/account/handlers,
    internal/admin/handlers, internal/clientauthcache,
    internal/jwt, internal/chain, internal/httpx, internal/wavt,
    internal/ulid, internal/bench, internal/samltest.
  • Root non-test .go file count: 49 (v2.0) to 28 (v2.1). Public surface
    unchanged.

Dead code purge (PR G)

PR G deleted seven unused unexported root methods that lost their last
in-tree caller during PRs B through F. Every one was forwarded to a
*as.Service or *<flow>.Service method that handler packages now reach
directly. None were on the public surface; consumers are unaffected.

  • (*TheAuth).currentSigningKey (was in jwks.go)
  • (*TheAuth).publicKeyByKID (was in jwks.go)
  • (*TheAuth).invalidateClientAuthCache (was in as.go)
  • (*TheAuth).agentBySubjectClaim (was in service_agent.go); the
    AgentLookup adapter wired in theauth.New now points at
    a.agentSvc.AgentBySubjectClaim directly.
  • (*TheAuth).authenticateClient (was in service_token.go)
  • (*TheAuth).finishRegistrationFromRequest (was in
    service_webauthn.go); every handler now wraps the body in
    http.MaxBytesReader itself.
  • (*TheAuth).finishLoginFromRequest (was in service_webauthn.go)

Forwarder consolidation (PR G)

Twenty-one service_*.go forwarder files became three grouped files plus
three substantive service files preserved as-is (because they still hold
local logic, not just one-line thunks).

  • forwarders_identity.go: session, magic-link, password, TOTP,
    WebAuthn, audit.
  • forwarders_oauth.go: DCR, introspect, revoke, token, token v3 / v4
    grants, AS metadata, protected-resource metadata, authorize.
  • forwarders_enterprise.go: SCIM, organizations, RBAC, delegation.
  • Retained: service_oauth.go (OAuth state cache + GC + provider flow),
    service_agent.go (validateAgentConfig plus the agent CRUD
    forwarders), service_saml.go (toInternal adapter plus SAML
    forwarders).

Twelve root handlers_*.go files became eight: the five thinnest mount
forwarders (oauth, password, totp, webauthn, oauth_server) collapsed
into mounts_extracted.go; the six handler files that carry substantive
service adapters (handlers_account.go, handlers_admin.go,
handlers_admin_agents.go, handlers_organizations.go,
handlers_saml.go, handlers_scim.go) plus the top-level handlers.go
stay separate.

errors_v20.go merged into errors.go; models_v20.go merged into
models.go.

Security (audit 2026-06-20, shipped in PR #17)

  • H1: POST /oauth/register Bearer gate now validates the supplied token
    against AuthorizationServerConfig.RegistrationTokens under
    crypto/subtle.ConstantTimeCompare against pre-hashed sha256 digests.
    The legacy "any non-empty bearer is accepted" behavior is gone. Empty
    and unknown bearers return 401 access_denied. When RegistrationTokens
    is empty and AllowAnonymousRegistration is false (the default and
    production-recommended state), all registration requests are denied.
  • H2: POST /oauth/register is now rate limited per source IP. The cap
    defaults to 1 req/min when AllowAnonymousRegistration is true (the
    documented public-MCP profile) and 5 req/min otherwise. Operators can
    override via the new
    AuthorizationServerConfig.RegistrationRateLimitPerMinute field;
    negative values disable the cap entirely.
  • H3: organization-scoped delegation admin (POST
    /admin/v1/organizations/{orgID}/delegations) now verifies that
    body.userId is a member of the calling admin's organization before
    creating the grant.
  • H4: X-Forwarded-For is no longer trusted by default. The new
    Config.TrustedProxies []netip.Prefix field gates XFF: the header is
    consulted only when the incoming r.RemoteAddr is inside one of the
    configured prefixes.
  • M2: AddOrganizationMember now refuses to demote the last owner of an
    organization.
  • M6: the email + password signin path pays the Argon2id verify cost on
    the user-not-found and password-empty branches by verifying against a
    fixed dummy PHC hash synthesized once at New time.

Reliability (PR #18)

  • Closed the handleToken / SCIM PATCH / end-to-end AS to mcpresource
    reliability gaps surfaced by the 2026-06 test audit.

Performance (PR #19)

  • clientauthcache interposes between the AS handlers and the Argon2id
    client_secret verifier. First verification pays the Argon2id cost; every
    subsequent verification of the same (client_id, secret_hash) pair is
    served from the cache until the secret rotates or the entry ages out.
  • JWKS key cache: the AES-decrypted Ed25519 private key is decrypted once
    per signing key version (rotation refreshes it) and stashed on
    *as.Service. Cold path is unchanged; hot path drops one AES decrypt
    per JWT mint.
  • mcpresource validator switched from sync.Mutex to sync.RWMutex on
    the JWKS + introspection caches so concurrent reads no longer serialize.

Removed in PR G

  • Seven unused unexported methods on *TheAuth (see "Dead code purge"
    above).
  • Twenty-one service_*.go files merged into three forwarders_*.go
    files (see "Forwarder consolidation").
  • Five handlers_*.go files merged into one mounts_extracted.go.
  • errors_v20.go merged into errors.go.
  • models_v20.go merged into models.go.

Deferred from the 2026-06-20 audit

Tracked as follow-up issues: M1 (SCIM cross-tenant email fallback), M3
(SecureCookie default), M4 (JWKS rotation transaction), M5 (mcpresource
missing-introspection startup warning), L1-L5, I1-I7.

[2.0.0] - 2026-06-20

v2.0 ships the OAuth 2.1 Authorization Server, agent identity and
delegation chains (RFC 8693), and the mcpresource validator SDK for MCP
servers. Three alpha tags shipped phases incrementally
(v2.0.0-alpha.1 through v2.0.0-alpha.3); v2.0.0 consolidated them.

Added in v2.0 phase 5 + 6 (targeted for v2.0.0)

  • New separately importable Go module: github.com/glincker/theauth-go/mcpresource.
    Zero dependencies outside the standard library: a consumer importing the
    package does not transitively pull theauth core or the storage adapters.
    Public surface: Validator, Principal, Option, New,
    PrincipalFromContext, WithJWKS, WithIntrospection, WithCacheTTL,
    WithHTTPClient, WithClockSkew, (*Validator).Middleware,
    (*Validator).Principal. Validates JWT signature against a cached JWKS
    (refreshes on kid miss and at the configured cache TTL), enforces the
    audience claim against the configured resource URI, checks expiry, nbf,
    and iat with a 60 second skew tolerance by default, and walks the RFC 8693
    act chain via the AS introspection endpoint so revocations propagate
    inside the configured cache window. On any failure the middleware emits
    HTTP 401 with WWW-Authenticate: Bearer error="invalid_token", resource_metadata="..." per RFC 6750 + RFC 9728.
  • RFC 9728 OAuth 2.0 Protected Resource Metadata: the AS exposes
    GET /.well-known/oauth-protected-resource (bare path returns the first
    configured resource) and GET /.well-known/oauth-protected-resource/{path}
    (per-resource discovery for multi-resource deployments). The document
    carries resource, authorization_servers, bearer_methods_supported,
    plus scopes_supported, resource_name, jwks_uri, and
    resource_signing_alg_values_supported.
  • Organization-scoped admin UX under /admin/v1/organizations/{orgID}:
    GET/POST/PATCH/DELETE /agents and
    GET/POST/DELETE /delegations. Gated by the new seeded permissions
    agents:admin and delegations:admin; both are added to the owner and
    admin default org roles.
  • End-user self-service UX under /account (enabled via
    Config.AccountUX): GET/POST /agents, DELETE /agents/{id},
    GET/POST /delegations, POST /delegations/{id}/revoke. Session-cookie
    gated; cross-user calls return 404 to avoid leaking record existence.
  • New seeded RBAC permissions: agents:admin, delegations:admin.
    Existing consumers see the additional permissions on the next
    SeedPermissions call; downstream role definitions that did not pre-grant
    them stay valid (catalog only grows).
  • New error sentinels: ErrAccountUXRequiresAgents.
  • Example examples/mcp-server/: standalone runnable demo of the
    mcpresource middleware on a tiny chi server. Shows the one-import claim:
    middleware wiring plus principal extraction in roughly ten lines of Go.
  • Audit emission additions: every admin and account mutation emits the same
    events used by th...
Read more