Skip to content

About

Authentication and authorization library for Go: password, magic link, OAuth 2.1/OIDC, passkeys (WebAuthn), TOTP, SAML, SCIM, API tokens, device login, agent identity and MCP authorization. SQLite, Postgres, MySQL.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

6 stars

Watchers

0 watching

Forks

Latest commit

 

History

392 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

theAuth

theauth-go

Go authentication library: OAuth 2.1 authorization server, MCP authorization, passkeys, SAML, SCIM and DPoP.
Mount it into your own net/http, chi, Gin, Echo or Fiber server. Your data stays in your own database (memory, SQLite, Postgres or MySQL). Self-hosted, MIT licensed, no hosted service.

Go Reference Go version Latest release CI CodeQL Go Report Card Coverage OpenSSF Scorecard SLSA 3 License: MIT Discord Context7

Website · Quickstart · Docs · pkg.go.dev · Examples · Discussions · Discord

theAuth for Go: authentication for Go. OAuth 2.1, OpenID Connect, MCP, passkeys, SAML. Self-hosted, MIT.


What it is

theauth-go is the Go library for theAuth. It handles sign-in for people (passwords, magic links, social login, passkeys, TOTP, SAML SSO, SCIM provisioning) and for software (an OAuth 2.1 authorization server with PKCE, DPoP, device login, token exchange and agent identities, plus a resource server module for MCP).

It runs inside your process as an http.Handler. You keep your users, sessions and tokens in your own database. The root module is Go 1.25, MIT licensed, and every release is signed and ships an SBOM. The TypeScript library is glincker/theauth.

Contents

Install

go get github.com/glincker/theauth-go/v2
go get github.com/glincker/theauth-go/storage/sqlite   # optional, embedded SQLite backend

The module path ends in /v2. The first resolvable v2 tag is v2.6.0, and the old path without /v2 is frozen at v1.0.0. See Migrating to /v2.

Verify releases

Release assets are signed keyless with cosign through GitHub Actions OIDC. Download checksums.txt, checksums.txt.sig and checksums.txt.cert from the release, then verify (replace v2.8.0 with your tag):

cosign verify-blob \
  --certificate-identity "https://github.com/glincker/theauth-go/.github/workflows/release.yml@refs/tags/v2.8.0" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --signature checksums.txt.sig --certificate checksums.txt.cert checksums.txt
sha256sum --ignore-missing -c checksums.txt

Each theauth-go-v*.tar.gz and its .sbom.json also ship with .sig and .cert files that verify the same way.

Packages

One Go module at the root plus three nested modules (mcpresource, storage/sqlite, audit/sinks/otlp) that carry their own dependencies. Everything else is a package inside the root module. Full index on pkg.go.dev.

Group Packages
Core theauth-go/v2 (server, sessions, OAuth 2.1 authorization server, agent identity, MCP authorization), admin, crypto, email, policy, clientauth (device login for Go CLIs), otp, openapi, mcpresource (separate module)
Storage storage, storage/memory, storage/postgres, storage/mysql, storage/sqlite (separate module), storagetest
Audit sinks audit/sinks/splunkhec, audit/sinks/webhook, audit/sinks/otlp (separate module)
Gin adapter adapters/gin (separate module): native Authn, RequireAuth, RequireAbility, accessors and Mount
Echo adapter adapters/echo (separate module): the same set for Echo v4
Fiber adapter adapters/fiber (separate module): the same set for Fiber v2 through its net/http adaptor (responses are buffered)
Providers provider/ apple, bitbucket, discord, facebook, github, gitlab, google, linkedin, microsoft, slack, twitch, x, oidc, and generic for 16 more (Spotify, Dropbox, Zoom, Kakao, Naver, Patreon, Box, Salesforce, Figma, Codeberg, Linear, PayPal, Kick, Hugging Face, Vercel, Yandex)
Commands theauth-go (secrets, OpenAPI), theauth-doctor (security doctor), theauth-migrate (Auth0 and Cognito import)

Modules on pkg.go.dev

