Authsome

Forge Extension

Mount Authsome into a Forge application as a first-class extension with automatic dependency discovery and route registration.

Authsome ships a ready-made Forge extension in the extension package. It wires the engine, HTTP API, middleware, and lifecycle management into Forge's extension system with automatic discovery of optional forgery bridges.

Installation

import "github.com/xraph/authsome/extension"

Registering the extension

package main

import (
	"github.com/xraph/forge"
	"github.com/xraph/authsome/extension"
)

func main() {
	app := forge.New()

	authExt := extension.New(
		extension.WithGroveDatabase(""), // resolve default grove.DB from DI
	)

	app.RegisterExtension(authExt)
	app.Run()
}

Or with an explicit store:

import pgstore "github.com/xraph/authsome/store/postgres"

authExt := extension.New(
	extension.WithEngineOption(authsome.WithStore(pgstore.New(db))),
)

What the extension does

Lifecycle eventBehaviour
RegisterLoads configuration, auto-discovers bridges from DI, builds the engine, registers it in the container
StartRuns store.Migrate (unless disabled), initializes plugins, auto-registers strategies
RegisterRoutesMounts all Authsome HTTP endpoints on the Forge router
MiddlewaresReturns the auth middleware for global application to all routes
StopCalls engine.Stop -- emits OnShutdown to all plugins

Extension options

OptionTypeDefaultDescription
WithConfig(cfg)ConfigdefaultsFull config struct
WithPlugin(p)plugin.Plugin--Register a plugin (repeatable)
WithPlugins(ps...)...plugin.Plugin--Register multiple plugins
WithEngineOption(opt)authsome.Option--Pass engine option directly
WithEngineOptions(opts...)...authsome.Option--Pass multiple engine options
WithDisableRoutes()--falseSkip HTTP route registration
WithDisableMigrate()--falseSkip migrations on Start
WithBasePath(path)string"/v1/auth"URL prefix for all auth routes
WithGroveDatabase(name)string""Name of the grove.DB to resolve from DI
WithRequireConfig(b)boolfalseRequire config in YAML files
WithLogger(l)log.LoggerForge loggerCustom logger

Auto-discovered bridges

When running inside a Forge app, the extension automatically discovers optional bridges from the DI container. You do not need to configure these manually -- they are resolved if the corresponding extension is registered.

BridgeDiscovered fromPurpose
Chroniclechronicle.EmitterAudit trail logging
Warden*warden.EngineRBAC/ReBAC/ABAC authorization
Keysmith*keysmith.EngineAPI key management
Relay*relay.RelayWebhook event delivery
Herald*herald.HeraldMulti-channel notifications
Dispatch*dispatch.EngineBackground job queue
Ledger*ledger.LedgerBilling and metering
Vault*vault.VaultSecrets and feature flags
Grove DB*grove.DBDatabase (PostgreSQL, SQLite, MongoDB)

If a bridge is not found in the container, a no-op implementation is used automatically.

File-based configuration (YAML)

The extension loads configuration from YAML config files. It looks for config under these keys (in order):

  1. extensions.authsome -- standard Forge extension config namespace
  2. authsome -- top-level shorthand

Example YAML config

# forge.yaml
extensions:
  authsome:
    base_path: "/v1/auth"
    disable_routes: false
    disable_migrate: false
    grove_database: ""
    debug: false

    session:
      token_ttl: "1h"
      refresh_token_ttl: "720h"
      max_active_sessions: 0
      rotate_refresh_token: true

    password:
      min_length: 8
      require_uppercase: true
      require_lowercase: true
      require_digit: true
      bcrypt_cost: 12
      algorithm: "bcrypt"

    rate_limit:
      enabled: true
      signin_limit: 5
      signup_limit: 3
      window_seconds: 60

    lockout:
      enabled: true
      max_attempts: 5
      lockout_duration_seconds: 900
      reset_after_seconds: 3600

    mailer:
      provider: "resend"
      resend:
        api_key: "${RESEND_API_KEY}"
        from: "noreply@example.com"

Merge behaviour

File-based configuration is merged with programmatic options. Programmatic boolean flags (DisableRoutes, DisableMigrate, Debug) always win when set to true. For other fields, YAML values take precedence, then programmatic values, then defaults.

Accessing the engine from other extensions

After Register is called by Forge, authExt.Engine() returns the fully initialized engine:

eng := authExt.Engine()
// Use eng.Store(), eng.Plugins(), etc. from another extension.

Or resolve the engine from the DI container:

import (
	"github.com/xraph/vessel"
	authsome "github.com/xraph/authsome"
)

eng, err := vessel.Inject[*authsome.Engine](app.Container())

Adding plugins

Register plugins that provide additional authentication methods:

import (
	"github.com/xraph/authsome/extension"
	"github.com/xraph/authsome/plugins/social"
	"github.com/xraph/authsome/plugins/mfa"
	"github.com/xraph/authsome/plugins/passkey"
	"github.com/xraph/authsome/plugins/magiclink"
	"github.com/xraph/authsome/plugins/phone"
)

authExt := extension.New(
	extension.WithGroveDatabase(""),
	extension.WithPlugins(
		social.New(
			social.WithGoogle(googleID, googleSecret),
			social.WithGitHub(ghID, ghSecret),
		),
		mfa.New(),
		passkey.New(passkey.Config{
			RPName: "My App",
			RPID:   "example.com",
			Origin: "https://example.com",
		}),
		magiclink.New(),
		phone.New(),
	),
)

Auth middleware

The extension automatically registers global authentication middleware via Middlewares(). This middleware:

  1. Reads the Authorization: Bearer <token> header
  2. Validates the token (opaque lookup or JWT validation)
  3. Falls back to the strategy registry (API keys, etc.) when session resolution fails
  4. Sets the authenticated user and session on the request context

Other extensions can access the authenticated user via the Forge scope:

import "github.com/xraph/forge"

func handler(w http.ResponseWriter, r *http.Request) {
	scope := forge.ScopeFromContext(r.Context())
	userID := scope.UserID()
	orgID := scope.OrgID()
}

Grove database integration

Using the default grove database

ext := extension.New(
	extension.WithGroveDatabase(""),
)

Using a named grove database

ext := extension.New(
	extension.WithGroveDatabase("authsome"),
)

Store resolution order

  1. Explicit store -- if WithEngineOption(authsome.WithStore(s)) was called, it is used directly.
  2. Grove database -- if WithGroveDatabase(name) was called, the named or default grove.DB is resolved from DI.
  3. Auto-discovery -- if neither is configured, the extension attempts to resolve a default grove.DB from the container.

Dashboard integration

When the Forge Dashboard extension is present, Authsome automatically registers as the dashboard's authentication provider:

  • Provides sign-in and sign-up pages for the dashboard
  • Handles session checking and user resolution
  • Contributes "Profile" and "Security" items to the user dropdown menu
  • Renders user management pages in the dashboard

On this page