Integration contract
Named Go inputs, HTTP errors, authentication policy, and resource ownership.
Integration contract
The public Auth.Services() interfaces are the contract for custom transports and application code. Import github.com/nazimdjebloun/go-auth/api for operation inputs. Mounted HTTP routes call those same operations and add cookie handling, authentication middleware, CSRF checks, and rate limiting.
Application HTTP routes
Use Auth.Protect("POST /widgets/{id}", handler) for the complete browser
protection chain on your own endpoints. Add role, app-permission or organization
wrappers inside it so they run after authentication. Patterns use ServeMux
syntax for routing, rate selection and exemptions. Put Auth.CORS around a
method-selecting router so preflights run before dispatch. See
Protect application routes for a full example,
failure behavior and third-party router integration.
Migrating Go callers
Service operations with multiple values after context.Context now take one named input. This is a breaking Go API change before v1; return values and HTTP request bodies are unchanged. Single-value operations, such as Session.Validate(ctx, token) and OAuth.Initiate(ctx, provider), keep their direct argument.
// Previously: ChangeName(ctx, userID, name)
err := auth.Services().Auth.ChangeName(ctx, api.ChangeNameInput{
UserID: userID,
Name: name,
})
verified, err := auth.Services().TwoFactor.Verify(ctx, api.TwoFactorVerifyInput{
ChallengeID: challengeID,
BindingToken: bindingCookie,
Code: code,
IP: clientIP,
UserAgent: userAgent,
})Move the old positional arguments into the following fields, in the order shown. All types are in api.
| Service operation | Input type | Fields replacing positional arguments |
|---|---|---|
Admin.GetStats | GetAdminStatsInput | ActorID, ActorSessionID |
Auth.ChangeName | ChangeNameInput | UserID, Name |
Auth.DeleteAccount | DeleteAccountInput | UserID, Password |
Session.Create | CreateSessionInput | UserID, IP, UserAgent |
Session.Touch | TouchSessionInput | Token, LastActiveAt |
Session.RevokeByIDForUser | RevokeSessionForUserInput | SessionID, UserID |
Session.RevokeManyForUser | RevokeSessionsForUserInput | SessionIDs, UserID |
Session.RevokeAllExcept | RevokeAllSessionsExceptInput | UserID, ExceptSessionID |
Session.List | ListSessionsInput | UserID, Offset, Limit |
OAuth.InitiateLink | OAuthLinkInput | Provider, UserID, SessionTokenHash |
OAuth.Callback | OAuthCallbackInput | Provider, Code, State, BrowserState, SessionToken, IP, UserAgent |
OAuth.Unlink | OAuthUnlinkInput | UserID, Provider |
TwoFactor.Verify | TwoFactorVerifyInput | ChallengeID, BindingToken, Code, IP, UserAgent |
TwoFactor.Resend | TwoFactorResendInput | ChallengeID, BindingToken |
TwoFactor.Enable | TwoFactorEnableInput | UserID, Password, KeepOtherSessions, CallerSessionID |
TwoFactor.Disable | TwoFactorDisableInput | UserID, Password |
Invite.HardDeleteInvite | HardDeleteInviteInput | InviteID, ActorID |
Invite.RevokeInvite | RevokeInviteInput | InviteID, ActorID |
Invite.ResendInviteEmail | ResendInviteEmailInput | InviteID, ActorID |
OrgInvite.DeleteOrgInvite | DeleteOrgInviteInput | OrgID, InviteID, ActorID |
OrgInvite.ResendOrgInviteEmail | ResendOrgInviteEmailInput | OrgID, InviteID, ActorID |
Update mocks and custom implementations of the public capability interfaces too. In particular, middleware.SessionAuthenticator.Touch now has this signature:
Touch(ctx context.Context, input api.TouchSessionInput) errorNamed fields do not establish trust. Set actor/user IDs from the authenticated identity, client metadata from your transport, and binding/session credentials from their original cookies or equivalent trusted storage. Built-in handlers decode transport-specific bodies and supply actor identity separately; never accept it from request JSON. Public HTTP business fields use camelCase. Inputs can still contain passwords and one-time codes; do not log them wholesale.
For OAuth callbacks, keep State (provider callback query) separate from BrowserState (the initiating browser's state cookie). Linking also needs the current raw session token in SessionToken. The service compares these credentials; an input struct does not replace the browser-binding checks.
Direct service calls do not execute HTTP middleware. Your custom transport must supply authentication and route-level authorization, rate limiting, and any applicable browser CSRF protection. Low-level operations such as session creation assume the caller has already completed the required authentication flow. Optional service capabilities can be nil when disabled; check before using them.
Errors across transports
Go operations return ordinary errors. Use errors.Is for a sentinel and errors.As for *domain.AuthError; backend errors may instead wrap their original cause. Do not send raw backend errors to clients.
The built-in HTTP handlers and middleware use {"error": code, "message": message} with Content-Type: application/json. CSRF rejection bodies now use this envelope too, replacing their former plain-text bodies. Match the code rather than parsing the message. OAuth callback failures remain browser redirects with an error query parameter. Router-generated errors and your own handlers are outside this contract. See Error Handling for codes and statuses.
Frontend authentication outcomes
Successful HTTP authentication flows return one step instead of requires*
booleans. OAuth callbacks use the same names in query parameters. See
Authentication outcomes for the decision table,
response fields and email-verification continuation behavior. Go service result
fields and the HTTP error envelope are unchanged.
Authentication policy
Password and OAuth login apply the same global and per-account email-2FA requirements. Admin OAuth also applies the admin default. Authentication and signup methods are independently selected with WithAuthentication and WithRegistration. The policy matrix specifies verification and second-factor behavior for each path.
Optional application authorization
WithAppPermissions(AppPermissionsConfig{Enable:true}) exposes
Services.AppPermissions. It is nil when disabled. Enabled mode authorizes
through the account's single appRoleId. Enabled-mode returned users have an
empty Go User.Role and omit the account role JSON field. Disabled mode
supports the fixed admin and user roles and returns role in account JSON.
Organization membership roles remain separate.
App permission operations take a trusted api.AppPermissionActor containing
the authenticated user ID and current session ID. Existing administrative
inputs use ActorSessionID alongside their actor/admin ID. Direct service
calls enforce the action key and required session assurance too. For stats,
use Admin.GetStats(ctx, api.GetAdminStatsInput{ActorID: id, ActorSessionID: sessionID}).
It replaces both GetStats(ctx, id) and GetStatsForActor; the permission and
required assurance checks are unchanged. Obtain these IDs from authenticated
server context, never a client request body.
Only protected administrators require local second-factor verification by
default, including OAuth login. Every other role follows the user MFA policy,
even with delegated library grants. Custom operators use ordinary login; admin
login remains reserved for protected administrators. See the
authentication policy matrix.
Use AppPermissions.RequirePermission for backend checks and
Auth.RequireAppPermission after authentication on consumer HTTP routes.
Self-access responses are informational and do not authorize writes.
See App roles and permissions for initialization, selective library provisioning, business definitions, revisions and delegation limits. Use the canonical schema; startup installs no permissions.
Auth migration ownership
Auth owns its fixed tables and the goauth_migrations journal. Run the matching
goauth migrate independently of application/ORM migrations, before starting
services and workers. Keep app fields in extension tables with foreign keys;
do not include auth SQL in application desired schemas or copied host migrations.
New checks database connectivity and recorded migration compatibility before
starting workers, including for borrowed handles. It never executes DDL. See
Migrations for adoption and recovery.
Startup and shutdown
NewConfig validates configuration and starts no background work. New clones and revalidates it, opens or adopts the database, constructs services, and starts the configured workers. Depending on configuration, these include maintenance, recovery-email delivery, audit delivery/retention, and cleanup for the library's in-memory rate-limit stores. Create an Auth for the application's lifetime, rather than per request.
Stop accepting work and wait for HTTP requests and direct service callers to finish before calling Auth.Close(). Close does not stop or drain your HTTP server. Call it once as part of orderly application shutdown, then release resources you supplied.
| Resource | Ownership and shutdown |
|---|---|
Database opened from DatabaseConfig.URL | Auth.Close closes it. |
Supplied DatabaseConfig.DB (*sql.DB) | Remains open; the caller closes it after Auth.Close. |
Supplied DatabaseConfig.Pool (*pgxpool.Pool) | Auth.Close closes its SQL adapter, leaving the caller's pool open. |
| Default route rate-limit store and internal 2FA notification store | Created and closed by go-auth. |
| Supplied rate-limit store | Remains caller-owned, including its cleanup lifecycle. |
| Supplied mailer, audit sinks, and OAuth providers | Their lifecycle remains caller-owned; Auth.Close does not close them. |
Close stops the recovery worker and maintenance before stopping audit work, then releases the owned stores and database. The audit stop receives a five-second timeout. There is no deadline for the whole Close call and it returns no error; custom dependencies must honor cancellation so workers can exit. A stop timeout is not a guarantee that external audit delivery finished: undelivered durable outbox records remain for later processing. If you use an asynchronous mailer, stop/drain that caller-owned mailer after Auth.Close.
Browser and CSRF migration
This is a breaking pre-v1 browser API change. Replace WithCookie(...) with WithBrowser(BrowserConfig{APIURL: ..., AllowedOrigins: ..., Cookies: ...}); move the origins out of SecurityConfig. Keep password policy, email-link policy, and pepper timestamps in WithSecurity, and lifetimes in WithSession.
Remove DisableCSRFToken, AllowMissingCSRFHeaders, CSRFToken, and their token overrides; there are no replacement bypasses. Remove TwoFactorConfig.DisableChallengeBinding and move ChallengeCookieName to BrowserConfig.Cookies.ChallengeName. Keep cookie domains empty for normal subdomain deployments.
Update clients to read 200 {"token":"..."} from /auth/csrf-token, send credentials and the token header, and consume X-CSRF-Token replacements. Do not read the HttpOnly CSRF cookie. Custom cookie helpers now require the request and return errors: SetSessionCookies(w, r, sessionID, sessionToken, refreshToken), ClearSessionCookies(w, r), and RotateCSRFToken(w, r). Issuing authentication cookies includes CSRF rotation; do not issue it separately. api.OAuthCallbackResult.SessionID supplies the stable ID for custom OAuth transports.
The low-level middleware dependency struct is not an application configuration API. The facade wires session identity resolution and validated snapshot reuse; custom HTTP routes should use Auth.RequireCSRF and Auth.RequireAuth. Identity lookup for CSRF does not authorize expired, revoked, banned, or otherwise invalid sessions.
Custom admin HTTP transports must select admin consistently through the request header, including when calling request-aware cookie helpers with credentials returned by AdminLogin. A helper rejects credentials from a different context instead of writing them into the wrong cookie set. Pure Go service calls have no browser header; admin login returns admin credentials, 2FA verification restores the challenge's context, and refresh preserves the original credential context.
For direct browser access to custom API routes, register each path's OPTIONS handler with CORS outermost, before authentication and rate limiting. A method-specific POST/PATCH/DELETE route alone does not handle preflight. Auth.Mount already registers OPTIONS for go-auth's own paths; consumers own preflight for their application paths.
Update admin clients to send X-GoAuth-Session: admin on every request, including bootstrap and 2FA follow-ups. Admin login always issues the admin context's credential cookies. Old shared admin cookies are not reused as admin credentials; sign in again. App cookie names remain unchanged. Optional Cookies.AdminName, AdminRefreshName, and AdminChallengeName customize the independent admin names; transport policy remains common to both contexts. The bundled panel handles selection automatically, with VITE_API_URL pointing directly at the backend.
Old CSRF tokens are incompatible and safe bootstrap requests replace them. No database schema migration is needed. Review HTTPS/HTTP development settings and direct API deployment using Deployment.
Session access checks and verification-only identity lookup
Use Auth.GetSession or Auth.CheckSession when authorizing ordinary
programmatic access. They apply the current email-verification requirement as
well as session liveness and account status. GetSession returns
email_not_verified and CheckSession returns false for a restricted account.
Auth.RequireAuth applies the same verification requirement to custom protected
HTTP routes.
The low-level Services().Session.ValidateWithUser checks session identity and
liveness without imposing email verification. It remains available to build
verification, minimal-status and logout flows. Its successful return is not
permission to bypass verification for ordinary protected operations. The
built-in /auth/me exposes only minimal verification status while restricted;
/auth/refresh rotates cookies but similarly omits the session body.