Skip to main content

Authentication API

AuthProxy exposes its public REST surface under /auth/v1/* and /auth/v2/*. All responses use the canonical ApiResponse<T> envelope (see Module integration).

This page is a curated map of the most useful endpoints with the actual DTO shapes. The authoritative, always-up-to-date reference is the built-in Swagger UI at https://yourdomain.com/docs/swagger/.

Route prefixes

PrefixControllerPurpose
/auth/v1/*AuthControllerLogin, sessions, logout, OTP/magic, MCP login
/auth/v1/social/{provider}SocialControllerOAuth providers (see OAuth Providers)
/auth/v1/identity/*IdentityControllerConfirming phone / email contacts after login
/auth/v1/keys/*KeysControllerKey registration / listing / deletion (FIDO2 + Ed25519 passkeys)
/auth/v1/file/*, /auth/v2/file/*FileUploadController, FileDownloadController, FileStreamControllerFile upload / download (see File Service)
/auth/v1/app/*AppControlleruser_app registration, webhook URL, scope management
/auth/v1/federated/*FederatedAuthControllerFederation v2 browser login
/auth/v1/notifications/*PushNotificationsControllerWeb Push subscription lifecycle
/auth/v1/subscribeNotificationsControllerSSE event stream
/auth/v1/stun/*StunDiagnosticsControllerEmbedded STUN diagnostics
/mcpMcpControllerJSON-RPC 2.0 MCP gateway (see MCP Protocol)
/private/v1/*PrivateControllerInter-module API guarded by X-API-Key

Response envelope

All endpoints return ApiResponse<T>:

{
"id": 0,
"result": { /* T or scalar; null on error */ },
"error": null
}

On error result is null and error carries an integer code and human-readable message:

{
"id": 0,
"result": null,
"error": { "code": 1101, "message": "The session is not in the system. Please, login." }
}

Error codes live in ApiGateError (ItBuild.Shared/Api/ApiGateError.cs). Selected entries (full list in source):

CodeNameMeaning
-11001GateErrorGateway / unexpected server failure
1001InvalidInputFormatMalformed input
1002AccessRateLimitRate limit exceeded
1003RouteNotFoundNo route_map match
1004ResourceNotFoundResource missing
1101SessionNotFoundNo active session
1102AccountNotFoundUser unknown
1103KeyNotFoundKey missing
1104KeysLimitReachedActiveKeysLimit exhausted
1106AccessDeniedAuthorisation refused
1108AppNotFounduser_app missing
1220ChallengeExpiredlogin_options challenge expired
1221DeviceIdMissingBrowser device GUID missing
1226AuthMethodDisabledMethod disabled in admin settings
1227TooManyAuthAttemptsOTP retry exhausted
1231InvalidSignatureEd25519 signature failed verification

JSON-RPC error codes returned by /mcp (-32xxx) live in MCP Protocol.

Health and feature discovery

GET /auth/v1/get_info

Returns project name, build version, UTC time, available login methods bitmask (LoginMethod flags AND-ed with active configuration), licensed domain, and PWA versions.

{
"id": 0,
"result": {
"projectName": "Acme",
"version": "2026.05.01",
"utcTime": "2026-05-18T20:00:00Z",
"loginMethods": 36879,
"domain": "app.acme.com",
"proxyNode": "p1",
"pwaVersions": { "apg": "1.5.0", "chat": "3.2.1" },
"dbVersion": "20260301-1"
},
"error": null
}

loginMethods is an OR of LoginMethod flag values currently enabled and configured. domain is the application domain. proxyNode ("p1", "p2", …) identifies which AuthProxy node serves the request in a multi-node deployment — distinct from domain; see Reverse Proxy.

Browser login

GET /auth/v1/login_options

Returns a fresh challenge plus FIDO2 options. The full result is the AuthOptions object (challengeId, challenge, rpId, timeout, userVerification, fido2Options, optional phone).

{
"id": 0,
"result": {
"challengeId": "638412345678901234",
"challenge": "base64url_24_bytes",
"rpId": "app.acme.com",
"timeout": 120000,
"userVerification": "preferred",
"fido2Options": null
},
"error": null
}

POST /auth/v1/login

Body (LoginIn):

FieldTypeWhen
challengeIdstring (long)always
credentialAuthenticatorAssertionRawResponseFIDO2 flow
signaturebase64url byte[]Ed25519 software passkey flow
publicKeybase64url byte[]password-derived key flow
rememberDevicebooloptional — true issues a sliding persistent cookie; false issues a browser-session cookie (no MaxAge)