Module Go Reference
github.com/glincker/theauth-go/v2 1.25 pkg.go.dev
github.com/glincker/theauth-go/mcpresource 1.25 pkg.go.dev
github.com/glincker/theauth-go/storage/sqlite 1.26 pkg.go.dev
github.com/glincker/theauth-go/adapters/gin 1.26 pkg.go.dev
github.com/glincker/theauth-go/adapters/echo 1.26 pkg.go.dev
github.com/glincker/theauth-go/adapters/fiber 1.26 pkg.go.dev

The TypeScript packages are listed in the theAuth README.

Quick start: net/http and SQLite

A complete server with sign-up, sign-in, magic links, sessions and one protected route, stored in a single SQLite file. Errors are checked; paste it into a main package. The storage/sqlite module needs Go 1.26.

package main

import (
	"context"
	"database/sql"
	"log"
	"net/http"

	"github.com/glincker/theauth-go/v2"
	sqlitestore "github.com/glincker/theauth-go/storage/sqlite"
	_ "modernc.org/sqlite"
)

func main() {
	ctx := context.Background()
	db, err := sql.Open("sqlite", "file:app.db?_pragma=foreign_keys(1)&_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_txlock=immediate")
	if err != nil {
		log.Fatal(err)
	}
	if err := sqlitestore.Migrate(ctx, db); err != nil {
		log.Fatal(err)
	}
	store, err := sqlitestore.New(db)
	if err != nil {
		log.Fatal(err)
	}

	a, err := theauth.New(theauth.Config{CoreStorage: store, BaseURL: "http://localhost:8080"})
	if err != nil {
		log.Fatal(err)
	}
	defer a.Close()

	mux := http.NewServeMux()
	mux.Handle("/auth/", a.Handler()) // sign-up, sign-in, magic link, sessions
	mux.Handle("GET /me", a.RequireAuth()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		user, _ := theauth.UserFromContext(r.Context())
		w.Write([]byte("hello " + user.Email))
	})))
	log.Fatal(http.ListenAndServe(":8080", mux))
}

For Postgres, chi or a runnable CLI with API tokens and device login, see examples/single-binary-sqlite and the Quick Start.

Mounting: a.Handler() returns a plain http.Handler for net/http's ServeMux and keeps the routes at their configured paths (/auth/..., /oauth/...), so no http.StripPrefix is needed. With chi, call a.Mount(r) on your router instead. Both register the same routes.

Quick start: agent identity and delegation

Give an agent its own credential, then let it act for a user with a narrower scope. This needs Config.AuthorizationServer (which requires a 32 byte EncryptionKey) and Config.AgentIdentity, on a storage backend with agent identity support (memory, SQLite, Postgres or MySQL). RegisterAgent creates the agent and, because Resource is set, a delegation grant from the user to the agent in one call.

package main

import (
	"context"
	"log"

	"github.com/glincker/theauth-go/v2"
	"github.com/glincker/theauth-go/v2/storage/memory"
)

