Authentication API Specification¶
This document defines the current ExtraSuite authentication protocol. It is based entirely on the v2 session-token flow used by the reference client.
Overview¶
ExtraSuite splits authentication into two phases:
- Browser login to establish a 30-day ExtraSuite session
- Headless credential exchange for each typed command
The browser is only required for Phase 1. All command execution after that is headless until the session expires or is revoked.
Design Goals¶
- Authenticate the user with the organization's existing identity system
- Avoid recurring browser interruptions during agent execution
- Issue the minimum Google credential needed for each command
- Keep long-lived secrets out of the client and server database
- Preserve an audit trail of what was requested and why
Actors¶
- Client: the ExtraSuite CLI or client library running on the user's machine
- Server: the ExtraSuite-compatible authentication service
- Browser: used only for interactive login
- Google: OAuth identity provider and token issuer
Phase 1: Session Establishment¶
Step 1: Start browser login¶
The client starts a localhost callback server on 127.0.0.1:<port> and opens:
Behavior:
- Validate
portis in the allowed range - If the browser already has a valid server-side session cookie, skip Google OAuth
- Otherwise redirect the browser to the identity provider
- After successful login, create a short-lived auth code
- Redirect the browser to:
On failure, redirect to:
Step 2: Exchange auth code for session token¶
The client exchanges the auth code for a 30-day ExtraSuite session token:
POST /api/auth/session/exchange
Content-Type: application/json
{
"code": "auth_code_from_redirect",
"device_mac": "0x1234abcd",
"device_hostname": "laptop",
"device_os": "Darwin",
"device_platform": "macOS-15.3-arm64"
}
Response:
{
"session_token": "random_opaque_secret",
"expires_at": "2026-04-05T11:30:00+00:00",
"email": "user@example.com"
}
Server requirements:
- Auth codes must be single-use
- Auth codes must expire quickly (reference implementation: 2 minutes)
- The server must validate the authenticated email against any domain allowlist
- The server must ensure the user's service account exists before issuing a session
- The session token must be stored server-side only as a SHA-256 hash
Phase 2: Headless Credential Exchange¶
The client sends the session token in the Authorization header and requests credentials for a typed command:
POST /api/auth/token
Authorization: Bearer <session_token>
Content-Type: application/json
{
"command": {
"type": "sheet.pull",
"file_url": "https://docs.google.com/spreadsheets/d/..."
},
"reason": "User asked the agent to review the quarterly budget"
}
Response:
{
"credentials": [
{
"provider": "google",
"kind": "bearer_sa",
"token": "ya29...",
"expires_at": "2026-03-06T13:45:00+00:00",
"scopes": [],
"metadata": {
"service_account_email": "user-abc@project.iam.gserviceaccount.com"
}
}
],
"command_type": "sheet.pull"
}
Credential selection¶
The server maps command.type to the minimum required credential:
- Service-account credential for file operations such as
sheet.*,doc.*,slide.*,form.*, and read-only Drive listing/search commands - Domain-wide-delegation credential for user-impersonating commands such as
gmail.*,calendar.*,contacts.*,script.*, anddrive.file.*
Scope selection is server-defined. The client does not request raw OAuth scopes directly.
Command Type Table¶
The authoritative command-to-credential mapping lives in:
server/src/extrasuite/server/command_registry.pyserver/src/extrasuite/server/commands.py
Reference categories:
| Category | Credential kind | Notes |
|---|---|---|
sheet.*, doc.*, slide.*, form.*, drive.ls, drive.search | bearer_sa | Access is limited to files shared with the per-user service account |
gmail.*, calendar.*, contacts.*, script.*, drive.file.* | bearer_dwd | Scopes are determined by the server's command registry |
Security Requirements¶
Transport¶
- All server endpoints must use HTTPS in production
- The localhost callback must bind to
127.0.0.1, not a public interface - The session token must be sent in the
Authorizationheader, not in the body or query string
Tokens¶
- Session tokens should be opaque random secrets with high entropy
- Session tokens must be stored server-side as hashes, not raw values
- Google access tokens should remain short-lived (reference implementation: 1 hour)
- The client must not write access tokens to disk; they should be held only in process memory
Auth codes and state¶
- OAuth state must be single-use and short-lived
- Auth codes must be single-use and short-lived
- Both values should be deleted immediately after successful consumption
Auditing¶
Each POST /api/auth/token request should log:
- authenticated user email
- session hash prefix
- command type
- command context
- user-intent
reason - client IP
- timestamp
Scope controls¶
For DWD-backed commands:
- The server must derive scopes from
command.type - The optional
DELEGATION_SCOPESallowlist may reject commands before token generation - Google Workspace Admin Console remains the final authority for allowed scopes
Rate Limits¶
Suggested defaults from the reference implementation:
| Endpoint | Limit |
|---|---|
GET /api/token/auth | 10 requests per minute per IP |
POST /api/auth/session/exchange | 10 requests per minute per IP |
POST /api/auth/token | 60 requests per minute per IP |
GET /api/admin/sessions | 30 requests per minute per IP |
DELETE /api/admin/sessions/{hash} | 30 requests per minute per IP |
POST /api/admin/sessions/revoke-all | 10 requests per minute per IP |
Session Management Endpoints¶
The v2 protocol also includes self-service/admin session management:
List sessions¶
Revoke one session¶
Revoke all sessions¶
Authorization rules:
- Users may manage their own sessions
- Emails in
ADMIN_EMAILSmay manage sessions for any user
Client Expectations¶
An ExtraSuite-compatible client should:
- Read
EXTRASUITE_SERVER_URLorgateway.json - Start a localhost callback server for Phase 1
- Store the returned session token in the OS keyring (macOS Keychain, Linux SecretService, Windows Credential Locker). The only on-disk file is
~/.config/extrasuite/profiles.json(0600) which stores profile names and email addresses — no tokens. - Never cache short-lived access tokens to disk. Hold them only in process memory.
- Re-run Phase 1 when the session expires or is revoked
Reference Implementation¶
The open-source reference implementation lives in:
server/src/extrasuite/server/api.pyserver/src/extrasuite/server/database.pyclient/src/extrasuite/client/credentials.py
Those files are the executable reference for this specification.