Drop-in authentication library for Go SaaS apps
- Go 100%
Features: - API key generation with configurable prefix and SHA-256 hash storage - JWT auth (HS256, RS256) with refresh token flow and revocation - Multi-tenant org → project → key hierarchy - net/http middleware: RequireAPIKey, RequireJWT, RequireScope - Rate limiting per key - MemoryStore (tests) and SQLiteStore (production) - Full test suite with adversarial tests and benchmarks |
||
|---|---|---|
| auth.go | ||
| auth_test.go | ||
| bench_test.go | ||
| go.mod | ||
| go.sum | ||
| jwt.go | ||
| middleware.go | ||
| README.md | ||
| store_memory.go | ||
| store_sqlite.go | ||
forge-auth
Drop-in authentication library for Go SaaS apps. Zero framework dependencies — works with net/http.
Features
- API Key Auth — Generate keys with configurable prefix, SHA-256 hash storage, validation, rotation, rate limiting
- JWT Auth — HS256/RS256, refresh token flow, token revocation via blacklist
- Multi-tenant — Organization → Project → API Key hierarchy with scoped permissions
- Middleware —
RequireAPIKey,RequireJWT,RequireScopefornet/http - Pluggable Storage —
KeyStoreinterface with SQLite and in-memory implementations
Quick Start
package main
import (
"fmt"
"net/http"
"time"
auth "go.ashforge.dev/auth"
)
func main() {
store := auth.NewMemoryStore()
// Create org and project.
store.CreateOrg(nil, &auth.Organization{
ID: "org-1", Name: "My Org", CreatedAt: time.Now(),
})
store.CreateProject(nil, &auth.Project{
ID: "proj-1", OrgID: "org-1", Name: "My Project", CreatedAt: time.Now(),
})
// Generate an API key.
plaintext, key, _ := auth.GenerateAPIKey("at_", "my-key", "org-1", "proj-1", []string{"read", "write"})
store.CreateKey(nil, key)
fmt.Println("API Key (save this!):", plaintext)
// Protected endpoint.
mux := http.NewServeMux()
mux.Handle("/api/data", auth.RequireAPIKey(store, auth.WithRateLimit(100, time.Minute))(
auth.RequireScope("read")(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
k := auth.GetKey(r.Context())
fmt.Fprintf(w, "Hello %s from org %s", k.Name, k.OrgID)
}),
),
))
http.ListenAndServe(":8080", mux)
}
JWT Usage
cfg := &auth.JWTConfig{
Algorithm: auth.HS256,
HMACSecret: []byte("your-secret-key"),
Issuer: "myapp",
DefaultExpiry: 15 * time.Minute,
Store: store, // For revocation support
}
// Issue token.
token, _ := auth.IssueJWT(cfg, &auth.Claims{
Subject: "user-123",
OrgID: "org-1",
Scopes: []string{"read", "write"},
})
// Validate token.
claims, _ := auth.ValidateJWT(ctx, cfg, token)
// Revoke token.
auth.RevokeJWT(ctx, cfg, token)
// Refresh token flow.
pair, _ := auth.IssueTokenPair(&auth.RefreshTokenConfig{
JWTConfig: cfg,
RefreshExpiry: 7 * 24 * time.Hour,
}, &auth.Claims{Subject: "user-123"})
Storage
Implements KeyStore interface. Ships with:
NewMemoryStore()— in-memory, great for testsNewSQLiteStore(db)— SQLite, great for single-node production
License
MIT