Success returns ApiResponse.Success (result: "Success") and sets the sid=… cookie. Failure returns one of ChallengeExpired, DeviceIdMissing, InvalidSignature, AccountNotFound, AccessDenied, etc.

POST /auth/v1/login_phone / POST /auth/v1/login_email

OTP / magic-link login. Bodies are LoginPhoneIn / LoginEmailIn; both require the method to be enabled in EnabledLoginMethods. The challengeId from login_options is mandatory — the OTP code is bound to that challenge.

// login_phone (LoginPhoneIn)
{ "challengeId": "638412345678901234", "phone": "+44...", "code": "123456" }

// login_email (LoginEmailIn) — omit "code" on the first call to request a code / magic link
{ "challengeId": "638412345678901234", "email": "user@acme.com", "code": "123456" }

GET /auth/v1/magic?token={base64url}

One-click email magic-link consumer. AES-256 decrypts the token, finds the matching challenge, creates a session, and redirects to the application URL.

POST /auth/v1/app_login

Body (AppLoginIn):

{ "appId": "638412345678901234", "saveAccess": true, "sessionCode": null }

Establishes a cross-application session bound to a verified user_app. When called outside an active session, supply sessionCode issued by the originating app.

POST /auth/v1/logout

Closes the current sid session. No body.

POST /auth/v1/close_sessions

Body (CloseSessionsIn?, optional):

{ "id": "638412345678901234" }

id is the session timetick. It is [LongToString], so the field accepts both a JS-safe string (recommended) and a number. Without a body, closes all sessions of the current user except the current one. With id, closes only that session.

GET /auth/v1/sessions?current=false

Returns the list of UserSession rows for the authenticated user. Each row has utcCreate, id (session timetick as stringint64 is serialised as a JSON string to avoid JS precision loss), userAgent, ip, loginType, current, flags, utcLastAccess, domain (application domain), and proxyNode ("pN" node label) — there is no country field. current=true returns only the active session.

GET /auth/v1/login_log

Recent login history (UserLoginLog): success / failure, login method (loginType), ipLocation, device, IP, domain, and proxyNode ("pN" node that handled the attempt).

GET /auth/v1/sign_log

Recent Ed25519 signature verification history for the authenticated user (UserSignLog, up to 100 rows, newest first):

{
"utcTime": "2026-07-01T12:00:00Z",
"coreId": "638412345678901234",
"coreType": "Wallet",
"keyId": "638412345678901235",
"signData": "base64url_payload",
"signature": "base64url_sig",
"isValid": true,
"proxyNode": "p1"
}

Requires an active session (401 without). Also exposed via MCP builtin tool sign_log.

GET /auth/v1/register_options?code=&attempt=1

Issues a registration challenge bound to the supplied verification code (sent earlier via SMS / email). Returns the same AuthOptions shape as login_options.

GET /auth/v1/reset_password?phone=...

Triggers password-reset flow on the given phone (delivered through the Chat module).

MCP login

POST /auth/v1/mcp_login

Authenticates an AI agent / programmatic client via Ed25519 keys. Inputs are HTTP headers only (X-PublicKey, optional X-Timestamp + X-Signature for McpVerified, optional X-App-Id for McpApp). Returns McpLoginResult (sessionId, loginType, expiresUtc, appId).

Detailed semantics, signature payload, and capability mode: MCP Protocol.

Key management

