2026-08-02 14:38:12 +00:00
|
|
|
# Authentication
|
|
|
|
|
|
|
|
|
|
This page documents the calls a client makes to sign in and stay signed in: what to send, what
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
comes back, and what each failure mode means. For the design behind these endpoints. Token
|
2026-08-02 14:38:12 +00:00
|
|
|
lifetimes, claims, why rotation is implemented by hand, why personal access tokens and organization
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
API keys exist alongside the JWT. See [Architecture: Authentication](../architecture/authentication.md),
|
2026-08-02 14:38:12 +00:00
|
|
|
which this page does not repeat or contradict. For a guided walkthrough of picking a sign-in path
|
|
|
|
|
locally, see [First sign-in](../getting-started/first-sign-in.md).
|
|
|
|
|
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
All three interactive sign-in paths below, Google OAuth, Google ID token, and both magic-link
|
|
|
|
|
verify endpoints, mint tokens through the same `OrgAwareRefreshToken.for_user_and_org()` helper and
|
2026-08-02 14:38:12 +00:00
|
|
|
are public: `permission_classes = []`, `authentication_classes = []`
|
|
|
|
|
(`backend/common/views/auth_views.py`). None of them require an existing token to call.
|
|
|
|
|
|
2026-08-15 18:29:44 +00:00
|
|
|
Everything on this page signs in somebody on **your** side of the org. A customer signing in to the
|
|
|
|
|
support portal uses a different set of endpoints and a different credential entirely, and neither
|
|
|
|
|
realm's token works against the other; see [Customer portal](customer-portal.md).
|
|
|
|
|
|
2026-08-02 14:38:12 +00:00
|
|
|
## Google OAuth (web)
|
|
|
|
|
|
|
|
|
|
`POST /api/auth/google/callback/` (`GoogleOAuthCallbackView`,
|
|
|
|
|
`backend/common/views/auth_views.py:52-198`) completes a PKCE authorization-code exchange for the
|
|
|
|
|
SvelteKit frontend.
|
|
|
|
|
|
|
|
|
|
Request body:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"code": "<authorization code from Google's redirect>",
|
|
|
|
|
"code_verifier": "<PKCE verifier the frontend generated>",
|
|
|
|
|
"redirect_uri": "<the redirect_uri used in the initial authorize request>"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2026-08-05 03:41:11 +00:00
|
|
|
`redirect_uri` is taken from this field, not from any server-side setting; there is no
|
|
|
|
|
server-side redirect-URI setting to configure. See
|
|
|
|
|
[Google OAuth](../self-hosting/google-oauth.md) for the settings that do matter.
|
2026-08-02 14:38:12 +00:00
|
|
|
The view exchanges these with Google, decodes the returned ID token, and rejects the sign-in
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
outright if Google's own `email_verified` claim is not `true`. An unverified Google address never
|
2026-08-02 14:38:12 +00:00
|
|
|
reaches the database. On success:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"access_token": "<jwt>",
|
|
|
|
|
"refresh_token": "<jwt>",
|
|
|
|
|
"user": {"id": "<uuid>", "email": "user@example.com"}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The token is minted with no org claim (`OrgAwareRefreshToken.for_user_and_org(user, None)`,
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
`:190`), both Google flows always hand back an org-less token (the magic-link flows differ; see
|
2026-08-02 14:38:12 +00:00
|
|
|
[Magic links](#magic-links) below), so the client's next call is normally
|
|
|
|
|
[switching organization](#switching-organization).
|
|
|
|
|
|
|
|
|
|
Failure responses: `400` for a missing `code`/`code_verifier`/`redirect_uri`, an invalid ID token,
|
|
|
|
|
a missing email, or an unverified email; `502` if Google's token endpoint could not be reached at
|
|
|
|
|
all (`requests.RequestException`, `:110-114`); `403` if the resulting user account is deactivated.
|
|
|
|
|
|
|
|
|
|
## Google ID token (mobile)
|
|
|
|
|
|
|
|
|
|
`POST /api/auth/google/` (`GoogleIdTokenView`, `backend/common/views/auth_views.py:201-319`) is the
|
|
|
|
|
mobile equivalent: the client has already obtained a Google ID token itself and hands it over
|
|
|
|
|
directly, rather than a code the server exchanges.
|
|
|
|
|
|
|
|
|
|
Request body:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"idToken": "<google id token>"}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The server verifies it against Google (`google.oauth2.id_token.verify_oauth2_token`, checked
|
|
|
|
|
against `GOOGLE_CLIENT_ID`) and applies the same `email_verified` check as the web flow. On success:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"JWTtoken": "<jwt access token>",
|
|
|
|
|
"refresh_token": "<jwt>",
|
|
|
|
|
"user": {"id": "<uuid>", "email": "user@example.com", "name": "user", "profileImage": ""},
|
|
|
|
|
"organizations": [{"id": "<uuid>", "name": "Acme", "role": "ADMIN"}]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
Note the access token field here is `JWTtoken`, not `access_token`. The two Google flows do not
|
2026-08-02 14:38:12 +00:00
|
|
|
share a response shape, so a client that supports both cannot read the access token the same way
|
|
|
|
|
from each. This response also includes `organizations` directly (the caller's existing memberships),
|
|
|
|
|
which the web callback response above does not; a mobile client can offer org selection without an
|
|
|
|
|
extra `GET /api/auth/me/` round trip. Failure responses mirror the web flow: `400` for a missing or
|
|
|
|
|
invalid `idToken`, a missing email, or an unverified email; `403` for a deactivated account.
|
|
|
|
|
|
|
|
|
|
## Magic links
|
|
|
|
|
|
|
|
|
|
Passwordless sign-in, in two deliveries, across three endpoints:
|
|
|
|
|
|
|
|
|
|
`POST /api/auth/magic-link/request/` (`MagicLinkRequestView`,
|
|
|
|
|
`backend/common/views/auth_views.py:586-655`):
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"email": "user@example.com", "delivery": "link"}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`delivery` is optional and defaults to `"link"`; the only other value is `"code"`. This endpoint
|
|
|
|
|
**always returns `200`** with the same body, regardless of whether the address has an account,
|
|
|
|
|
whether it failed validation (a malformed or disposable-domain email), or whether it was rate
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
limited (five tokens per address per hour), every one of those cases falls through to the same
|
2026-08-02 14:38:12 +00:00
|
|
|
generic response:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"message": "If this email is valid, you will receive a sign-in link."}
|
|
|
|
|
```
|
|
|
|
|
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
This is deliberate, see [First sign-in](../getting-started/first-sign-in.md#magic-links), and
|
2026-08-02 14:38:12 +00:00
|
|
|
means a client cannot distinguish "sent" from "rejected" from this response; there is no `400` to
|
|
|
|
|
handle here at all. A generated token expires after 10 minutes.
|
|
|
|
|
|
2026-10-02 06:53:22 +00:00
|
|
|
The one other answer is `429`, from the per-client and site-wide limits in
|
|
|
|
|
`backend/common/throttles.py` (`MAGIC_LINK_THROTTLE_IP`, default `30/hour`, and
|
|
|
|
|
`MAGIC_LINK_THROTTLE_GLOBAL`, default `300/hour`). They run before the email is read, so a `429`
|
|
|
|
|
says nothing about the address either.
|
|
|
|
|
|
2026-08-02 14:38:12 +00:00
|
|
|
`POST /api/auth/magic-link/verify/` (`MagicLinkVerifyView`,
|
|
|
|
|
`backend/common/views/auth_views.py:658-768`) exchanges the token from a link-delivery email:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"token": "<64-character hex token from the emailed link>"}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`POST /api/auth/magic-link/verify-code/` (`MagicLinkVerifyCodeView`,
|
|
|
|
|
`backend/common/views/auth_views.py:771-899`) exchanges a code-delivery OTP instead:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"email": "user@example.com", "code": "123456"}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A code is checked under `select_for_update` so two concurrent verify attempts can't both observe
|
|
|
|
|
the same attempt count, and is burned (marked used) after 5 failed attempts even if the correct
|
|
|
|
|
code is submitted later. Both verify endpoints return the same success shape:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"access_token": "<jwt>",
|
|
|
|
|
"refresh_token": "<jwt>",
|
|
|
|
|
"user": {
|
|
|
|
|
"id": "<uuid>",
|
|
|
|
|
"email": "user@example.com",
|
|
|
|
|
"profile_pic": "",
|
|
|
|
|
"is_active": true,
|
|
|
|
|
"organizations": [{"id": "<uuid>", "name": "Acme", "role": "ADMIN", "is_organization_admin": true, "has_sales_access": true, "has_marketing_access": true}]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
with one addition: if the user has **any** active org membership, the token is minted bound to
|
|
|
|
|
one of them and the response also includes:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"current_org": {"id": "<uuid>", "name": "Acme"}}
|
|
|
|
|
```
|
|
|
|
|
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
This is not gated on having exactly one membership; `Profile.objects.filter(user=user,
|
2026-08-02 14:38:12 +00:00
|
|
|
is_active=True).first()` (`backend/common/views/auth_views.py:740,745-746` for verify, `:874,878-879`
|
|
|
|
|
for verify-code) picks whichever active profile sorts first, and `Profile`'s default ordering is
|
|
|
|
|
`("-created_at",)` (`backend/common/models.py:238`), so for a user in more than one org this binds
|
|
|
|
|
to their **most recently created** membership, not necessarily the one they intend to use next. A
|
|
|
|
|
client for a user who might belong to multiple orgs cannot assume magic-link verification always
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
leaves org selection to a later step. It has to check whether `current_org` is present and let the
|
2026-08-02 14:38:12 +00:00
|
|
|
user switch away from it if it's the wrong one, via
|
|
|
|
|
[switching organization](#switching-organization). If there is no active org membership at all, the
|
|
|
|
|
token comes back org-less, same as the OAuth flows.
|
|
|
|
|
Failure responses: `400` with `{"error": "Invalid request"}` if the request body itself fails
|
|
|
|
|
validation (missing token, malformed email, code not exactly 6 digits); `400` with
|
|
|
|
|
`{"error": "Invalid or expired link"}` / `{"error": "Invalid or expired code"}` if the token or code
|
|
|
|
|
doesn't match an unused, unexpired row; `403` for a deactivated account. Verifying creates the user
|
|
|
|
|
if the email has never signed in before, exactly like the OAuth flows do.
|
|
|
|
|
|
|
|
|
|
## Using an access token
|
|
|
|
|
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
Send the access token as `Authorization: Bearer <access_token>`. It is valid for one hour. See
|
2026-08-02 14:38:12 +00:00
|
|
|
[Token model](../architecture/authentication.md#token-model) for why, and what happens after it
|
|
|
|
|
expires.
|
|
|
|
|
|
|
|
|
|
`GET /api/auth/me/` (`MeView`, `backend/common/views/auth_views.py:322-336`) returns the signed-in
|
|
|
|
|
user and every organization they have an active profile in:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"id": "<uuid>",
|
|
|
|
|
"email": "user@example.com",
|
|
|
|
|
"profile_pic": "",
|
|
|
|
|
"is_active": true,
|
|
|
|
|
"organizations": [
|
|
|
|
|
{"id": "<uuid>", "name": "Acme", "role": "ADMIN", "is_organization_admin": true, "has_sales_access": true, "has_marketing_access": true}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Two things worth knowing about this specific endpoint: it sets `authentication_classes =
|
|
|
|
|
[JWTAuthentication]` explicitly (`:327`), so unlike most endpoints it does not also accept a
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
personal access token or an organization API key, only a JWT works here. And its exact path is one
|
2026-08-02 14:38:12 +00:00
|
|
|
of a short list `GetProfileAndOrg` skips org-context resolution for entirely
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
(`backend/common/middleware/get_company.py:29-38`). The middleware returns immediately without
|
2026-08-02 14:38:12 +00:00
|
|
|
trying to set `request.profile`/`request.org`, so this endpoint never requires an `org_id` claim.
|
|
|
|
|
That is what makes it work with the org-less token an org-less sign-in above produces, and exactly
|
|
|
|
|
what makes it useful for letting a freshly-authenticated client list organizations to choose from.
|
|
|
|
|
|
|
|
|
|
## Refreshing
|
|
|
|
|
|
|
|
|
|
`POST /api/auth/refresh-token/` (`OrgAwareTokenRefreshView`,
|
|
|
|
|
`backend/common/views/auth_views.py:339-448`) exchanges a refresh token for a new access/refresh
|
|
|
|
|
pair:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"refresh": "<refresh token>"}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"access": "<new access token>", "refresh": "<new refresh token>"}
|
|
|
|
|
```
|
|
|
|
|
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
Note the field names here, `access` and `refresh`, do not match `access_token`/`refresh_token`
|
2026-08-02 14:38:12 +00:00
|
|
|
used by the sign-in endpoints above; this is the one response on this page that uses the shorter
|
|
|
|
|
pair. Failure responses: `400` if `refresh` is missing; `401` if the token is expired, malformed,
|
|
|
|
|
or already blacklisted (`TokenError`); `403` if the user account has been deactivated since the
|
|
|
|
|
token was issued; `403` if the token carries an `org_id` the user no longer has an active profile
|
|
|
|
|
in. See [Refresh and rotation](../architecture/authentication.md#refresh-and-rotation) for what
|
|
|
|
|
happens to the old token on a successful call, and why.
|
|
|
|
|
|
|
|
|
|
## Switching organization
|
|
|
|
|
|
|
|
|
|
`POST /api/auth/switch-org/` (`OrgSwitchView`, `backend/common/views/auth_views.py:451-583`) moves
|
|
|
|
|
an authenticated caller to a different org and re-mints both tokens bound to it. Unlike most
|
|
|
|
|
endpoints it only requires `IsAuthenticated`, not org context, because it has to work for a token
|
|
|
|
|
that has none yet:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"org_id": "<uuid of an org the caller has an active profile in>",
|
|
|
|
|
"refresh": "<optional: the refresh token being replaced>"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"access_token": "<jwt bound to org_id>",
|
|
|
|
|
"refresh_token": "<jwt bound to org_id>",
|
|
|
|
|
"current_org": {"id": "<uuid>", "name": "Acme"},
|
|
|
|
|
"profile": {"id": "<uuid>", "role": "ADMIN", "is_organization_admin": true}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`refresh` is optional; when it is sent and belongs to the caller, it is blacklisted as part of the
|
|
|
|
|
switch so the org-less (or previous-org) token stops working immediately rather than staying valid
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
for the rest of its 14-day life. See
|
2026-08-02 14:38:12 +00:00
|
|
|
[Organization switching](../architecture/authentication.md#organization-switching) for why the
|
|
|
|
|
ownership check on that field matters. Failure responses: `400` if `org_id` is missing or not a
|
|
|
|
|
valid UUID; `403` if the caller has no active profile in the requested org.
|
|
|
|
|
|
|
|
|
|
## Local development
|
|
|
|
|
|
|
|
|
|
For local development against a running backend with `DEBUG=True`, the fastest way to obtain a
|
Refactor comments for clarity and consistency across multiple screens and services
- Updated comments in LeadsListScreen, MoreScreen, TaskDetailScreen, TaskFormScreen, TasksListScreen, TicketAnalyticsScreen, TicketDetailScreen, TicketsListScreen, ApiService, AuthService, CrashReporting, DealCard, TicketCard, MultiSelectSheet, KanbanColumn, StageStepper, TicketApprovalPanel, TicketPropertiesCard, and TicketTimePanel to improve readability and maintain a consistent style.
- Adjusted punctuation and phrasing for better flow and understanding.
2026-08-03 04:06:48 +00:00
|
|
|
token pair is the `devlogin` management command rather than any of the endpoints above. It needs
|
2026-08-02 14:38:12 +00:00
|
|
|
no OAuth provider and no outbound email. It refuses to run at all unless `DEBUG` is `True`. See
|
|
|
|
|
[First sign-in: Local development](../getting-started/first-sign-in.md) for its exact invocation,
|
|
|
|
|
flags, and org-resolution behavior; this page's endpoints are what `devlogin` is a shortcut around,
|
|
|
|
|
and the tokens it prints work identically against every endpoint described above.
|