Help us shape the future of Go! Take the 2026 Go Developer Survey

authkit

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 18 Imported by: 0

README

authkit

Authkit is a Go authentication toolkit you configure once and mount as an HTTP handler. It includes email/password login, social sign-in, sessions, and optional two-factor, passkey, and organization features. The default PostgreSQL adapter creates and migrates its tables; the core services remain available for custom storage and frameworks.

Requires Go 1.25 or newer.

go get github.com/goat-io/authkit@v0.1.0

One-config setup

Set DATABASE_URL, provider credentials, and AUTHKIT_ED25519_SEED (a persistent, base64-encoded 32-byte Ed25519 seed for the two-factor challenge signer). The example runs at http://localhost:8080; use an HTTPS BaseURL in production.

package main

import (
    "context"
    "encoding/base64"
    "log"
    "net/http"
    "os"

    "github.com/goat-io/authkit"
    "github.com/goat-io/authkit/token/eddsa"
    "github.com/goat-io/authkit/webauthn/gowebauthn"
    "github.com/jackc/pgx/v5/pgxpool"
)

func main() {
    ctx := context.Background()
    pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
    if err != nil { log.Fatal(err) }
    defer pool.Close()

    seed, err := base64.StdEncoding.DecodeString(os.Getenv("AUTHKIT_ED25519_SEED"))
    if err != nil { log.Fatal(err) }
    signer, err := eddsa.New(seed)
    if err != nil { log.Fatal(err) }

    auth, err := authkit.New(ctx, authkit.Config{
        BaseURL: "http://localhost:8080",
        Storage: authkit.Postgres(pool),
        Signer: signer,
        EmailAndPassword: authkit.EmailAndPassword{Enabled: true},
        SocialProviders: map[string]authkit.SocialProvider{
            "google": {
                ClientID: os.Getenv("GOOGLE_CLIENT_ID"),
                ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
            },
            "github": {
                ClientID: os.Getenv("GITHUB_CLIENT_ID"),
                ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
            },
        },
        Plugins: []authkit.Plugin{
            authkit.TwoFactor(),
            authkit.Passkey(gowebauthn.Config{
                RPID: "localhost", RPDisplayName: "My App",
                RPOrigins: []string{"http://localhost:8080"},
            }),
            authkit.Organization(),
        },
    })
    if err != nil { log.Fatal(err) }

    mux := http.NewServeMux()
    mux.Handle("/api/auth/", auth.Handler())
    mux.Handle("GET /me", auth.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        principal, _ := authkit.PrincipalFrom(r.Context())
        if !principal.Authenticated() {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
            return
        }
        _, _ = w.Write([]byte(principal.Subject))
    })))
    log.Fatal(http.ListenAndServe(":8080", mux))
}

authkit.New validates the configuration, discovers configured OpenID providers, wires the services, and runs the PostgreSQL migration. Auth.Handler() mounts the selected routes. Auth.Middleware accepts its session cookie or a Bearer token and puts an identity.Principal in the request context; your application checks roles, scopes, organization membership, and step-up requirements.

Enabled feature Routes under /api/auth
Email/password POST /sign-up/email, POST /sign-in/email
Social providers GET /sign-in/social/{provider}, GET or POST /callback/{provider}, POST /link/social/{provider}
Organization SSO GET /sign-in/sso/{connection}, GET or POST /callback/{connection}, POST /link/sso/{connection}
Two-factor plugin POST /two-factor/enroll, POST /two-factor/confirm, POST /two-factor/complete
Passkey plugin POST /passkey/register/begin, /register/finish, /sign-in/begin, /sign-in/finish
Organization plugin POST /organization/create, GET /organization/list
Always GET /session, POST /sign-out

Email/password and passkey requests use JSON. The passkey begin routes return browser credential options; the finish routes accept the browser's raw credential response JSON. Social start and link routes redirect the browser to the provider. POST /link/social/{provider} requires an authenticated session and an Origin header matching BaseURL (a same-origin HTML form works). The callback sets an HTTP-only session cookie and redirects to BaseURL.

When two-factor is enabled and a user has enrolled TOTP, password, social, and passkey sign-ins return an MFA challenge instead of a session. Browser flows store that challenge in a short-lived HTTP-only cookie and redirect with ?authkit_mfa=required where needed. Submit {"code":"123456"} to POST /two-factor/complete; an API client can also pass the challenge in the JSON body. Only the completed challenge creates a session.

Social providers

Use each provider's name as a key in SocialProviders. Callback URLs default to BaseURL + /api/auth/callback/{provider}; set RedirectURL to override. Register the exact callback URL with the provider.

Provider Additional setup
Google OAuth web client ID and secret.
GitHub OAuth app client ID and secret. Authkit requests read:user and user:email and uses the stable GitHub user ID.
Microsoft Entra client ID and secret plus MicrosoftTenant set to a specific tenant GUID. Multi-tenant authorities are not supported yet.
LinkedIn Client ID and secret; enable Sign In with LinkedIn using OpenID Connect.
Apple App ID or Services ID as ClientID; supply ClientSecret or AppleTeamID, AppleKeyID, and a P-256 ApplePrivateKey. Web callback URLs must use HTTPS.

Google, Microsoft, LinkedIn, and Apple use verified OpenID Connect ID tokens. GitHub uses its OAuth code flow and authenticated user API. Authkit keys accounts by (provider, subject), never by email. An existing email produces identity.ErrEmailInUse until the signed-in user explicitly links that provider. PostgreSQL stores callback and passkey challenges as one-time, expiring flows so multiple server instances can share them.

For native iOS sign-in, verify a Google, Microsoft, or Apple ID token with auth.SignInWithIDToken(ctx, provider, rawToken, expectedNonce). Generate and retain a one-time nonce for that attempt. If the native app uses a different client ID from the web flow, add it to that provider's NativeClientIDs; the web flow keeps using ClientID. The result contains either Session or an MFA Challenge. LinkedIn and GitHub use the authorization-code flow.

Organization SSO (bring your own identity provider)

An organization can use its own OpenID Connect (OIDC) provider. Create the organization first, then register a connection at application startup:

OrganizationSSO: map[string]authkit.OIDCProvider{
    "acme": {
        OrgID:        "org_acme",
        IssuerURL:    "https://login.acme.example/realms/employees",
        ClientID:     os.Getenv("ACME_OIDC_CLIENT_ID"),
        ClientSecret: os.Getenv("ACME_OIDC_CLIENT_SECRET"),
    },
},

Register https://your-app.example/api/auth/callback/acme as the redirect URI at the identity provider, then direct users to GET /api/auth/sign-in/sso/acme. Authkit discovers the issuer's OIDC endpoints and signing keys, uses authorization code with PKCE, and checks the signed ID token's issuer, audience, expiry, and nonce. After successful verification, it adds the user to org_acme with the member role; an existing role is preserved. If the email belongs to an existing account, the user must sign in first and explicitly link it with POST /api/auth/link/sso/acme (same-origin request). The connection name, issuer, and organization ID isolate account identities, so reusing a name for another issuer or organization does not silently transfer accounts.

OrganizationSSO is currently configured by the application operator at startup; it is not yet a self-service organization-admin API. The issuer must be HTTPS and serve standard OIDC discovery. Plain OAuth 2.0 without OIDC identity tokens and SAML are separate integrations. For a self-service admin UI, keep client secrets encrypted and validate issuer URLs before allowing an organization to register a connection.

Lower-level packages

The configured handler is optional. identity defines the session, machine credential, MFA, organization, and storage interfaces. social exposes provider flows; webauthn/gowebauthn exposes passkey ceremonies; token/eddsa signs machine JWTs and MFA challenges. store/postgres supplies the schema and store implementations. transport/nethttp and transport/fiber remain available when you own the HTTP routes.

For machine clients, create identity.NewCredentialService(postgres.NewCredentials(pool), signer, ttl). Mint returns a durable secret once; Exchange trades it for a short-lived JWT. Revoking the secret stops future exchanges, while already-issued JWTs remain valid until expiry. The Authenticator resolves machine JWTs, user sessions, and an optional admin token to one Principal.

Releases

Go modules are distributed from Git tags; there is no separate package upload. CI runs module checks, vet, and the race-enabled test suite against PostgreSQL for pull requests, main, and version tags. After a version tag passes, CI creates a GitHub release. To publish the next version, merge the changes to main, then create and push an immutable semantic version tag such as v0.1.1. Consumers can pin a version with go get github.com/goat-io/authkit@v0.1.1.

The v0 series is for refining the public API. A future v1.0.0 will signal a stable compatibility commitment; breaking changes after that require a new major module path such as /v2.

Operational notes

  • Set an HTTPS BaseURL in production and keep provider secrets and the Ed25519 seed stable and private. The session cookie is HTTP-only and Secure when BaseURL uses HTTPS.
  • Add rate limits and email verification for public password sign-up. Authkit currently creates an account immediately from the submitted email.
  • Run postgres.NewSessions(pool).PurgeExpired(ctx) and postgres.NewFlows(pool).PurgeExpired(ctx) periodically. Expired records cannot authenticate or complete a flow, but periodic cleanup reclaims storage.
  • TOTP secrets are stored in plaintext by the PostgreSQL adapter. Protect database access and backups. Recovery codes and session tokens are stored as hashes.

Run the tests with go test ./.... PostgreSQL tests use AUTHKIT_TEST_DATABASE_URL when set and skip when their database is unreachable.

MIT license

Documentation

Overview

Package authkit wires authentication services from one application config. Mount Auth.Handler at Config.BasePath for the built-in net/http routes.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PrincipalFrom

func PrincipalFrom(ctx context.Context) (identity.Principal, bool)

Types

type Auth

type Auth struct {
	Sessions      *identity.SessionService
	Password      *identity.LoginService
	Social        *identity.SocialLoginService
	TwoFactor     *identity.MFAService
	Passkeys      *gowebauthn.Provider
	Organizations identity.OrgStore
	Authenticator *identity.Authenticator
	// contains filtered or unexported fields
}

Auth owns the configured services and the ready-to-mount HTTP handler.

func New

func New(ctx context.Context, cfg Config) (*Auth, error)

func (*Auth) Handler

func (a *Auth) Handler() http.Handler

Handler returns ready-to-mount net/http routes under Config.BasePath.

func (*Auth) Middleware

func (a *Auth) Middleware(next http.Handler) http.Handler

Middleware resolves authkit's session cookie or a Bearer token and adds the principal to the request context. Authorization remains the handler's job.

func (*Auth) SignInWithIDToken

func (a *Auth) SignInWithIDToken(ctx context.Context, provider, rawIDToken, expectedNonce string) (identity.LoginResult, error)

SignInWithIDToken verifies a native Google, Microsoft, or Apple ID token and returns either an authkit session or a TOTP challenge for the configured user. The app must retain a one-time nonce and pass its expected value here.

type Config

type Config struct {
	BaseURL          string
	BasePath         string
	Storage          Storage
	Signer           identity.Signer // required for the two-factor plugin
	AdminToken       string
	EmailAndPassword EmailAndPassword
	SocialProviders  map[string]SocialProvider
	OrganizationSSO  map[string]OIDCProvider
	Plugins          []Plugin
}

type EmailAndPassword

type EmailAndPassword struct{ Enabled bool }

type FlowStore

type FlowStore interface {
	Put(ctx context.Context, id, kind string, payload []byte, expiresAt time.Time) error
	Take(ctx context.Context, id, kind string) (payload []byte, ok bool, err error)
}

FlowStore keeps short-lived, one-time OAuth and passkey challenges. Take must atomically remove the flow so a callback cannot replay it.

type OIDCProvider

type OIDCProvider struct {
	OrgID        string
	IssuerURL    string
	ClientID     string
	ClientSecret string
	RedirectURL  string
	Scopes       []string
}

OIDCProvider binds an organization to its own OpenID Connect issuer. OrgID must refer to an existing organization.

type Plugin

type Plugin interface {
	// contains filtered or unexported methods
}

Plugin configures an optional authkit feature.

func Organization

func Organization() Plugin

func Passkey

func Passkey(cfg gowebauthn.Config) Plugin

func TwoFactor

func TwoFactor() Plugin

type SocialProvider

type SocialProvider struct {
	ClientID        string
	NativeClientIDs []string
	ClientSecret    string
	RedirectURL     string
	MicrosoftTenant string
	AppleTeamID     string
	AppleKeyID      string
	ApplePrivateKey *ecdsa.PrivateKey
}

SocialProvider is the app-level configuration for one provider. RedirectURL defaults to BaseURL + BasePath + "/callback/" + provider name.

type Storage

type Storage struct {
	Migrate        func(context.Context) error
	Flows          FlowStore
	Users          identity.UserStore
	Sessions       identity.SessionStore
	Memberships    identity.MembershipStore
	SocialAccounts identity.SocialAccountStore
	MFA            identity.MFAStore
	Recovery       identity.RecoveryStore
	Passkeys       identity.WebAuthnCredentialStore
	Organizations  identity.OrgStore
}

Storage supplies the persistence ports. Postgres returns a complete Storage; applications can supply their own implementations.

func Postgres

func Postgres(pool *pgxpool.Pool) Storage

Directories

Path Synopsis
Package identity is the framework- and storage-agnostic core of authkit: it resolves who a caller is (a Principal) from a credential, and mints/exchanges the credentials machines use.
Package identity is the framework- and storage-agnostic core of authkit: it resolves who a caller is (a Principal) from a credential, and mints/exchanges the credentials machines use.
Package social provides OpenID Connect sign-in for Google, Microsoft, LinkedIn, and Apple.
Package social provides OpenID Connect sign-in for Google, Microsoft, LinkedIn, and Apple.
store
postgres
Package postgres is authkit's canonical storage adapter: pgx-backed implementations of the identity ports (credentials, users, memberships, sessions) plus the schema.
Package postgres is authkit's canonical storage adapter: pgx-backed implementations of the identity ports (credentials, users, memberships, sessions) plus the schema.
token
eddsa
Package eddsa is authkit's default Signer adapter: short-lived EdDSA (Ed25519) JWTs verified by signature only, with a published JWKS.
Package eddsa is authkit's default Signer adapter: short-lived EdDSA (Ed25519) JWTs verified by signature only, with a published JWKS.
transport
fiber
Package fiber adapts authkit to gofiber/fiber/v2: a middleware that authenticates the request and carries the Principal in c.Locals.
Package fiber adapts authkit to gofiber/fiber/v2: a middleware that authenticates the request and carries the Principal in c.Locals.
nethttp
Package nethttp adapts authkit to the standard net/http server: a middleware that authenticates the request and carries the Principal in the context.
Package nethttp adapts authkit to the standard net/http server: a middleware that authenticates the request and carries the Principal in the context.
webauthn
gowebauthn
Package gowebauthn is authkit's WebAuthn (passkey) adapter over github.com/go-webauthn/webauthn.
Package gowebauthn is authkit's WebAuthn (passkey) adapter over github.com/go-webauthn/webauthn.

Jump to

Keyboard shortcuts

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