Core Concepts
This guide helps developers understand: which extension layer you belong to, which integration mode to take, what's inside the wsa token, and "how far you want to use it."
Two Integration Modes: Push vs. Pull
There are two paths by which the platform hands identity to you. The same wsa contract applies to both; the only difference is "how you obtain the identity authentication information."
Push (Push handoff) — see 03
The user opens your app from within the workspace "Extensions" page, and the platform appends the wsa to your landing page URL and pushes it to you:
https://app.example.com/landing?wsa=<JWT>
- The entry point is inside the workspace.
- The developer only needs to consume the
?wsa=on the landing page — the simplest approach. - Best for: an "embedded app" in the workspace sidebar.
Pull (Pull / M-Auth) — see 04
The developer's extension app places its own "Login with Workspace" button. When the user clicks it, they go to GPTBots to log in ➡️ select a workspace ➡️ and are brought back with the login state. This uses the OAuth2 authorization code + PKCE:
- The entry point is on the developer's extension app login page.
- The browser only receives a one-time
code; the actualwsais exchanged by your backend using thecode+ PKCE and never enters the browser URL. - Best for: cases where the developer's extension app is a standalone site that wants to offer "sign in with a GPTBots workspace account."
| Push | Pull (M-Auth) | |
|---|---|---|
| Login entry | Workspace "Extensions" page | Your app's page (login button) |
| How the token reaches you | Pushed directly to the landing page via URL ?wsa= |
The frontend gets a code, the backend exchanges it for the wsa |
Has the browser seen the wsa |
Yes (must be stripped immediately after landing) | Never (more secure) |
| PKCE required | No | Yes (S256 enforced) |
| SDK frontend method | consumeHandoff |
startWorkspaceLogin + completeWorkspaceLogin |
Identity Authentication Usage Guide
The platform is only responsible for "passing along the identity"; whether to use it is entirely up to the developer:
| Level | Meaning | What the developer must do |
|---|---|---|
use |
Verify signature + establish a session + gate features by role |
consumeHandoff / completeWorkspaceLogin on the frontend, verifyWsa on the backend |
receive-only |
Read the identity for display/analytics but do not create a session or gate anything; keep using your own auth or stay anonymous | Only readHandoffToken() (a pure function with no side effects) |
ignore |
Read nothing at all — equivalent to auth_mode=none, just an ordinary external link |
Nothing |
Both
verifyWsaandreadHandoffTokenare pure functions, so "receiving but not using" is zero-cost.
Mapped to the auth_mode chosen at registration:
auth_mode = workspace_account: the platform signs thewsaand appends it to the URL (push) / supports M-Auth (pull), and you receive the identity.auth_mode = none: the platform redirects directly, the URL carries no authentication information, and you receive no identity (corresponds toignore).
The wsa Token Contract (JWT)
The wsa is a JWT signed with HS256 (HMAC-SHA256), valid for 5 minutes (exp = iat + 300).
Standard Claims
| Claim | Type | Description |
|---|---|---|
iss |
string | Fixed value gptbots-workspace, must be verified |
aud |
string | The developer extension app host (e.g., app.example.com), parsed from the registered URL, must be verified |
sub |
string | The workspace user's accountId, globally unique, usable as the primary user-ID key on the developer side |
iat |
number (seconds) | Issued-at time |
exp |
number (seconds) | Expiration time, fixed at iat + 300, must be verified |
Business Claims
| Claim | Type | Description |
|---|---|---|
role |
string | OWNER / ADMIN / MEMBER — the user's role in this workspace |
workspace_id |
string | The workspace ID (i.e., projectId), the multi-tenant isolation key |
username |
string | The user's nickname (may be absent) |
email |
string | The user's email (may be absent) |
avatar |
string | The avatar URL (may be absent) |
app_name |
string | The name of the extension app for this particular handoff (useful for auditing, may be absent) |
Absent fields:
username/avatar/app_namewill not appear in the payload when the source data is empty, so always handle null values defensively and never assume they are present.
Example payload
{
"iss": "gptbots-workspace",
"aud": "app.example.com",
"sub": "65f7c8a1d8f3a40012345678",
"iat": 1730000000,
"exp": 1730000300,
"username": "John Doe",
"email": "john.doe@example.com",
"avatar": "https://cdn.example.com/avatar/u123.png",
"role": "ADMIN",
"workspace_id": "65a0000000000000000abcde",
"app_name": "Contract Review System"
}
role≠ the permissions inside your app. Theroleonly reflects the user's role in this workspace; treat it as a "default permission mapping for the first landing," and have your app maintain its own permission model.
Next step: read 03 - Push Handoff or 04 - Pull Login according to the mode you chose; whichever it is, please read 05 - Token Verification and Security.