func main() {
	ctx := context.Background()
	a, err := theauth.New(theauth.Config{
		Storage:       memory.New(),
		BaseURL:       "https://as.example.com",
		EncryptionKey: []byte("0123456789abcdef0123456789abcdef"), // 32 bytes, load from your secrets manager
		AuthorizationServer: &theauth.AuthorizationServerConfig{
			Issuer: "https://as.example.com",
			Resources: []theauth.ProtectedResource{
				{Identifier: "https://api.example.com", Scopes: []string{"read", "write"}},
			},
		},
		AgentIdentity: &theauth.AgentConfig{},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer a.Close()

	// The human who owns the agent. In a real app this is a signed-in user.
	user, err := a.UserByID(ctx, ownerID)
	if err != nil {
		log.Fatal(err)
	}

	// 1. Create the agent and a delegation grant for "read" on one resource.
	reg, err := a.RegisterAgent(ctx, theauth.RegisterAgentInput{
		OwnerID:  user.ID,
		Name:     "report-bot",
		Scope:    []string{"read"},
		Resource: "https://api.example.com",
	})
	if err != nil {
		log.Fatal(err)
	}
	// reg.Secret.ClientID and reg.Secret.Secret are the agent's credential.
	// The secret is shown once and only its Argon2id hash is stored.

	// 2. The agent authenticates as itself (client credentials).
	self, err := a.ClientCredentialsToken(ctx, theauth.TokenRequest{
		GrantType:    "client_credentials",
		ClientID:     reg.Secret.ClientID,
		ClientSecret: reg.Secret.Secret,
		Resource:     "https://api.example.com",
		Scope:        []string{"read"},
	})
	if err != nil {
		log.Fatal(err)
	}
	_ = self.AccessToken // sub is "agent:<id>"

	// 3. The agent trades a user's access token for a delegated one
	// (RFC 8693). The result carries the user as sub and the agent in act.
	delegated, err := a.ExchangeToken(ctx, theauth.TokenExchangeRequest{
		ClientID:         reg.Secret.ClientID,
		ClientSecret:     reg.Secret.Secret,
		SubjectToken:     userAccessToken, // issued to the user by this authorization server
		SubjectTokenType: "urn:ietf:params:oauth:token-type:access_token",
		Resource:         "https://api.example.com",
		Scope:            []string{"read"},
	})
	if err != nil {
		log.Fatal(err)
	}
	log.Println("delegated token expires in", delegated.ExpiresIn, "seconds")
}

ownerID and userAccessToken stand in for values from your sign-in flow (the user's ULID and an access token the authorization server issued to that user). Revoke access at any point with RevokeDelegation, SuspendAgent or RevokeAgent; resource servers see the change on their next introspection. Details: Agent identity and revocation, Agent delegation and Authorization server.

What you can build

Add sign-in to a Go web app

Email and password, magic links, 12 OAuth login providers, generic OIDC and 16 more catalog providers, with server-side sessions. Start with the net/http quick start or the Gin, Echo and Fiber adapters. Guide: Add an OAuth provider.

Run an OAuth 2.1 authorization server in Go

Authorization code with PKCE, refresh rotation, client_credentials, token exchange, PAR, JAR, CIBA and DPoP, with dynamic client registration and client ID metadata documents. Guide: Authorization server.

Secure an MCP server

Use theauth-go as the authorization server for MCP clients, and the separate mcpresource module to validate tokens and guard tools in your MCP resource server. Example: examples/mcp-server. Guides: MCP authorization, Protect MCP tools.

Passkeys and WebAuthn

Discoverable passkey login and TOTP with recovery codes, plus step-up checks with RequireRecentAuth. Example: examples/webauthn-passkey. Guide: Enable passkeys.

Device login for Go CLIs

RFC 8628 device authorization on the server and the clientauth package in your CLI. Example: examples/cli-login. Guide: CLI login.

SAML SSO and SCIM provisioning

SAML 2.0 single sign-on, SCIM 2.0 user provisioning, organizations, RBAC and an append-only audit log with Splunk, webhook and OTLP sinks. Needs memory, SQLite, Postgres or MySQL storage. Guide: Splunk audit streaming.

Create a user for an existing identity

CreateUser adds a user row without sending email or creating a credential or session. It is idempotent on the normalized email, so calling it on every login of an external identity provider is safe, even concurrently. Typed errors match ErrUserInvalidInput, ErrUserEmailConflict and ErrUserIDConflict with errors.Is. Details: docs/create-user.md.

u, err := a.CreateUser(ctx, theauth.CreateUserInput{Email: "ada@example.com", Name: "Ada"})

Agent identity and delegation

Give an AI agent its own credential and let it act for a user with a narrower scope. See the agent quick start.

Features

Area What you get Docs
Sign-in Email and password (Argon2id, 12 character minimum), magic links, 12 OAuth login providers, generic OIDC Add an OAuth provider
Second factors WebAuthn passkeys with discoverable login, TOTP with recovery codes, step-up with RequireRecentAuth Enable passkeys
Sessions Opaque server-side sessions, session list and revoke, idle timeout Configuration
API tokens and CLIs Scoped tokens with RequireAbility, RFC 8628 device login via the clientauth package API tokens, CLI login
OAuth 2.1 server Authorization code with PKCE, refresh rotation, client_credentials, token exchange, CIBA, PAR, JAR, DPoP OAuth 2.1 primer, Authorization server
Token policy, RAR and ID-JAG Per-client signing algorithm (EdDSA, ES256, RS256) and opaque access tokens, RFC 9396 authorization_details end to end, and ID-JAG issue and redeem over token exchange and jwt-bearer Token policy, RAR, ID-JAG
OpenID Provider ID tokens with nonce and at_hash, userinfo, RP-initiated and back-channel logout, pairwise subjects, OpenID discovery fields OpenID Provider
Multi-replica and abuse controls Pluggable rate limiter, DPoP replay cache and CIMD cache (memory, SQL, Redis or your own), per-IP and per-client limits and an Argon2id concurrency cap on the OAuth endpoints Pluggable stores, Endpoint limits, Request admission
Browserless OAuth RFC 8628 device authorization grant on the authorization server with a themable verification page, plus scoped one-time registration tokens for agents Device grant, Registration tokens
One-time codes Email and SMS OTP with pluggable senders (Twilio built in), resend cooldown, attempt limit, lockout, constant-time compare OTP
More sign-in providers Table-driven catalog of 16 more OAuth providers, custom specs, and OIDC discovery overrides Provider catalog
Developer CLI and OpenAPI theauth-go secret and theauth-go openapi, plus openapi.Generate for your own router CLI
MCP and agents MCP authorization (protected resource metadata, CIMD), agent identities with delegation, mcpresource SDK module (scope and step-up guards, tool security metadata) MCP authorization, Resource server, Protect MCP tools
Enterprise SAML 2.0 SSO, SCIM 2.0, organizations, RBAC, append-only audit log with SIEM sinks Splunk audit streaming, Threat model
Policy Authorization policy engine Policy engine
Observability OpenTelemetry spans, Prometheus metrics OpenTelemetry tracing, Metrics
Supply chain SBOM and Sigstore signed release artifacts, SLSA 3 provenance Releases and verification

Running more than one replica

Rate limits, DPoP replay records and the CIMD cache are per process by default. Share them through the database you already have:

db := stdlib.OpenDBFromPool(pool) // pgx stdlib
kvStore, _ := sqlkv.New(db, sqlkv.Postgres)
_ = kvStore.EnsureSchema(ctx)

auth, _ := theauth.New(theauth.Config{
    // ...
    Stores: kv.FromCache(kvStore), // or kv/redis, or your own kv.RateLimiter / kv.ReplayCache / kv.Cache
})

Details and the Redis adapter are in docs/pluggable-stores.md.

Choosing between a library and a hosted service

theauth-go is a library you embed and run yourself, so you own the database and the operations. It does not include a hosted login UI or a managed service. If you need either, it is not the right shape for you. The TypeScript library at glincker/theauth covers Node, Bun, Deno and Workers backends.

Storage backends

Persistence is split into small capability interfaces, and New fails at startup if a feature you enabled has no matching capability. Full matrix: Capability interfaces.

Capability memory sqlite postgres mysql
Users, sessions, magic links, passwords Yes Yes Yes Yes
OAuth accounts, passkeys, TOTP, audit log Yes Yes Yes Yes
Scoped API tokens, device grant, session management Yes Yes Yes Yes
Organizations, SAML, SCIM, RBAC Yes Yes Yes Yes
OAuth 2.1 authorization server, agent identity Yes Yes Yes Yes
CIBA backchannel auth Yes Yes Yes No
OAuth device grant (RFC 8628) and registration tokens Yes Yes Yes Yes
Durable JWT-bearer jti replay Yes Yes No No

Postgres and MySQL implement the newer token, device, session and throttle capabilities and pass the new storagetest suites against live PostgreSQL 16 and MySQL 8. The full shared contract passes on Postgres when you opt in with THEAUTH_PG_CONTRACT=1 and a DSN in THEAUTH_TEST_PG_DSN (verified on PostgreSQL 14), but that gate is not enabled in CI yet. The MySQL gate is still off, so treat MySQL support for the newer features as newly added. You can write your own backend and verify it with the public storagetest suite.

Stability

Packages and APIs that were Stable before v2.6 keep their SemVer guarantees. Among the additions in v2.6, only the storage capability split, Handler() and Config.PathPrefix are Stable. Experimental: storage/sqlite, clientauth, policy, the agent identity and revocation APIs, Doctor, Config.ProviderResolver and the new optional storage capabilities. Experimental APIs may change in a minor release. The SemVer rules and the list of stable packages are in STABILITY.md.

Security

  • Passwords are hashed with Argon2id, sessions are opaque and stored server side, and agent secrets are shown once and stored only as hashes.
  • Release assets are signed with cosign, ship an SBOM and carry SLSA 3 provenance. See Verify releases.
  • CI runs CodeQL, govulncheck, dependency review and OpenSSF Scorecard.
  • The threat model and the theauth-doctor security doctor describe what is covered and how to check a deployment.
  • Report vulnerabilities privately through GitHub advisories or support@glinr.com, as described in the security policy. Do not open a public issue.

Migrating

FAQ

Does theauth-go work without Postgres? Yes. Use storage/sqlite for a single static binary with one database file, storage/memory for tests, or MySQL. SQLite supports organizations, SAML, SCIM and RBAC, and runs the OAuth 2.1 authorization server, agents and CIBA (migrations 0020 to 0034); see docs/storage-capabilities.md.

How do I add passkeys to a Go app? Set Config.WebAuthn and use a storage backend that implements WebAuthnStorage (all four built in backends do). Register and login routes are then mounted under your auth handler. See the passkeys guide and examples/webauthn-passkey.

Does it support MCP authorization? Yes. The library can act as the OAuth 2.1 authorization server and publishes protected resource metadata. The separate zero dependency mcpresource module validates tokens inside an MCP resource server. See MCP authorization and examples/mcp-server.

How do I migrate from Auth0, Cognito or Keycloak? Follow Migrate from Auth0, Migrate from Cognito or Migrate from Keycloak. Password hashes carry over and upgrade to Argon2id at each user's first login. Coming from hand-rolled sessions: Migrate from hand-rolled sessions.

Which Go versions are supported? The root module declares Go 1.25. storage/sqlite is a separate module and needs Go 1.26.

Is it a hosted service? No. It is a library you run inside your own process, under the MIT license.

Examples

Runnable apps live in examples/, each with its own go.mod and a short README. More in the example apps guide.

Area Examples
Frameworks single-binary-sqlite, chi-app, gin-app, echo-app, stdlib-app
OAuth providers oauth-multi-provider, oauth-apple, oauth-bitbucket, oauth-facebook, oauth-gitlab, oauth-linkedin, oauth-slack, oauth-twitch, oauth-x
Second factors webauthn-passkey, totp-stepup
Agents and CLIs mcp-server, cli-login
Operations doctor-admin, observability-otel, observability-prom

Links

Made by GLINCKER, a GLINR STUDIOS company

theAuth is built and maintained in the open by GLINCKER, the open-source division of GLINR STUDIOS. Founder: thegdsks.com. The TypeScript library is glincker/theauth.

Partner project What it is Site Source
LevelRail Self-hosted deployment platform: push to git, get a running app. levelrail.com glincker/levelrail
theSVG Open-source brand SVG icons. thesvg.org glincker/thesvg
GLINUI Open-source liquid glass UI components for React. theauth.dev is designed with it. glinui.com glincker/glinui

About

Authentication and authorization library for Go: password, magic link, OAuth 2.1/OIDC, passkeys (WebAuthn), TOTP, SAML, SCIM, API tokens, device login, agent identity and MCP authorization. SQLite, Postgres, MySQL.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages