A small library providing go HTTP handlers for OIDC authentication and API key validation.
Find a file
2026-03-23 16:36:10 -04:00
pkg refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
.gitignore build: configure goreleaser to skip builds 2026-03-18 01:56:15 -04:00
.golangci.yml build: add golangci-lint and goreleaser configs 2026-03-18 01:25:43 -04:00
.goreleaser.yaml build: configure goreleaser to skip builds 2026-03-18 01:56:15 -04:00
auth.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
callback.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
errs.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
go.mod refactor(auth): separate web and api auth logic 2026-03-21 18:22:11 -04:00
go.sum refactor(auth): separate web and api auth logic 2026-03-21 18:22:11 -04:00
handlers.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
info.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
LICENSE Initial commit 2026-02-06 18:41:56 -05:00
login.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
logout.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
middleware.go fix(auth): swap requireAuthorizedHandler boolean arguments 2026-03-23 16:36:10 -04:00
oicd.go refactor(auth): unify auth to support OIDC and API key authentication 2026-03-18 01:25:19 -04:00
options.go refactor(auth): separate web and api auth logic 2026-03-21 18:22:11 -04:00
README.md refactor(auth): separate web and api auth logic 2026-03-21 18:22:11 -04:00

simple-auth

A lightweight Go library providing HTTP handlers for OIDC authentication and API key validation.

Features

  • OIDC/OAuth 2.0 authentication with automatic token refresh
  • PKCE support for enhanced security
  • API key authentication with BLAKE2b hashing
  • Cookie-based session management with encryption
  • JWT profile authentication support
  • Pluggable user storage via interface

Installation

go get git.crawford.zone/jd/simple-auth.git

Quick Start

OIDC Authentication

// Define a User type
type MyUser struct{}

func (u *MyUser) Authorized(path string) bool { return true }
func (u *MyUser) NeedsUpdate(claims *oidc.IDTokenClaims) bool {
    return false
}

// Define a UserHandler
type MyUserHandler struct{}

func (h *MyUserHandler) CreateUser(ctx context.Context, claims *oidc.IDTokenClaims) (string, error) {
    return "", nil
}
func (h *MyUserHandler) GetUserID(ctx context.Context, subject string) (string, error) {
    return "", simpleauth.ErrNoUser
}
func (h *MyUserHandler) GetUser(ctx context.Context, id string) (*MyUser, error) {
    return &MyUser{}, nil
}
func (h *MyUserHandler) UpdateUser(ctx context.Context, id string, claims *oidc.IDTokenClaims) (*MyUser, error) {
    return &MyUser{}, nil
}

// Create Auth instance
auth, err := simpleauth.New[string, *MyUser](
    context.Background(),
    &MyUserHandler{},
    simpleauth.WithOAuthClient("client-id", "client-secret", "https://app.com/auth/callback"),
    simpleauth.WithIssuer("https://issuer.com"),
    simpleauth.WithSecurityKeys(hashKey, encryptionKey),
    simpleauth.WithPaths(
        simpleauth.WithHomePath("/"),
        simpleauth.WithLoginPath("/login"),
        simpleauth.WithLogoutPath("/logout"),
        simpleauth.WithRedirectPath("/auth/callback"),
    ),
)

// Register handlers
mux := http.NewServeMux()
mux.HandleFunc("/auth/login", auth.LoginHandler())
mux.HandleFunc("/auth/logout", auth.LogoutHandler())
mux.HandleFunc("/auth/callback", auth.CallbackHandler())
mux.Handle("/", auth.InfoHandler(auth.RequireAuthorizedHandler(handler)))

API Key Authentication

// Define KeyHandler
type MyKeyHandler struct{}

func (h *MyKeyHandler) HashKey(key string) string {
    sum := blake2b.Sum256([]byte(key))
	return base64.RawURLEncoding.EncodeToString(sum[:])
}

func (h *MyKeyHandler) Valid(ctx context.Context, hashedKey string) (bool, error) {
    return true, nil
}

// Create ApiAuth instance
apiAuth := simpleauth.NewApi(&MyKeyHandler{}, slog.Default())

// Register middleware
mux := http.NewServeMux()
mux.HandleFunc("/api/data", apiAuth.InfoHandler(handler))
mux.HandleFunc("/api/private", apiAuth.InfoHandler(apiAuth.APIRequireAuthorizedHandler(handler)))

Configuration

Auth Options

Option Description
WithOAuthClient Client ID, secret, and redirect URL
WithSecurityKeys Hash key (32/64 bytes) and encryption key (16/24/32 bytes)
WithJWKSKey JWT signing key path and key ID
WithRedirectURL OAuth redirect URL
WithInsecureHost Allow HTTP cookies
WithInsecureProvider Skip TLS verification for OIDC provider
WithClientID OAuth client ID
WithClientSecret OAuth client secret
WithPkce Enable PKCE flow
WithIssuer OIDC issuer URL
WithResponseMode OAuth response mode
WithScopes OAuth scopes (default: openid, email, profile)
WithCookiePrefix Cookie prefix (default: auth)
WithPaths Path configuration

Paths

Field Description Default
Home Post-login redirect /
Login Login handler /auth/login
Logout Logout handler /auth/logout
Redirect OAuth callback /auth/callback
Unauthorized Unauthorized redirect -

Middleware

InfoHandler

Extracts authentication info and adds it to the request context. Must be used before RequireAuthorizedHandler.

mux.Handle("/protected", auth.InfoHandler(handler))

RequireAuthorizedHandler

For web pages - redirects to login/unauthorized pages when not authenticated/authorized.

mux.Handle("/dashboard", auth.InfoHandler(auth.RequireAuthorizedHandler(handler)))

APIRequireAuthorizedHandler

For API endpoints - returns HTTP 401/403 with WWW-Authenticate header instead of redirects.

mux.Handle("/api/data", auth.InfoHandler(auth.APIRequireAuthorizedHandler(handler)))

Accessing User Information

func handler(w http.ResponseWriter, r *http.Request) {
    info := auth.GetInfo(r)
    
    if !info.Authenticated {
        http.Redirect(w, r, "/auth/login", http.StatusFound)
        return
    }
    
    // info.ID      - User ID
    // info.Name    - Display name
    // info.Picture - Profile URL
    // info.Authorized - Path authorization status
}

Accessing API Key Information

func handler(w http.ResponseWriter, r *http.Request) {
    info := apiAuth.GetInfo(r)
    
    if !info.Authenticated {
        w.Header().Set("WWW-Authenticate", `Bearer realm="Restricted"`)
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }
    
    // info.HashedKey - BLAKE2b-hashed API key
}