authkit

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 12 Imported by: 0

README

goravel-authkit

Batteries-included, session-based authentication + user management for Goravel apps. Install it, register the provider, configure it — and it publishes the API routes, database migrations, the User model, and (optionally) the admin user-management endpoints for you.

Inspired by jeremykenedy/laravel-auth, adapted to Go/Goravel idioms (static typing, code-based migrations). The React/PatternFly frontend counterpart lives in @freshost/authkit-ui.

Status: v0.2.0 (pre-1.0). Ships the core that real projects already use: login/logout/session, change-password with other-session invalidation, login rate-limiting, admin user-management CRUD, an audit log, TOTP two-factor, and — new in 0.2.0 — multi-guard (several independent auth domains in one app). Later phases: self-registration, email verification, password reset by email, RBAC. While pre-1.0, minor versions may carry breaking changes — see the CHANGELOG.

Multi-guard (new in 0.2.0)

A guard is a self-contained auth domain: its own user table, Goravel session guard, route prefix, remember cookie, rate-limit bucket and feature set. Declare as many as you like under authkit.guards in config/authkit.go and they run side-by-side in one app — e.g. an internal admin console and a customer-facing client portal, each with its own users:

config.Add("authkit", map[string]any{
    // Root-level authkit.* settings apply to every guard; a guard can override them.
    "min_password_length":   8,
    "user_management_roles": []string{"admin"},

    "guards": map[string]any{
        "admin": map[string]any{
            "prefix":      "/api/v1",     // routes under /api/v1/auth, /api/v1/users …
            "users_table": "users",
        },
        "client": map[string]any{
            "prefix":              "/api/client/v1",
            "users_table":         "client_users",   // secondary tables default to client_audit_logs, etc.
            "min_password_length": 12,               // per-guard override wins
            "features":            map[string]any{"two_factor": false},
        },
    },
})

Each guard's secondary tables (<name>_audit_logs, remember tokens, sessions), session keys and remember cookie are namespaced, so two guards on one origin never collide. The package auto-registers a Goravel session guard and the migrations for every declared guard — no config/auth.go needed (opt out with authkit.register_guards / authkit.register_migrations = false).

Single-guard apps are unchanged: omit authkit.guards and keep authkit.guard / authkit.route_prefix — behaviour is exactly v0.1.0.

What you get

Endpoint Description
POST /auth/login Session login (rate-limited), bcrypt verification
POST /auth/logout Destroys the session
GET /auth/me Current authenticated user
PUT /auth/password Change password (invalidates other sessions)
POST /auth/two-factor TOTP enroll / confirm / disable / recovery-codes (toggleable)
POST /auth/two-factor-challenge Complete a 2FA login
GET/POST/PUT/DELETE /users Admin user management (toggleable)
POST /users/{id}/password Admin set-password

Endpoints are mounted per guard under its prefix (e.g. /api/v1/auth/login, /api/client/v1/auth/login). Plus: session-fixation protection, password_changed_at multi-session invalidation, TOTP two-factor (encrypted secret + single-use recovery codes), a per-guard audit-log table + writer, and an auth:create-user artisan command for bootstrapping the first admin.

