authkit initial
This commit is contained in:
parent
5173b0a43d
commit
134393fbca
43 changed files with 5188 additions and 1 deletions
121
authkit.go
Normal file
121
authkit.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package authkit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.juancwu.dev/juancwu/errx"
|
||||
)
|
||||
|
||||
// Deps bundles every backing store and the password hasher the Auth service
|
||||
// depends on. All fields are required; New panics on a nil dep so misuse is
|
||||
// caught at boot rather than under load.
|
||||
type Deps struct {
|
||||
Users UserStore
|
||||
Sessions SessionStore
|
||||
Tokens TokenStore
|
||||
APIKeys APIKeyStore
|
||||
Roles RoleStore
|
||||
Permissions PermissionStore
|
||||
Hasher Hasher
|
||||
}
|
||||
|
||||
// Config tunes session/JWT/token TTLs, cookie shape, JWT signing material,
|
||||
// and optional hooks. Any zero-valued duration is replaced with a sane
|
||||
// default in New; required fields (notably JWTSecret) cause New to panic.
|
||||
type Config struct {
|
||||
// Session (opaque) cookies + DB-backed lifetime
|
||||
SessionIdleTTL time.Duration
|
||||
SessionAbsoluteTTL time.Duration
|
||||
SessionCookieName string
|
||||
SessionCookieDomain string
|
||||
SessionCookiePath string
|
||||
SessionCookieSecure bool
|
||||
SessionCookieHTTPOnly bool
|
||||
SessionCookieSameSite http.SameSite
|
||||
|
||||
// JWT (HS256)
|
||||
JWTSecret []byte
|
||||
JWTIssuer string
|
||||
JWTAudience string
|
||||
AccessTokenTTL time.Duration
|
||||
RefreshTokenTTL time.Duration
|
||||
|
||||
// Single-use tokens
|
||||
EmailVerifyTTL time.Duration
|
||||
PasswordResetTTL time.Duration
|
||||
MagicLinkTTL time.Duration
|
||||
|
||||
// Hooks (optional)
|
||||
Clock func() time.Time
|
||||
Random io.Reader
|
||||
LoginHook func(ctx context.Context, email string, success bool) error
|
||||
}
|
||||
|
||||
// Auth is the high-level service that composes the stores and hasher into the
|
||||
// flows callers use: registration, login, sessions, JWTs, magic links, API
|
||||
// keys, and authz checks. It is safe for concurrent use; method receivers
|
||||
// never mutate Auth state after construction.
|
||||
type Auth struct {
|
||||
deps Deps
|
||||
cfg Config
|
||||
}
|
||||
|
||||
// New validates Deps and Config, fills in defaults, and returns a ready
|
||||
// service. It panics on missing deps or missing JWT secret rather than
|
||||
// returning an error — these are programmer errors, not runtime ones.
|
||||
func New(deps Deps, cfg Config) *Auth {
|
||||
if deps.Users == nil || deps.Sessions == nil || deps.Tokens == nil ||
|
||||
deps.APIKeys == nil || deps.Roles == nil || deps.Permissions == nil ||
|
||||
deps.Hasher == nil {
|
||||
panic(errx.New("authkit.New", "all Deps fields are required"))
|
||||
}
|
||||
if len(cfg.JWTSecret) == 0 {
|
||||
panic(errx.New("authkit.New", "Config.JWTSecret is required"))
|
||||
}
|
||||
|
||||
if cfg.SessionIdleTTL == 0 {
|
||||
cfg.SessionIdleTTL = 24 * time.Hour
|
||||
}
|
||||
if cfg.SessionAbsoluteTTL == 0 {
|
||||
cfg.SessionAbsoluteTTL = 30 * 24 * time.Hour
|
||||
}
|
||||
if cfg.SessionCookieName == "" {
|
||||
cfg.SessionCookieName = "authkit_session"
|
||||
}
|
||||
if cfg.SessionCookiePath == "" {
|
||||
cfg.SessionCookiePath = "/"
|
||||
}
|
||||
if cfg.SessionCookieSameSite == 0 {
|
||||
cfg.SessionCookieSameSite = http.SameSiteLaxMode
|
||||
}
|
||||
if cfg.AccessTokenTTL == 0 {
|
||||
cfg.AccessTokenTTL = 15 * time.Minute
|
||||
}
|
||||
if cfg.RefreshTokenTTL == 0 {
|
||||
cfg.RefreshTokenTTL = 30 * 24 * time.Hour
|
||||
}
|
||||
if cfg.EmailVerifyTTL == 0 {
|
||||
cfg.EmailVerifyTTL = 48 * time.Hour
|
||||
}
|
||||
if cfg.PasswordResetTTL == 0 {
|
||||
cfg.PasswordResetTTL = time.Hour
|
||||
}
|
||||
if cfg.MagicLinkTTL == 0 {
|
||||
cfg.MagicLinkTTL = 15 * time.Minute
|
||||
}
|
||||
if cfg.Clock == nil {
|
||||
cfg.Clock = func() time.Time { return time.Now().UTC() }
|
||||
}
|
||||
if cfg.Random == nil {
|
||||
cfg.Random = rand.Reader
|
||||
}
|
||||
|
||||
return &Auth{deps: deps, cfg: cfg}
|
||||
}
|
||||
|
||||
// now returns the configured wall clock, defaulting to time.Now in UTC.
|
||||
func (a *Auth) now() time.Time { return a.cfg.Clock() }
|
||||
Loading…
Add table
Add a link
Reference in a new issue