go-idento

module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: Apache-2.0

README

go-idento

Go Reference Go Report Card CI License

A complete, batteries-included identity framework for Go — user/role management, password hashing, claims, lockout, two-factor and JWT, built on pluggable stores so the persistence layer can be swapped without touching business logic.

Status: beta (v0.6.0). User/role/sign-in managers, PBKDF2 hashing, JWT (HS256/RS256/ES256) + JWKS, TOTP/SMS two-factor with recovery codes, external logins, email-confirmation/password-reset tokens, and policy authorization are implemented and tested. Four stores ship: in-memory, GORM, raw pgx, and a sqlc-generated pgx store — with configurable schema/table names and referential integrity (cascade delete + optimistic concurrency). The API may still change before v1.0.

Docs: getting started · architecture · extending the user & migrations · customizing the schema · contributor/agent guide

Why

Go has JWT libraries and full identity providers (Ory, ZITADEL), but no embedded, batteries-included identity toolkit you drop straight into your own app and own your data. go-idento fills that gap with familiar building blocks — UserManager / RoleManager / SignInManager over pluggable stores.

What you get

Area API
Entities identity.User / identity.Role / identity.Claim
Users identity.UserManager (create, password add/change/remove, email & phone change, roles, claims, lockout, 2FA)
Guests CreateAnonymous / ConvertToRegistered (preserves ID) / PurgeAnonymousUsers GC
Roles identity.RoleManager (CRUD + role claims, optimistic concurrency)
Queries users-by-role / users-by-claim, paged ListUsers
Sign-in identity.SignInManager (password, 2FA, external, remember-this-machine, security-stamp validation)
API keys identity.APIKeyManager — opaque M2M bearer credentials (create/verify/revoke/list, non-expiring, hash-stored)
Two-factor TOTP + SMS + recovery codes, pluggable TwoFactorTokenProvider
Tokens identity.TokenService (JWT access + refresh, HS256/RS256/ES256)
Sessions multi-session refresh via WithSessionStore — one token per device, per-session revoke, MaxSessions
Delivery identity.SMSSender / identity.EmailSender (provider-agnostic)
Persistence identity.UserStore / identity.RoleStore interfaces
Password hashing identity.PasswordHasher (PBKDF2, versioned format)
Config identity.Options (password / lockout / user / sign-in policy)
HTTP auth.RequireAuth / auth.RequireRole / auth.RequirePolicy

The default password hasher uses a versioned PBKDF2 format (0x01 marker, PRF/iterations/salt-length header, PBKDF2-HMAC-SHA256); the version byte lets parameters evolve while old hashes keep verifying.

Quick start

ctx := context.Background()
secret := []byte("change-me-32-byte-jwt-secret-min!")

db, _ := gorm.Open(sqlite.Open("app.db"), &gorm.Config{})
gormstore.Migrate(db)

users  := identity.NewUserManager(gormstore.NewUserStore(db), identity.DefaultOptions())
signIn := identity.NewSignInManager(users)
tokens := identity.NewTokenService(users,
    identity.DefaultTokenOptions(secret, "issuer", "audience"))

u := &identity.User{UserName: "jane", Email: "m@x.com"}
users.CreateWithPassword(ctx, u, "Abcdef1!")

res, signed := signIn.PasswordSignIn(ctx, "jane", "Abcdef1!", true)
if res.Succeeded {
    pair, _ := tokens.IssuePair(ctx, signed) // access + refresh token
}

Run the demo server:

cd stores/gormstore && go run ./examples/httpserver

Modules

The repo is split so the core stays dependency-light: importing github.com/terglos/go-idento pulls only golang-jwt, google/uuid and golang.org/x/crypto — no ORM or DB driver. Each heavy store is its own module you opt into:

Module Import Extra deps
core github.com/terglos/go-idento (+/auth, /stores/memstore) jwt, uuid, x/crypto
GORM store github.com/terglos/go-idento/stores/gormstore gorm, drivers
pgx store github.com/terglos/go-idento/stores/pgxstore pgx
sqlc store github.com/terglos/go-idento/stores/pgxsqlc pgx

Layout

identity/          core: entities, managers, hasher, options, JWT, signer/JWKS
auth/              HTTP middleware: Bearer + cookie, RequireAuth/RequireRole/RequirePolicy, JWKS
stores/memstore/   in-memory implementation (tests / prototyping) — in the core module
stores/gormstore/  [module] GORM (Postgres / MySQL / SQLite) + examples
stores/pgxstore/   [module] raw pgx (PostgreSQL) + demo
stores/pgxsqlc/    [module] sqlc-generated pgx (PostgreSQL)
demo/totp/         TOTP code helper (core only)