All endpoints under /auth/v1/keys/* operate on user_key rows owned by the current session.

EndpointBodyPurpose
POST /auth/v1/keys/register_keyAuthIn (challengeId + OTP code + credentialNew for FIDO2 or publicKey for Ed25519)Enroll a new key. The OTP code (bound to challengeId + the user's crm_id) is required.
GET /auth/v1/keys/user_keysList the current user's active keys (returns List<UserKey>).
POST /auth/v1/keys/remove_keyRemoveKeyIn ({ "keyId": "..." })Soft-delete the key.
POST /auth/v1/keys/update_keyUpdateKeyIn ({ "keyId": "...", "mcpAccess": true, "cardDavAccess": false })Toggle McpAccess and CardDav flags.

UserKey shape:

{
"utcCreate": "2026-04-01T12:00:00Z",
"id": "638412345678901234",
"publicKey": "base64url_32_bytes",
"keyType": "FIDO2",
"current": true,
"mcpAccess": false,
"cardDavAccess": false
}

keyType is one of PasswordKey, UserKey, FIDO2, Unknown. FIDO2 entries store the credential id in publicKey and the CBOR public key in the underlying user_key.fido2_key column.

Identity verification (signed-in user)

After login the user can attach additional verified contacts:

EndpointBodyPurpose
POST /auth/v1/identity/confirm_phoneConfirmPhoneIn ({ "phone": "...", "code": "...", "attempt": 1 })Verify a phone contact via SMS code. Sets user_exid_flags.Verified.
POST /auth/v1/identity/confirm_emailConfirmEmailIn ({ "email": "...", "code": "..." })Verify an email contact.

See Verified Contacts for the resulting flag semantics.

OAuth providers

/auth/v1/social/{provider} — Google, Telegram, Discord, Apple, Facebook, GitHub, VKontakte. Each provider has a slightly different shape because the upstream protocol differs. See OAuth Providers for the full table with endpoints, methods, and configuration.

?link=true on Google / Discord / Facebook attaches the social account to the currently signed-in user instead of creating a new one.

Federation v2 (browser)

EndpointMethodPurpose
GET /auth/v1/federated/providersanonymousList partner providers (FederatedProviderOut[]).
GET /auth/v1/federated/start?providerAppId={long}anonymousSet partner state cookie and return the partner's login URL (FederatedStartOut.redirectUrl).
POST /auth/v1/federated/loginanonymous (state cookie required)Body FederatedLoginIn ({ "usr", "check" }) — partner-signed federation payload; returns FederatedLoginOut.returnUrl after minting the local sid.

Full security model and key exchange: Federation v2 — Browser Login.

Embedded STUN diagnostics

Auth-only diagnostics for the embedded STUN responder; see Embedded STUN for the runtime details.

POST /auth/v1/stun/report

Body is ClientStunDiagnosticReport:

{
"callId": "...",
"stage": "checking",
"connectionState": "connecting",
"iceConnectionState": "checking",
"iceGatheringState": "gathering",
"signalingState": "stable",
"localCandidateTypes": ["host", "srflx"],
"remoteCandidateTypes": ["host"],
"selectedLocalCandidateType": "srflx",
"selectedRemoteCandidateType": "host",
"currentRoundTripTime": 0.042,
"bytesSent": 12345,
"bytesReceived": 67890
}

Stores the current client's WebRTC diagnostics for the active call and returns the merged server snapshot (server counters + client report). Throttled per-session to avoid hot-loop spam.

GET /auth/v1/stun/current?callId={id}

Returns the merged client + server snapshot without writing anything new.

Inbound email

The SMTP listener is documented in Inbound Email Bridge. It exposes no public HTTP endpoints — inbound mail arrives over SMTP on port 25 and is dispatched into Chat through private/v1/inbound_email_send (X-API-Key, internal call between AuthProxy and Chat).

File operations

Two upload generations are supported; the v2 streaming flow is recommended for new clients.

v1 — buffered, part-based

EndpointPurpose
POST /auth/v1/file/init_uploadStart a session. Body: { "FileName", "FileSize", "CRC32", "ChatId", "Message", "LinkedMessageId" }. Returns the numeric uploadId.
POST /auth/v1/file/upload_part?uploadId=&chunkNumber=&chunkCrc32=Send one chunk (raw binary body). Final chunk returns { "chunkNumber", "fileId", "fileCode" }.
POST /auth/v1/file/cancel_upload?uploadId=Abort an active upload.
GET /auth/v1/file/{fileId} (HEAD also supported)Session-authenticated download. Optional ?part_number=N for 256 KB slices.

v2 — streaming

EndpointPurpose
POST /auth/v2/file/create_draft?fileName=&fileSize=&chatId=&message=&linkedMessageId=&crc32=Create a pending Chat file message. Returns "fileId:fileCode".
POST /auth/v2/file/stream_upload/{fileId}Stream upload with Range: bytes=... and raw binary body. HTTP 200 with empty body on success.
GET /auth/v2/file/{id_or_code} (HEAD, Range, ETag, If-None-Match, If-Match)Download by obfuscated code or numeric id (id-based downloads are gated by FileDownloadByIdEnabled; code-based downloads optionally require a session via FileDownloadAuthRequired).

Application management

EndpointMethodPurpose
GET /auth/v1/app/get_auth_param?appId={long}anonymousAuthentication parameters for an external user_app.
GET /auth/v1/app/get_app_info?appId={long}anonymousPublic app metadata.
GET /auth/v1/app/get_usr_appssessionThe current user's owned apps.
POST /auth/v1/app/upsert_usr_appsessionBody UserAppIn — create/update an app, set webhookUrl, currencies (array), netTypes (array). Does not manage user scopes.
POST /auth/v1/app/validate_webhook?appId={long}sessionPerform the webhook validation handshake.
GET /auth/v1/app/list_app_accesssessionList apps the current user has granted access to.
POST /auth/v1/app/revoke_app_accesssessionBody RevokeAppAccessIn — revoke a previously granted app access.
POST /auth/v1/app/delete_usr_app?appId={string}sessionDelete an owned app.
POST /auth/v1/app/verify_domain?appId={long}sessionRun DNS TXT verification.
GET /auth/v1/app/get_dns_instruction?appId={long}sessionReturns the DNS TXT record the operator must publish.
POST /auth/v1/app/federation_webhookservice-to-serviceFederation broadcast hook used between paired AuthProxy projects.

Push notifications

GET /auth/v1/subscribe remains the SSE real-time stream endpoint. Web Push subscription management uses the separate /auth/v1/notifications/* namespace.

For setup, platform limitations, and operational troubleshooting, see Browser Push Notifications.

Transport split

AuthProxy exposes two different notification transports:

  • SSEGET /auth/v1/subscribe
    • live browser tabs
    • reconnect with ?since=
    • capability mode for anonymous/private resources
  • Web Push/auth/v1/notifications/*
    • browser subscription lifecycle
    • service worker push delivery
    • background notifications and closed-tab delivery while the browser push infrastructure is still able to wake the service worker

Web Push does not replace SSE. A typical deployment uses both:

  • SSE for active sessions and live UI refresh
  • Web Push for background delivery when the browser is not actively rendering the app shell

Important browser caveat:

  • AuthProxy controls subscription storage and payload delivery, but it does not control whether a fully terminated browser process will be resumed by the OS/browser vendor push service.
  • Treat Web Push as the background transport, not as a guarantee that every fully closed desktop browser will wake up on every machine.

GET /auth/v1/notifications/public_key

Returns the VAPID public key (PushPublicKeyOut).

GET /auth/v1/notifications/device_status?...

Reports whether the current device has an active subscription and whether its VAPID key hash is current.

POST /auth/v1/notifications/subscribe

Body (PushSubscribeIn):

{
"endpoint": "https://fcm.googleapis.com/...",
"keys": { "p256dh": "base64", "auth": "base64" },
"origin": "https://app.acme.com",
"appScope": "root",
"deviceGuid": "00000000-0000-0000-0000-000000000001",
"flags": 2047,
"userApp": "0",
"userAgent": "Mozilla/5.0 ...",
"clientVersion": "2.1.0",
"locale": "en-US",
"metaJson": "{\"pwa\":\"chat\"}"
}

Upsert by endpoint hash; repeated subscribe with the same browser endpoint is treated as refresh, not conflict. origin must match the current AuthProxy/browser origin; userApp is optional and reserved for customer app targeting.

appScope is opaque to the platform; common values: root (shell), chat, wallet, core:<app>.

POST /auth/v1/notifications/unsubscribe

Body (PushUnsubscribeIn) — either { "endpoint": "..." } or { "origin", "appScope", "deviceGuid" }. Idempotent: the subscription is marked disabled and cleaned up later.

POST /auth/v1/notifications/test

Body { "title": "...", "body": "..." } — send a real test push through the configured transport. Does not exercise event generation, PushDispatcher event mapping, or SSE-suppression rules; use /ProxyAdmin/PushSubscriptions for those.

GET /auth/v1/notifications/mute_status and POST /auth/v1/notifications/set_mute

Manage per-user Do Not Disturb (PushMuteIn / PushMuteOut).

Customer admin checklist

To make browser push work end-to-end, customer admins must configure all of the following:

  1. Enable Web Push in AuthProxy configuration (WebPush.Enabled, VAPID keys, subject).
  2. Enable PushSending on exactly one AuthProxy node in phase 1 deployments.
  3. Deploy one canonical root sw.js on the application origin.
  4. Ensure frontend apps call public_key + subscribe after notification permission is granted.
  5. Keep SSE enabled as the live-tab transport; Web Push is the background channel, not a replacement for /auth/v1/subscribe.
  6. Use /ProxyAdmin/PushSubscriptions for transport diagnostics; a successful admin test there proves sender transport only, not the full business-event pipeline.

Interactive Documentation

AuthProxy includes built-in Swagger UI documentation available at:

https://yourdomain.com/docs/swagger/AuthProxyOpenApi.json

This provides complete API documentation with request/response examples and the ability to test endpoints directly.