API Endpoints
All paths are prefixed with your BasePath (default: /auth). Endpoints depend on which modules you register. These examples describe the current checkout.…
All paths are prefixed with your BasePath (default: /auth). Endpoints depend on which modules you register. These examples describe the current checkout. Timestamps and token strings are illustrative; fields omitted by a DTO may be absent. The OpenAPI document generated from your initialized instance is available through the Auth API when enabled.
Most JSON responses use the standard APIResponse envelope. OAuth redirects and the raw JWKS response are exceptions:
{
"data": { ... },
"message": "optional message"
}Handler errors use the response status and structured fields inside data. Runtime guards (such as unavailable authentication or admission exhaustion) can return plain-text 503 responses:
{
"data": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
}List endpoints return a ListResponse inside data:
{
"data": {
"list": [ ... ],
"sort_field": "created_at",
"sort_dir": "desc",
"total": 42
}
}Core Module (auto-registered)
The core module is always present. It handles user registration, profile management, password flows, and verification. Login/logout/refresh are provided by either the Session or Stateless module.
POST /signup — Register a new user
Request:
{
"email": "user@example.com",
"password": "SecurePassword123!",
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"username": "johndoe",
"phone_number": "+1234567890"
}Only email (or username) and password are required. The other fields are optional.
Success Response 201 Created:
{
"data": {
"user": {
"id": "01961abc-...",
"email": "user@example.com",
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"username": "johndoe",
"phone_number": "+1234567890",
"active": true,
"email_verified": false,
"phone_number_verified": false,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": null
},
"message": "User registered successfully"
}
}Signup only creates the user; it does not issue credentials. Complete any required contact verification, then authenticate using /login or another enabled method. The default password policy accepts 8–72 bytes without trimming or normalization.
Error 400: validation errors (email already exists, password policy violation, etc.)
GET /me — Get current user Auth
Headers: Authorization: Bearer <access_token>
Response 200:
{
"data": {
"id": "01961abc-...",
"email": "user@example.com",
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"username": "johndoe",
"avatar": "https://example.com/avatar.png",
"phone_number": "+1234567890",
"active": true,
"email_verified": true,
"phone_number_verified": false,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T00:00:00Z",
"last_login_at": "2025-01-20T10:30:00Z"
}
}PUT /profile — Update profile Auth
Headers: Authorization: Bearer <access_token>
Request:
{
"name": "Jane Doe",
"phone": "+9876543210",
"avatar": "https://example.com/new-avatar.png"
}All fields are optional. phone is validated in E.164 format. avatar must be a valid URL.
Response 200:
{
"data": {
"id": "01961abc-...",
"email": "user@example.com",
"name": "Jane Doe",
"phone_number": "+9876543210",
"avatar": "https://example.com/new-avatar.png",
"active": true,
"email_verified": true,
"phone_number_verified": false,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-20T12:00:00Z"
}
}PUT /change-password — Change password Auth
Headers: Authorization: Bearer <access_token>
Request:
{
"old_password": "OldPassword123!",
"new_password": "NewPassword456!"
}Both fields are required. New password must be at least 8 characters and different from the old password.
Response 200:
{
"data": {
"message": "Password changed successfully"
}
}POST /is-available — Check availability
Check whether an email, username, or phone number is already taken. Provide exactly one of the three fields.
Request (email):
{"email": "newuser@example.com"}Request (username):
{"username": "johndoe"}Request (phone):
{"phone": "+1234567890"}Response 200:
{
"data": {
"available": true,
"field": "email",
"message": "email is available"
}
}POST /forgot-password — Request password reset
Request:
{"email": "user@example.com"}Or by phone:
{"phone": "+1234567890"}Response 200:
{
"data": {
"message": "Password reset email sent"
}
}POST /reset-password — Reset password with token or code
Request (token-based, from email link):
{
"token": "reset-token-from-email",
"email": "user@example.com",
"new_password": "NewPassword456!"
}Request (code-based, from SMS):
{
"code": "123456",
"phone": "+1234567890",
"new_password": "NewPassword456!"
}Either token or code is required, along with the corresponding email or phone.
Response 200:
{
"data": {
"message": "Password reset successfully"
}
}POST /send-verification-email — Send verification email
Request:
{"email": "user@example.com"}Response 200:
{
"data": {
"message": "Verification email sent"
}
}POST /resend-verification-email — Resend verification email
Request:
{"email": "user@example.com"}Same as /send-verification-email but intended for resend flows.
GET /verify-email?token=...&email=... — Verify email
Called from the email verification link. Redirects to FrontendConfig.VerifyEmailCallbackPath with ?status=success or ?status=error.
Falls back to a JSON response if FrontendConfig is not set.
POST /send-verification-phone — Send phone verification SMS
Request:
{"phone": "+1234567890"}Requires the Notification module with an SMS sender configured.
POST /resend-verification-phone — Resend phone verification SMS
Request:
{"phone": "+1234567890"}POST /verify-phone — Verify phone number
Request:
{
"phone": "+1234567890",
"code": "123456"
}Code must be exactly 6 digits.
Response 200:
{
"data": {
"message": "Phone number verified successfully"
}
}Session Module
Requires session.New(...). Mutually exclusive with the Stateless module.
POST /login — Login (create session)
Request:
{
"email": "user@example.com",
"password": "SecurePassword123!"
}Or login by username:
{
"username": "johndoe",
"password": "SecurePassword123!"
}Success Response 200:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"expires_in": 900,
"user": {
"id": "01961abc-...",
"email": "user@example.com",
"name": "John Doe",
"active": true,
"email_verified": true,
"phone_number_verified": false,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T00:00:00Z"
},
"message": "Login successful"
}
}If the Two-Factor module is registered and the user has 2FA enabled, the response will contain a challenges array instead of tokens:
{
"data": {
"user": { ... },
"challenges": [
{
"type": "2fa",
"data": {
"requires_2fa": true,
"temp_token": "eyJhbGciOi...",
"user_id": "01961abc-...",
"message": "Two-factor authentication required. Please provide your 2FA code."
}
}
],
"message": "Login requires additional verification"
}
}POST /logout — Logout (end session) Auth
Headers: Authorization: Bearer <access_token>
Ends the current session.
Response 200:
{
"data": {
"message": "Logged out successfully"
}
}POST /refresh — Refresh tokens
Request:
{"refresh_token": "eyJhbGciOi..."}Response 200:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"expires_in": 900,
"message": "Token refreshed successfully"
}
}GET /sessions — List sessions Auth
Headers: Authorization: Bearer <access_token>
Returns all active sessions for the authenticated user.
Response 200:
{
"data": [
{
"id": "sess-01961abc-...",
"user_agent": "Mozilla/5.0...",
"ip_address": "192.168.1.1",
"created_at": "2025-01-01T00:00:00Z",
"expires_at": "2025-01-02T00:00:00Z",
"current": true
},
{
"id": "sess-01961def-...",
"user_agent": "Chrome/120...",
"ip_address": "10.0.0.1",
"created_at": "2025-01-01T12:00:00Z",
"expires_at": "2025-01-02T12:00:00Z",
"current": false
}
]
}GET /sessions/{session_id} — Get session Auth
Headers: Authorization: Bearer <access_token>
Returns details for a specific session.
Response 200:
{
"data": {
"id": "sess-01961abc-...",
"user_agent": "Mozilla/5.0...",
"ip_address": "192.168.1.1",
"created_at": "2025-01-01T00:00:00Z",
"expires_at": "2025-01-02T00:00:00Z",
"current": false
}
}DELETE /sessions/{session_id} — Revoke session Auth
Headers: Authorization: Bearer <access_token>
Revokes a specific session by ID.
Response 200:
{
"data": {
"message": "Session revoked successfully"
}
}DELETE /sessions — Revoke all sessions Auth
Headers: Authorization: Bearer <access_token>
Revokes all of the user’s sessions, including the current session.
Response 200:
{
"data": {
"message": "All other sessions revoked"
}
}Stateless Module
Default if no auth module is registered. Or register explicitly with stateless.New(...). Mutually exclusive with Session module.
POST /login — Login (JWT tokens)
Request:
{
"email": "user@example.com",
"password": "SecurePassword123!"
}Or login by username:
{
"username": "johndoe",
"password": "SecurePassword123!"
}Success Response 200:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"expires_in": 900,
"user": {
"id": "01961abc-...",
"email": "user@example.com",
"name": "John Doe",
"active": true,
"email_verified": true,
"phone_number_verified": false,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T00:00:00Z"
},
"message": "Login successful"
}
}If the Two-Factor module is registered and the user has 2FA enabled, the response includes challenges instead (see Session module login for shape).
POST /logout — Revoke user credentials Auth
Headers: Authorization: Bearer <access_token>
Increments the user’s token version and deletes their stored token records. This invalidates existing credentials for that user across devices; it is not a single-token blacklist.
Response 200:
{
"data": {
"message": "Logged out successfully"
}
}POST /refresh — Refresh tokens
Request:
{"refresh_token": "eyJhbGciOi..."}Returns new access and refresh credentials after conditionally consuming the stored nonce. Rotation is always applied by the current service, including when the legacy RefreshTokenRotation flag is false. Reusing the old refresh credential fails.
Response 200:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"expires_in": 900,
"message": "Token refreshed successfully"
}
}Two-Factor Module
Requires twofactor.New(...).
POST /2fa/setup — Start 2FA setup Auth
Headers: Authorization: Bearer <access_token>
Generates a TOTP secret, QR code URL, and backup codes. The user must verify with a code before 2FA is enabled.
Response 200:
{
"data": {
"secret": "JBSWY3DPEHPK3PXP",
"qr_url": "otpauth://totp/GoAuth:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=GoAuth",
"backup_codes": ["A1B2C3D4", "E5F6G7H8", "..."],
"message": "Scan the QR code with your authenticator app, then verify with a code to enable 2FA"
}
}POST /2fa/verify — Verify and enable 2FA Auth
Headers: Authorization: Bearer <access_token>
Verifies a TOTP code against the pending secret and enables 2FA.
Request:
{"code": "123456"}Response 200:
{
"data": {
"message": "Two-factor authentication enabled successfully"
}
}POST /2fa/disable — Disable 2FA Auth
Headers: Authorization: Bearer <access_token>
Requires a current TOTP code for verification before disabling.
Request:
{"code": "123456"}Response 200:
{
"data": {
"message": "Two-factor authentication disabled successfully"
}
}GET /2fa/status — Get 2FA status Auth
Headers: Authorization: Bearer <access_token>
Response 200:
{
"data": {
"enabled": true,
"method": "totp"
}
}When 2FA is not enabled, method is an empty string.
POST /2fa/verify-login — Complete login with 2FA
Called after login returns a challenges array with type: "2fa". No auth header required -- uses temp_token from the challenge data.
Request:
{
"temp_token": "eyJhbGciOi...",
"code": "123456"
}The code can be a TOTP code or a backup code. temp_token is required and is bound to a persisted, expiring 2fa_pending continuation. Attempts are counted; successful completion consumes the continuation atomically. It cannot be used as an access token.
Success Response 200:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"user": {
"id": "01961abc-...",
"email": "user@example.com"
}
}
}OAuth Module
Requires oauth.New(...) with configured providers and APIURL set in config.
GET /oauth/{provider} — Start OAuth flow
Redirects the user to the OAuth provider's consent screen.
Built-in provider types: google, github, microsoft, discord. The {provider} segment is a configured connection ID; a named connection can select a type with OAuthProviderConfig.Type. Microsoft requires an explicit tenant UUID.
GET /oauth/{provider}/callback — OAuth callback
The callback requires the matching one-use state and browser binding cookie from the start endpoint. It verifies the provider identity, applies account-linking policy, and runs shared completion. A 2FA challenge returns JSON without redirecting or issuing final credentials. A completed flow:
- Redirects to
DefaultRedirectURL; cookie-only mode omits token fragments, while bearer/both modes include them. - Returns JSON when no redirect URL is configured:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"expires_in": 900,
"token_type": "Bearer",
"user": {
"id": "01961abc-...",
"email": "user@gmail.com",
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"avatar": "https://lh3.googleusercontent.com/...",
"email_verified": true,
"active": true,
"created_at": "2025-01-01T00:00:00Z"
},
"is_new_user": false,
"provider": "google"
}
}DELETE /oauth/{provider} — Unlink provider Auth
Headers: Authorization: Bearer <access_token>
Unlinks an OAuth provider from the user's account.
Response 200:
{
"data": {
"message": "Provider unlinked successfully"
}
}GET /oauth/providers — List available providers
Response 200:
{
"data": {
"providers": [
{"name": "google", "enabled": true},
{"name": "github", "enabled": true}
]
}
}GET /oauth/linked — List linked providers Auth
Headers: Authorization: Bearer <access_token>
Response 200:
{
"data": {
"providers": ["google", "github"]
}
}Admin Module
Requires admin.New(...). All routes require auth + admin middleware.
GET /admin/users — List users Admin
Headers: Authorization: Bearer <access_token>
Query Parameters:
| Param | Default | Description |
|---|---|---|
offset | 0 | Number of records to skip |
limit | 20 | Max records to return (max 100) |
sort_field | created_at | Sort by: created_at, email, username, name |
sort_dir | desc | Sort direction: asc or desc |
query | Search by name, email, or username |
Response 200:
{
"data": {
"list": [
{
"id": "01961abc-...",
"email": "user@example.com",
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"username": "johndoe",
"avatar": "",
"phone_number": "+1234567890",
"active": true,
"email_verified": true,
"phone_number_verified": false,
"is_super_admin": false,
"token_version": 1,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T00:00:00Z",
"last_login_at": "2025-01-20T10:30:00Z"
}
],
"sort_field": "created_at",
"sort_dir": "desc",
"total": 42
}
}GET /admin/users/{id} — Get user Admin
Headers: Authorization: Bearer <access_token>
Returns full user details including admin-only fields (is_super_admin, token_version).
Response 200:
{
"data": {
"id": "01961abc-...",
"email": "user@example.com",
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"username": "johndoe",
"avatar": "",
"phone_number": "+1234567890",
"active": true,
"email_verified": true,
"phone_number_verified": false,
"is_super_admin": false,
"token_version": 1,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T00:00:00Z",
"last_login_at": "2025-01-20T10:30:00Z"
}
}PUT /admin/users/{id} — Update user Admin
Headers: Authorization: Bearer <access_token>
All fields are optional (use JSON null or omit). At least one field must be provided.
Request:
{
"first_name": "Jane",
"last_name": "Smith",
"name": "Jane Smith",
"email": "jane@example.com",
"username": "janesmith",
"avatar": "https://example.com/avatar.png",
"phone_number": "+9876543210",
"active": false,
"email_verified": true,
"phone_number_verified": false,
"is_super_admin": false
}Response 200:
{
"data": {
"message": "User updated successfully"
}
}DELETE /admin/users/{id} — Delete user Admin
Headers: Authorization: Bearer <access_token>
Permanently deletes the user.
Response 200:
{
"data": {
"message": "User deleted successfully"
}
}Audit Module
Requires audit.New(...).
User self-service routes
GET /me/audit — My audit logs Auth
Headers: Authorization: Bearer <access_token>
Query Parameters:
| Param | Default | Description |
|---|---|---|
offset | 0 | Number of records to skip |
limit | 20 | Max records to return (max 100) |
sort_field | created_at | Sort by: created_at, action, severity, actor_id |
sort_dir | desc | Sort direction: asc or desc |
Response 200:
{
"data": {
"list": [
{
"id": "01961abc-...",
"action": "auth.login.success",
"actor_id": "01961abc-...",
"actor_type": "user",
"severity": "info",
"details": "Action: auth.login.success",
"metadata": "{...}",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0...",
"created_at": "2025-01-20T10:30:00Z"
}
],
"sort_field": "created_at",
"sort_dir": "desc",
"total": 15
}
}GET /me/audit/logins — My login history Auth
Headers: Authorization: Bearer <access_token>
Returns login-related audit events for the authenticated user. Same query parameters and response shape as /me/audit.
GET /me/audit/changes — My profile changes Auth
Headers: Authorization: Bearer <access_token>
Returns profile update events. Same query parameters and response shape as /me/audit.
GET /me/audit/security — My security events Auth
Headers: Authorization: Bearer <access_token>
Returns security-related events (2FA changes, password changes, etc.). Same query parameters and response shape as /me/audit.
Admin audit routes
GET /admin/audit — All audit logs Admin
Headers: Authorization: Bearer <access_token>
Returns all audit logs across all users. Same query parameters and response shape as /me/audit.
GET /admin/audit/users/{id} — User audit logs Admin
Headers: Authorization: Bearer <access_token>
Returns audit logs for a specific user. Same query parameters and response shape as /me/audit.
GET /admin/audit/actions/{action} — Logs by action Admin
Headers: Authorization: Bearer <access_token>
Returns audit logs filtered by action type (e.g., auth.login.success, auth.password.changed). Same query parameters and response shape as /me/audit.
POST /admin/audit/cleanup — Cleanup old logs Admin
Headers: Authorization: Bearer <access_token>
Triggers manual cleanup of old audit logs based on retention policy.
Response 200:
{
"data": {
"message": "Audit logs cleaned up successfully"
}
}Magic Link Module
Requires magiclink.New(...) and the Notification module.
POST /magic-link/send — Send magic link
Request:
{"email": "user@example.com"}Sends an email with a magic link. If AutoRegister: true, creates a new user if the email does not exist.
Response 200:
{
"data": {
"message": "Magic link sent"
}
}GET /magic-link/verify?token=... — Verify magic link
Called from the email link. Verifies the token and either:
- Redirects to
CallbackURLwith tokens in the URL fragment:#access_token=xxx&refresh_token=xxx - Or returns a JSON auth response if no callback URL is configured
POST /magic-link/verify-code — Verify by code
For mobile apps -- verifies using the numeric code from the email.
Request:
{"email": "user@example.com", "code": "123456"}Response 200:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"expires_in": 900,
"user": {
"id": "01961abc-...",
"email": "user@example.com"
}
}
}POST /magic-link/resend — Resend magic link
Request:
{"email": "user@example.com"}Response 200:
{
"data": {
"message": "Magic link sent"
}
}CSRF Module
Requires csrf.New(...).
GET /csrf-token — Get CSRF token
Response 200:
{
"data": {
"csrf_token": "hmac-signed-token"
}
}Also sets a cookie (default name: __goauth_csrf) with HttpOnly: false so client JavaScript can read it.
Include the token in the X-CSRF-Token header for state-changing requests (POST, PUT, DELETE, PATCH). The CSRF middleware validates the header token against the cookie automatically.
Invitation Module
Standalone platform invitations. For org-scoped invitations, see the Organization Module below.
Authenticated routes
POST /invitations — Send invitation Auth
Headers: Authorization: Bearer <access_token>
Request:
{
"email": "invitee@example.com",
"purpose": "beta",
"metadata": "{\"referral_code\": \"ABC123\"}"
}purpose defaults to "platform". metadata is optional.
Response 201:
{
"data": {
"id": "01961abc-...",
"email": "invitee@example.com",
"purpose": "beta",
"inviter_id": "01961def-...",
"status": "pending",
"expires_at": "2025-01-09T00:00:00Z",
"created_at": "2025-01-02T00:00:00Z"
}
}GET /invitations — List sent invitations Auth
Lists invitations sent by the authenticated user. Supports ?status=pending&purpose=beta&limit=20&offset=0&sort_field=created_at&sort_dir=desc.
GET /invitations/my — My pending invitations Auth
Returns pending invitations for the authenticated user's email.
DELETE /invitations/{invId} — Cancel invitation Auth
Only the inviter can cancel their own invitations.
Public routes (no auth required)
POST /invitations/accept — Accept invitation Public
No authentication required. The invitation token is the authorization. If the invited email has no account, provide name and password to create one.
Request:
{
"token": "invitation-token-from-email",
"name": "John Doe",
"password": "securepassword123"
}name and password are only required when the invited email has no existing account.
Response 200:
{
"data": {
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"user": {
"id": "01961abc-...",
"email": "invitee@example.com",
"name": "John Doe",
"email_verified": true
},
"is_new_user": true
}
}The invitation mutation commits before authentication completion. The response may contain challenges instead of final credentials when the account requires 2FA. Consume that challenge response rather than retrying the already-accepted invitation.
POST /invitations/decline — Decline invitation Public
No authentication required.
Request:
{"token": "invitation-token-from-email"}Response 200:
{"data": null}Organization Module
Requires organization.New(...).
User-level routes (no org context)
POST /org — Create organization Auth
Headers: Authorization: Bearer <access_token>
Request:
{
"name": "Acme Inc",
"slug": "acme-inc"
}slug is optional -- auto-generated from name if omitted. Must be lowercase alphanumeric with hyphens, 2-64 characters.
Response 201:
{
"data": {
"id": "01961abc-...",
"name": "Acme Inc",
"slug": "acme-inc",
"owner_id": "01961abc-...",
"active": true,
"created_at": "2025-01-01T00:00:00Z"
}
}GET /org/my — List my organizations Auth
Headers: Authorization: Bearer <access_token>
Returns all organizations the authenticated user is a member of.
Response 200:
{
"data": [
{
"id": "01961abc-...",
"name": "Acme Inc",
"slug": "acme-inc",
"owner_id": "01961abc-...",
"active": true,
"created_at": "2025-01-01T00:00:00Z"
}
]
}POST /org/switch — Switch active organization Auth
Headers: Authorization: Bearer <access_token>
Requires a verified principal and current membership in an active organization. Shared completion preserves the original authentication time and methods, and returns new credentials with active_org_id and org_role. No first organization is selected automatically and no org_memberships claim is added. The current switch handler rejects results that require another challenge.
Request:
{"org_id": "01961abc-..."}Response 200:
{
"data": {
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi..."
}
}GET /org/my/invitations — My pending invitations Auth
Headers: Authorization: Bearer <access_token>
Returns pending invitations for the authenticated user.
Response 200:
{
"data": [
{
"id": "01961abc-...",
"org_id": "01961def-...",
"email": "user@example.com",
"role": "member",
"inviter_id": "01961ghi-...",
"status": "pending",
"expires_at": "2025-01-08T00:00:00Z",
"created_at": "2025-01-01T00:00:00Z"
}
]
}POST /org/invitations/accept — Accept org invitation Public
No authentication required. The invitation token is the authorization. If the invited email has no account, provide name and password to create one.
Request:
{
"token": "invitation-token-from-email",
"name": "John Doe",
"password": "securepassword123"
}name and password are only required when the invited email has no existing account.
Response 200:
{
"data": {
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"user": {
"id": "01961abc-...",
"email": "john@example.com",
"name": "John Doe",
"email_verified": true
},
"member": {
"id": "01961def-...",
"org_id": "01961ghi-...",
"user_id": "01961abc-...",
"role": "member",
"joined_at": "2025-01-02T00:00:00Z"
},
"is_new_user": true
}
}The invitation mutation commits before authentication completion. The response may contain challenges instead of final credentials when the account requires 2FA. Consume that challenge response rather than retrying the already-accepted invitation.
POST /org/invitations/decline — Decline org invitation Public
No authentication required.
Request:
{"token": "invitation-token-from-email"}Response 200:
{
"data": null
}Org-scoped routes (require org membership)
These routes require the org auth middleware, which verifies the user is a member of the specified organization.
GET /org/{orgId} — Get organization Auth Org
Headers: Authorization: Bearer <access_token>
Response 200:
{
"data": {
"id": "01961abc-...",
"name": "Acme Inc",
"slug": "acme-inc",
"owner_id": "01961abc-...",
"logo_url": "https://example.com/logo.png",
"metadata": "{\"plan\": \"pro\"}",
"active": true,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T00:00:00Z"
}
}PUT /org/{orgId} — Update organization Auth Org
Headers: Authorization: Bearer <access_token>
At least one field must be provided.
Request:
{
"name": "Acme Corp",
"logo_url": "https://example.com/new-logo.png",
"metadata": "{\"plan\": \"enterprise\"}"
}Response 200:
{
"data": {
"id": "01961abc-...",
"name": "Acme Corp",
"slug": "acme-inc",
"owner_id": "01961abc-...",
"logo_url": "https://example.com/new-logo.png",
"metadata": "{\"plan\": \"enterprise\"}",
"active": true,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-20T12:00:00Z"
}
}DELETE /org/{orgId} — Delete organization Auth Org
Headers: Authorization: Bearer <access_token>
Permanently deletes the organization. Requires owner role.
Response 200:
{
"data": {
"message": "Organization deleted"
}
}GET /org/{orgId}/members — List members Auth Org
Headers: Authorization: Bearer <access_token>
Response 200:
{
"data": [
{
"id": "01961abc-...",
"org_id": "01961def-...",
"user_id": "01961ghi-...",
"role": "owner",
"joined_at": "2025-01-01T00:00:00Z",
"user": {
"id": "01961ghi-...",
"name": "John Doe",
"email": "john@example.com",
"username": "johndoe",
"avatar": "",
"active": true,
"email_verified": true,
"created_at": "2025-01-01T00:00:00Z"
}
}
]
}GET /org/{orgId}/members/{userId} — Get member Auth Org
Headers: Authorization: Bearer <access_token>
Returns a specific member with their user details. Same shape as a single item in the list above.
PUT /org/{orgId}/members/{userId} — Update member role Auth Org
Headers: Authorization: Bearer <access_token>
Request:
{"role": "admin"}Valid roles: owner, admin, member.
Response 200:
{
"data": {
"message": "Member role updated"
}
}DELETE /org/{orgId}/members/{userId} — Remove member Auth Org
Headers: Authorization: Bearer <access_token>
Response 200:
{
"data": {
"message": "Member removed"
}
}POST /org/{orgId}/invite — Invite member Auth Org
Headers: Authorization: Bearer <access_token>
Request:
{
"email": "newmember@example.com",
"role": "member"
}role is optional (defaults to member). Valid values: owner, admin, member.
Response 201:
{
"data": {
"id": "01961abc-...",
"org_id": "01961def-...",
"email": "newmember@example.com",
"role": "member",
"inviter_id": "01961ghi-...",
"status": "pending",
"expires_at": "2025-01-08T00:00:00Z",
"created_at": "2025-01-01T00:00:00Z"
}
}GET /org/{orgId}/invitations — List invitations Auth Org
Headers: Authorization: Bearer <access_token>
Returns all invitations for the organization.
Response 200:
{
"data": [
{
"id": "01961abc-...",
"org_id": "01961def-...",
"email": "newmember@example.com",
"role": "member",
"inviter_id": "01961ghi-...",
"status": "pending",
"expires_at": "2025-01-08T00:00:00Z",
"created_at": "2025-01-01T00:00:00Z"
}
]
}DELETE /org/{orgId}/invitations/{invId} — Cancel invitation Auth Org
Headers: Authorization: Bearer <access_token>
Response 200:
{
"data": {
"message": "Invitation cancelled"
}
}Public Signing Keys
GET /.well-known/jwks.json — Public JWT verification keys
Returns a raw JSON Web Key Set with active and unexpired retained asymmetric public keys. It is not wrapped in data. An HS256-only configuration returns 404; HMAC secrets are never published. Successful responses set a one-hour cache lifetime. See Security for key rotation and custom-manager support.
Passkeys Module
Register passkeys.New(...) with an RP ID and allowed origins. See Passkeys for browser serialization and complete ceremony examples.
POST /passkeys/register/begin — Begin registration (Auth)
Returns data.challenge_token and data.public_key creation options. Authenticate the existing account, then call navigator.credentials.create with converted options.
POST /passkeys/register/finish — Finish registration (Auth)
Send challenge_token, optional name, and the actual browser-generated credential_response object. The record is consumed before verification, and a successful registration returns 201 with the new credential summary.
POST /passkeys/login/begin — Begin discoverable login
No email or user ID is required. Returns data.challenge_token and data.public_key request options for navigator.credentials.get.
POST /passkeys/login/finish — Finish login
Send challenge_token and the serialized assertion as credential_response. After origin, RP, identity, signature, and counter checks, shared completion returns credentials or challenges. Cookie transport follows AuthMode.
GET /passkeys — List owned passkeys (Auth)
Returns an array in data containing id, name, created_at, optional transports, and optional last_used_at. The current login path does not update last-used timestamps.
PUT /passkeys/{id} — Rename an owned passkey (Auth)
Send {"name":"Work laptop"}. Ownership is verified; success returns 200.
DELETE /passkeys/{id} — Delete an owned passkey (Auth)
Ownership is verified; success returns 200. Deleting a passkey does not delete the user account.
Passkey finish requests are one-use and purpose-bound. A failed finish needs a new begin request. credential_response must be a JSON object with base64url-encoded binary fields, not a stringified object. Current challenge expiry is capped at five minutes.
Authentication
Protected endpoints (marked with Auth) require:
Authorization: Bearer <access_token>This header is the bearer example. Cookie or both mode can accept the configured access cookie instead. Conflicting sources are rejected; a refresh token, pending 2FA continuation, or passkey challenge is not an access credential. Both strategies check current user/version state, and session and selected-organization state where applicable.
Get credentials from /login or another enabled method. Password, OAuth, magic-link, passkey, and invitation completion may return data.challenges instead of final tokens. Send the continuation’s temp_token and a factor code to /2fa/verify-login; a user ID alone is never sufficient. Refresh via /refresh using the configured transport. Cookie-only JSON responses still include token fields.
Admin endpoints (marked with Admin) additionally require the is_super_admin flag on the user account.
Org endpoints (marked with Org) additionally require the user to be a member of the specified organization.
Auth Lifecycle
GoAuth follows New → Use → Initialize → Shutdown. Configuration is copied at construction; configure the application before initialization rather than…
Error Reference
GoAuth uses structured error responses across all modules. Service and HTTP errors use types.GoAuthError, providing a machine-readable code, a…