Skip to content

Auth

OAuth 2.0 token lifecycle (Laravel Passport) and logout

Revoke the credential used on this request (log out this device)

POST /api/v1/logout

Requires authentication (Bearer token) and the X-Organization-Id header.

Revokes the Passport access token AND its refresh token (or deletes the Sanctum token) that authenticated this request. Revocation is immediate: the next API call with the same token returns 401, and refreshing with its refresh token returns 400 invalid_grant.

Organization API tokens are managed in the admin panel and are rejected here with 400 invalid_token_type. Calling logout twice with the same token yields 401 on the second call (the token is already revoked).

Responses

Status Description
200 Token revoked
400 Credential cannot be revoked via logout: invalid_token_type (organization API token — manage it in the admin panel) or unsupported_token_type (unrecognised credential).
401

Example request

curl -X POST "https://kworia.com/api/v1/logout" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "X-Organization-Id: {{organization_id}}" \
  -H "Accept: application/json"

Revoke ALL of the user's tokens (log out every device)

POST /api/v1/logout-all

Requires authentication (Bearer token) and the X-Organization-Id header.

Revokes every Passport access + refresh token and deletes every Sanctum personal access token belonging to the authenticated user — all devices and sessions. Same error contract as /api/v1/logout: organization API tokens get 400 invalid_token_type.

Responses

Status Description
200 All tokens revoked
400 Credential cannot be revoked via logout (invalid_token_type for organization API tokens, unsupported_token_type fallback).
401

Example request

curl -X POST "https://kworia.com/api/v1/logout-all" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "X-Organization-Id: {{organization_id}}" \
  -H "Accept: application/json"

Current authenticated user

GET /api/v1/user

Requires authentication (Bearer token) and the X-Organization-Id header.

Returns the authenticated user. There is no dedicated /me; this is the closest equivalent.

Responses

Status Description
200

Example request

curl -X GET "https://kworia.com/api/v1/user" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "X-Organization-Id: {{organization_id}}" \
  -H "Accept: application/json"

Start the OAuth2 Authorization Code + PKCE flow

GET /oauth/authorize

Open this URL in the system browser (NOT a WebView). Unauthenticated users are first redirected to /login and return here after signing in.

For first-party clients (the EncryptInvoice mobile app) the consent screen is auto-skipped: the browser is immediately redirected to redirect_uri with code and state. Third-party clients get a consent screen (HTTP 200) first.

PKCE is mandatory for public clients: send code_challenge (Base64url-encoded SHA-256 of a random 43–128 char code_verifier) and code_challenge_method=S256. Verify the returned state matches what you sent before exchanging the code.

Parameters

Name In Type Required Description
client_id query string yes Public client UUID.
redirect_uri query string yes Must be registered on the client. Mobile uses https:///mobile/oauth2/callback (App/Universal Link) or the custom scheme com.encryptinvoice.mobile://oauth2redirect.
response_type query string yes
scope query string no Space-separated scopes. Default mobile scope string: organizations:read invoices:read invoices:write invoices:send quotes:read quotes:write quotes:convert customers:read customers:write contacts:read contacts:write expenses:read expenses:write
state query string yes Opaque CSRF token; echoed back on the redirect. MUST be verified by the client.
code_challenge query string yes Base64url(SHA-256(code_verifier)), no padding.
code_challenge_method query string yes

Responses

Status Description
302 Redirect to redirect_uri with ?code=...&state=... (consent auto-skipped for first-party clients). Unauthenticated users are bounced to /login first and return here after authenticating.
200 Consent screen (HTML) — shown for third-party clients only.
401 invalid_client — e.g. unknown client_id or unregistered redirect_uri. Returned directly (NO redirect), so a misregistered URI can never receive codes.

Example request

curl -X GET "https://kworia.com/oauth/authorize" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "X-Organization-Id: {{organization_id}}" \
  -H "Accept: application/json"

Exchange an authorization code, refresh token, or (deprecated) password for tokens

POST /oauth/token

Token endpoint for all grants. Send application/x-www-form-urlencoded (JSON bodies are also accepted).

  • authorization_code + PKCE (PRIMARY, mobile): public client — send code_verifier, NO client_secret. The mobile app is a first-party client, so the issued token is user-scoped (organization_id stays NULL) — any organization_id sent is ignored, and the active org is chosen per request via the X-Organization-Id header (see Organization context). For non-first-party (org-scoped) clients, organization_id binds the token to that org; omitted = the user's primary org.
  • refresh_token: refresh tokens ROTATE — the old refresh token is revoked on use; always store the new pair. (Org-scoped only: re-send organization_id to keep a non-primary binding; user-scoped tokens stay NULL.)
  • password (DEPRECATED): legacy/web integrations only — mobile MUST use PKCE.

After logout (or any revocation) the access token immediately fails with 401 and its refresh token is revoked (refresh attempts return 400 invalid_grant).

Responses

Status Description
200 Token issued
400 invalid_grant — expired/used authorization code, bad code_verifier, or a rotated/revoked refresh token (incl. after logout). For the deprecated password grant: wrong credentials.
401 invalid_client — unknown client_id, or a confidential client without its secret. Public PKCE clients never send a client_secret.

Example request

curl -X POST "https://kworia.com/oauth/token" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "X-Organization-Id: {{organization_id}}" \
  -H "Accept: application/json"

We use cookies and privacy-respecting analytics

We use essential cookies for authentication and privacy-respecting analytics (self-hosted, respects Do Not Track). No advertising or third-party tracking. Learn more in our Privacy Policy