Note on SDKs/Swagger: the package ships no Swagger/OpenAPI annotations (dynamic per-guard mounting can't be expressed in static annotations), so authkit's endpoints won't appear in a host's swag-generated OpenAPI. The React companion @freshost/authkit-ui is hand-written against these endpoints.

Documentation

  • Installation — full wiring (provider, config, migrations, routes, first admin).
  • Configurationauthkit.* keys, feature toggles, the role gate.
  • API reference — endpoints, request/response shapes, error codes, operation ids.
  • Security model — guarantees, operator responsibilities, v1 limitations, audit actions.
  • Architecture — layering, the canonical model, Goravel integration, the SDK contract loop.

Adopting into an existing app? There is a bundled agent skill at .claude/skills/adopt-goravel-authkit/ — copy it into your project's .claude/skills/ and an AI agent can perform the swap (including the admin_users → users data migration) step by step.

Install

Register the provider, then mount the routes with one line. The provider auto-registers the Goravel guard(s), migrations and artisan commands from your config; you add a single RegisterAll call in the routing callback.

go get github.com/freshost/goravel-authkit
./artisan package:install github.com/freshost/goravel-authkit
./artisan migrate
./artisan auth:create-user --email=admin@example.com --password=change-me

package:install makes three small, additive edits to your app:

  1. registers authkit.ServiceProvider in bootstrap/providers.go;
  2. writes config/authkit.go (the package's own settings file);
  3. sets bcrypt cost 12 in your existing config/hashing.go.

Then mount the routes in your routing callback (routes/web.go) — one line, which covers every declared guard:

import authkitroutes "github.com/freshost/goravel-authkit/routes"

func Web() {
    authkitroutes.RegisterAll(facades.Route())
}

Routes are mounted here (not in the provider) because Goravel rebuilds the HTTP engine when global middleware is set, which would discard routes a provider registered in Boot. You don't append migrations or write config/auth.go by hand — the provider auto-registers the Goravel session guard and migrations for each guard declared in config/authkit.go (opt out with authkit.register_guards / authkit.register_migrations = false; an existing hand-written guard still wins).

Existing auth already? The install is additive, but if your app already has an admin guard, a users table, or its own login code, follow the adoption skill (reconcile the guard, migrate admin_users → users) instead of the plain install.

For local development before the repo is public, add a replace directive (replace github.com/freshost/goravel-authkit => ../goravel-authkit) and register the provider manually (&authkit.ServiceProvider{} in bootstrap/providers.go).

Configuration

Everything is tuned through the config/authkit.go file that package:install writes (all keys optional — the package falls back to safe defaults). The you consume the package only by registering the provider — there is no Go wiring to do.

Key Type Default Meaning
authkit.route_prefix string /api/v1 Prefix for all routes
authkit.guard string admin Goravel session guard name
authkit.min_password_length int 8 Minimum new-password length
authkit.rate_limit.attempts int 5 Login attempts per window per IP
authkit.rate_limit.window int 60 Rate-limit window (seconds)
authkit.features.user_management bool true Register /users CRUD
authkit.features.audit_log bool true Write audit entries
authkit.features.two_factor bool true Register TOTP two-factor endpoints + login gate
authkit.two_factor.issuer string "" Authenticator issuer (empty = app name)
authkit.two_factor.recovery_codes int 8 Recovery codes per confirmation
authkit.user_management_roles []string [] Roles allowed on /users (empty = any)
authkit.guards map Per-guard config (multi-guard). Each key is a guard name; value carries prefix, users_table and optional overrides. Omit for single-guard.
authkit.register_guards bool true Auto-register a Goravel session guard per declared guard
authkit.register_migrations bool true Auto-register the migrations for each guard's tables

Root-level authkit.* settings apply to every guard; anything set inside a authkit.guards.<name> entry overrides them for that guard. A guard's secondary tables default from its users_table (e.g. client_usersclient_audit_logs). See docs/configuration.md for details.

Protect your own routes with a guard

To put one of authkit's guards in front of a route your host app owns (the authkit equivalent of Laravel's auth:guard), use Protect / ProtectRole for the middleware chain and AuthUserID to read the current user:

import authkitroutes "github.com/freshost/goravel-authkit/routes"

facades.Route().Prefix("/api/client/v1/portal").
    Middleware(authkitroutes.Protect("client")...).
    Group(func(r route.Router) {
        r.Get("/whoami", func(ctx http.Context) http.Response {
            return ctx.Response().Success().Json(http.Json{
                "userId": authkitroutes.AuthUserID(ctx).String(),
            })
        })
    })

// Or require a role: authkitroutes.ProtectRole("client", "admin")

Programmatic API (facade)

Besides the HTTP endpoints, the package exposes a facade so your own Go code (seeders, installers, custom commands, domain logic) can drive auth/users without HTTP:

import authfacades "github.com/freshost/goravel-authkit/facades"

user, err := authfacades.Authkit().CreateUser(ctx, "jane@example.com", "Jane", "secret123", "admin")
user, err := authfacades.Authkit().Authenticate(ctx, email, password)
err := authfacades.Authkit().ChangePassword(ctx, id, current, next)

// two-factor
secret, url, err := authfacades.Authkit().EnableTwoFactor(ctx, id)
codes, err := authfacades.Authkit().ConfirmTwoFactor(ctx, id, "123456")

See contracts/authkit.go for the full interface.

Security notes (read before deploying)

  • Hashing is bcrypt cost 12. package:install writes config/hashing.go with bcrypt cost 12 (what existing $2a$12$ hashes verify against). Keep it that way — the package hashes via the Goravel Hash facade.
  • Configure trusted proxies. The login rate-limiter and the audit log key on ctx.Request().Ip(), which honours X-Forwarded-For. Behind a proxy/CDN, set Goravel's http.trusted_proxies or the limit is bypassable (and audit IPs are spoofable) by sending a forged header.
  • No full RBAC yet. The /users management endpoints sit behind the session guard only — every authenticated user can manage users. Once your app assigns roles, gate them with authkit.user_management_roles (e.g. []string{"admin"}), which adds a RequireRole check. True RBAC (roles/permissions tables) is a later phase.
  • Each guard is an isolated auth domain — separate user table, session key, remember cookie and rate-limit bucket. Active-session tracking is keyed by a stable per-guard token, so concurrent logins to several guards in one browser all keep working. For stronger isolation, run each portal on its own subdomain/origin.
  • Sessions use httpOnly cookies with session-id regeneration on login (anti-fixation) and password_changed_at multi-session invalidation. Set session.same_site to lax or strict and session.secure=true in production.

License

MIT © Freshost

Documentation

Overview

Package authkit is the root of the goravel-authkit package: a batteries-included, session-based authentication + user-management module for Goravel apps.

Install is a single step:

./artisan package:install github.com/freshost/goravel-authkit

which registers this ServiceProvider and publishes config/authkit.go (and config/hashing.go). The app declares its auth domains as guards under authkit.guards in config/authkit.go; the provider then auto-registers a Goravel session guard and the migrations for each guard (no config/auth.go needed). The app mounts the HTTP routes from its own routing callback with one line:

authkitroutes "github.com/freshost/goravel-authkit/routes"
// inside foundation.Setup().WithRouting(func(){ ... })
authkitroutes.RegisterAll(facades.Route())

Routes are registered app-side (not in the provider) because Goravel rebuilds the HTTP engine when global middleware is set — which happens AFTER providers boot — so any routes a provider registers in Boot are discarded. The routing callback runs after that rebuild, so routes registered there survive. The package starts its own session on each guard's /auth group, so no global session middleware is required.

See the README and docs/installation.md.

Index

Constants

View Source
const Binding = "authkit"

Binding is the service-container key under which the Authkit service is bound; the facades.Authkit() accessor resolves it.

View Source
const Name = "Authkit"

Name is the human-readable module name.

View Source
const PackageName = "github.com/freshost/goravel-authkit"

PackageName is the module path, used as the first argument to Publishes.

Variables

App holds the application instance, used by the facade to resolve the service.

Functions

This section is empty.

Types

type Authkit

type Authkit struct {
	// contains filtered or unexported fields
}

Authkit is the concrete implementation behind facades.Authkit() and the value returned by New. It wraps the auth + user-management + two-factor services so app code can drive them programmatically against a specific user table.

func New added in v0.2.0

func New(cfg Config) *Authkit

New builds a programmatic authkit instance bound to cfg.UsersTable. Zero-valued fields fall back to the package defaults, so authkit.New(authkit.Config{}) behaves exactly like the default single-instance service.

func NewAuthkit

func NewAuthkit(app foundation.Application) *Authkit

NewAuthkit builds the default instance from the published authkit.* config (read lazily at resolve time, so config is available). It is the thin wrapper behind facades.Authkit(); multi-table hosts use New directly.

func (*Authkit) Authenticate

func (a *Authkit) Authenticate(ctx context.Context, email, password string) (*models.User, error)

func (*Authkit) ChangePassword

func (a *Authkit) ChangePassword(ctx context.Context, id uuid.UUID, currentPassword, newPassword string) error

func (*Authkit) ConfirmTwoFactor

func (a *Authkit) ConfirmTwoFactor(ctx context.Context, id uuid.UUID, code string) ([]string, error)

func (*Authkit) CreateUser

func (a *Authkit) CreateUser(ctx context.Context, email, name, password, role string) (*models.User, error)

func (*Authkit) DeleteUser

func (a *Authkit) DeleteUser(ctx context.Context, id uuid.UUID) error

func (*Authkit) DisableTwoFactor

func (a *Authkit) DisableTwoFactor(ctx context.Context, id uuid.UUID) error

func (*Authkit) EnableTwoFactor

func (a *Authkit) EnableTwoFactor(ctx context.Context, id uuid.UUID) (secret, otpauthURL string, err error)

func (*Authkit) GetUser

func (a *Authkit) GetUser(ctx context.Context, id uuid.UUID) (*models.User, error)

func (*Authkit) ListUsers

func (a *Authkit) ListUsers(ctx context.Context) ([]models.User, error)

func (*Authkit) SetPassword

func (a *Authkit) SetPassword(ctx context.Context, id uuid.UUID, newPassword string) (*models.User, error)

func (*Authkit) VerifyTwoFactor

func (a *Authkit) VerifyTwoFactor(ctx context.Context, id uuid.UUID, code string) (bool, error)

type Config added in v0.2.0

type Config struct {
	// Guard is the Goravel guard this instance represents (e.g. "client"). It is
	// recorded for symmetry with routes.Options; the programmatic methods operate
	// directly on UsersTable and do not resolve a guard.
	Guard string
	// UsersTable is the table this instance's users live in (default "" → "users").
	UsersTable string
	// MinPasswordLength is the minimum accepted new-password length (default → DefaultMinPasswordLength).
	MinPasswordLength int
	// TwoFactorIssuer is the issuer shown in the authenticator app.
	TwoFactorIssuer string
	// RecoveryCodeCount is how many recovery codes confirmation generates (default → DefaultRecoveryCodeCount).
	RecoveryCodeCount int
	// Roles, when non-empty, is the set of role values accepted on create/update.
	Roles []string
	// UserManagementRoles gates the management invariants (default → AdminRole).
	UserManagementRoles []string
}

Config describes one programmatic authkit instance: which user table it operates on plus the password / two-factor / role policy. Every field is optional — the zero value reproduces the single-instance defaults — so a host running two user domains builds one instance per domain:

client := authkit.New(authkit.Config{Guard: "client", UsersTable: "accounts"})
admin  := authkit.New(authkit.Config{Guard: "admin", UsersTable: "admin_users"})

type ServiceProvider

type ServiceProvider struct{}

ServiceProvider registers the goravel-authkit migrations, commands, and publishable config. HTTP routes are mounted app-side via routes.Register (see the package doc) because provider-registered routes do not survive the engine rebuild that global middleware triggers.

func (*ServiceProvider) Boot

func (r *ServiceProvider) Boot(app foundation.Application)

Boot registers the package migrations, artisan commands, and the publishable config (for `vendor:publish`). HTTP routes are NOT registered here — the app calls routes.Register from its routing callback (see the package doc).

func (*ServiceProvider) Register

func (r *ServiceProvider) Register(app foundation.Application)

Register stores the application instance and binds the Authkit service so facades.Authkit() (and any app code) can resolve it.

func (*ServiceProvider) Relationship

func (r *ServiceProvider) Relationship() binding.Relationship

Relationship declares the framework services the package depends on so it boots after them. It registers no container bindings of its own.

Directories

Path Synopsis
Package commands holds the goravel-authkit artisan commands.
Package commands holds the goravel-authkit artisan commands.
Package contracts defines the public interface exposed by the goravel-authkit facade.
Package contracts defines the public interface exposed by the goravel-authkit facade.
Package facades exposes the goravel-authkit programmatic API as a Goravel facade.
Package facades exposes the goravel-authkit programmatic API as a Goravel facade.
Package helpers holds small HTTP utilities shared by the goravel-authkit controllers and middleware: route-param parsing, the authenticated-user context key, and the session-regeneration workaround.
Package helpers holds small HTTP utilities shared by the goravel-authkit controllers and middleware: route-param parsing, the authenticated-user context key, and the session-regeneration workaround.
http
controllers
Package controllers holds the goravel-authkit HTTP controllers: the auth endpoints (login/logout/me/change-password) and the admin user-management CRUD.
Package controllers holds the goravel-authkit HTTP controllers: the auth endpoints (login/logout/me/change-password) and the admin user-management CRUD.
middleware
Package middleware holds the goravel-authkit HTTP middleware: the session guard (Authenticated) and the login rate-limiter (RateLimitAuth).
Package middleware holds the goravel-authkit HTTP middleware: the session guard (Authenticated) and the login rate-limiter (RateLimitAuth).
responses
Package responses holds the request/response DTOs for the goravel-authkit HTTP endpoints.
Package responses holds the request/response DTOs for the goravel-authkit HTTP endpoints.
Package migrations holds the code-based migrations owned by goravel-authkit.
Package migrations holds the code-based migrations owned by goravel-authkit.
Package models holds the canonical GORM entities owned by the goravel-authkit package: the single User table backing authentication and the AuditLog table.
Package models holds the canonical GORM entities owned by the goravel-authkit package: the single User table backing authentication and the AuditLog table.
Package repositories owns the GORM data access for goravel-authkit.
Package repositories owns the GORM data access for goravel-authkit.
Package routes registers the goravel-authkit HTTP endpoints onto a consuming app's router.
Package routes registers the goravel-authkit HTTP endpoints onto a consuming app's router.
Package services holds the goravel-authkit business logic: credential verification, password changes with other-session invalidation, user management, and audit writes.
Package services holds the goravel-authkit business logic: credential verification, password changes with other-session invalidation, user management, and audit writes.
Command setup implements `./artisan package:install github.com/freshost/goravel-authkit`.
Command setup implements `./artisan package:install github.com/freshost/goravel-authkit`.
config
This file is published into the consuming app's config/ directory by `./artisan package:install github.com/freshost/goravel-authkit`.
This file is published into the consuming app's config/ directory by `./artisan package:install github.com/freshost/goravel-authkit`.

Jump to

Keyboard shortcuts

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