Extending the user

Four ways, smallest blast radius first (full analysis in docs/design/extending-user-and-migrations.md):

// Option D — custom typed columns on the user row.
type AppUser struct {
    identity.User          // embeds -> Base()/TableName() promoted
    TenantID string
}
db, _ := gorm.Open(sqlite.Open("app.db"), &gorm.Config{})
gormstore.MigrateOf[AppUser](db)
store := gormstore.NewUserStoreOf[AppUser](db)
um := identity.NewUserManagerOf[AppUser](store, identity.DefaultOptions())
um.CreateWithPassword(ctx, &AppUser{User: identity.User{UserName: "jane"}, TenantID: "acme"}, "Abcdef1!")
// Option C — schema-less JSON attributes (no custom type).
u.SetAttribute("tenant", "acme")           // persisted in the attributes column
// Option B — claims (flow into the JWT). Option A — your own 1:1 extension table.

See stores/gormstore/examples/genericuser (Option D) and stores/gormstore/examples/customfields (Options A & B).

Migrations

// No CLI: apply the canonical schema from Go.
migrations.ApplyPostgres(ctx, sqlDB)

For versioned, reviewable history use Atlas (atlas.hcl + migrations/): atlas migrate diff <name> --env local generates SQL from schema changes — the EF add-migration loop. goose / golang-migrate can run the same SQL.

Demos

  • stores/pgxstore/example/postgres — full flow against a real PostgreSQL via the raw pgx store: docker compose up -d && go run . then follow its README (register, login, JWT + cookie, refresh, role-gated route, TOTP 2FA).
  • stores/gormstore/examples/httpserver — minimal zero-setup server on SQLite: cd stores/gormstore && go run ./examples/httpserver.

Features

  • User/role management (UserManager, RoleManager, SignInManager)
  • PBKDF2 password hashing with a versioned wire format
  • JWT access + refresh tokens with security-stamp revocation
  • Multi-session refresh tokens (TokenService.WithSessionStore): one refresh session per device/browser (industry model — one row per token), per-session rotation + sliding TTL, RevokeSession / global Revoke / ListSessions, opportunistic GC, optional MaxSessions cap
  • HS256, RS256 and ES256 signing via pluggable Signer + RSAKeyring / ECDSAKeyring (kid rotation)
  • JWKS endpoint (auth.JWKSHandler) publishing RSA/EC public keys
  • TOTP two-factor (RFC 6238) + one-time recovery codes
  • Phone (SMS) two-factor via a pluggable SMSSender
  • Email confirmation, password-reset & change-email token providers
  • External/OAuth login association (AddLogin / FindByLogin / ExternalLoginSignIn)
  • Opaque API keys (machine-to-machine): non-expiring, revocable, hash-stored bearer credentials bound to a user — CreateAPIKey / VerifyAPIKey / RevokeAPIKey / ListAPIKeys, configurable prefix + hasher, ImportAPIKey for zero-reissue migration, and auth.WithAPIKeys middleware
  • Guest / anonymous identity: CreateAnonymous, ConvertToRegistered (promote in place, preserving the ID), and a PurgeAnonymousUsers GC sweep
  • Account lockout
  • Policy/claims authorization (auth.Policy, RequirePolicy) beyond roles
  • Stores: GORM (Postgres/MySQL/SQLite), raw pgx, sqlc-generated pgx, in-memory
  • Configurable schema: custom namespace / table prefix / table names per store (WithSchema/WithTablePrefix/WithTableNames) + ON DELETE CASCADE integrity (docs)
  • Extensible user: custom columns via the generic UserManagerOf[T] (embed identity.User), a JSON Attributes bag, an extension table, or claims
  • Migrations: zero-CLI migrations.ApplyPostgres, plus Atlas config for versioned, diff-generated migrations (goose/golang-migrate also supported)
Two-factor quick reference
key, _ := users.GetAuthenticatorKey(ctx, u)       // provision (show as QR via identity.AuthenticatorURI)
users.SetTwoFactorEnabled(ctx, u, true)
res, u := signIn.PasswordSignIn(ctx, name, pw, true)
if res.RequiresTwoFactor {
    res = signIn.TwoFactorAuthenticatorSignIn(ctx, u, totpCode)
}
codes, _ := users.GenerateRecoveryCodes(ctx, u, 10)
RS256 with key rotation
ring := identity.NewRSAKeyring("key-1", privKey)
tokens := identity.NewTokenService(users, identity.TokenOptions{
    Signer: ring, Issuer: "issuer", Audience: "api",
    AccessTokenTTL: 15*time.Minute, RefreshTokenTTL: 7*24*time.Hour,
})
ring.Add("key-2", newKey, true) // new tokens use key-2; key-1 tokens still verify
ring.Remove("key-1")            // retire once no live tokens reference it

ES256 is identical — use identity.NewECDSAKeyring(kid, ecdsaKey).

Guest / anonymous identity
guest, _ := users.CreateAnonymous(ctx)   // no email/password; IsAnonymous=true
pair, _  := tokens.IssuePair(ctx, guest) // issue a JWT like any user

// Later, promote in place — the ID (and any cart/claims keyed on it) is kept:
users.ConvertToRegistered(ctx, guest, "jane", "jane@x.com", "Abcdef1!")

// Reclaim abandoned guests from a cron/worker (cascades their satellite rows):
n, _ := users.PurgeAnonymousUsers(ctx, time.Now().Add(-24*time.Hour))
API keys (machine-to-machine)

Long-lived, opaque bearer credentials for callers that can't do interactive login or token rotation (POS terminals, payment partners, webhooks). Bound to a user, so roles/claims and the auth middleware apply unchanged.

keys := identity.NewAPIKeyManager(gormstore.NewAPIKeyStore(db), users).
    WithAPIKeyPrefix("myapp-")               // optional; default hasher is SHA-256

secret, key, _ := keys.CreateAPIKey(ctx, u, identity.APIKeyOptions{
    Name: "POS terminal", Scopes: []string{"pay:write"}, // ExpiresAt nil = never expires
})
// `secret` is shown ONCE — only its hash + display prefix (key.Prefix) are stored.

// Authenticate requests: a Bearer value that isn't a JWT is tried as an API key.
mux := auth.Middleware(tokens, cookies, auth.WithAPIKeys(keys)) // Authorization: Bearer <secret>

owner, k, err := keys.VerifyAPIKey(ctx, secret) // err == identity.ErrInvalidAPIKey vs store error
keys.RevokeAPIKey(ctx, key.ID)

Migrating from an existing key system? Point the hasher at your current function and ImportAPIKey(userID, name, prefix, keyHash, expiresAt, scopes...) your already-issued hashes — every key stays valid with zero reissue.

JWKS endpoint
ring := identity.NewRSAKeyring("key-1", privKey) // or NewECDSAKeyring
mux.Handle("/.well-known/jwks.json", auth.JWKSHandler(ring)) // publishes public keys
Phone (SMS) two-factor
users := identity.NewUserManager(store, opts).WithSMSSender(mySMSSender)
users.SendPhoneToken(ctx, u)                       // delivers a 6-digit code
res := signIn.TwoFactorPhoneSignIn(ctx, u, code)   // after PasswordSignIn returned RequiresTwoFactor

Roadmap

  • WebAuthn / passkeys
  • Backchannel logout / token introspection endpoint

License

Licensed under the Apache License 2.0 — permissive, with an explicit patent grant. See NOTICE for attribution. Built by Terglos.

Directories

Path Synopsis
Package auth provides HTTP middleware that turns a validated identity into a request-scoped principal, plus cookie-session helpers — the transport layer on top of the identity core.
Package auth provides HTTP middleware that turns a validated identity into a request-scoped principal, plus cookie-session helpers — the transport layer on top of the identity core.
demo
totp command
Command totp prints the current TOTP code for an authenticator shared key.
Command totp prints the current TOTP code for an authenticator shared key.
Package identity is a batteries-included identity framework for Go: user/role management, password hashing, claims, lockout, two-factor and token generation, built on pluggable stores so the persistence layer can be swapped without touching the business logic in the managers.
Package identity is a batteries-included identity framework for Go: user/role management, password hashing, claims, lockout, two-factor and token generation, built on pluggable stores so the persistence layer can be swapped without touching the business logic in the managers.
migrations
Package migrations exposes the canonical go-idento schema as an embedded asset, so applications can bootstrap the database with the standard library's database/sql — no ORM, no external migration CLI required.
Package migrations exposes the canonical go-idento schema as an embedded asset, so applications can bootstrap the database with the standard library's database/sql — no ORM, no external migration CLI required.
stores
memstore
Package memstore is an in-memory implementation of the identity stores.
Package memstore is an in-memory implementation of the identity stores.
gormstore module
pgxsqlc module
pgxstore module

Jump to

Keyboard shortcuts

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