Authentication
2. Authentication
2.1 Authentication Methods
| Method | Typical use |
|---|---|
| API Key | Application integrations, EMR, OpenAI-compat clients |
| JWT Bearer | WebApp, admin CRUD, agents, sessions, automations |
| Google OAuth | Interactive SPA login (DefaultLoginMode=Google in many deploys) |
| Delegation token | External services acting for a federated user on MCP instance APIs |
| Local login | Dev / testing only (Authentication:EnableLocalLogin) |
Discover which login modes are enabled:
curl -s https://your-kroov-host/api/auth/options
{
"enableLocalLogin": false,
"enableGoogleLogin": true,
"googleOAuthConfigured": true,
"defaultLoginMode": "Google",
"mobileOAuthSupported": true
}
2.2 API Key Authentication
- An administrator creates a key via
POST /api/auth/apikey(or project rotate-key flows) - The key may be scoped to a project
- Clients send
x-api-key: <secret>on every request - On
/v1/*,Authorization: Bearer <api-key>is also accepted (rewritten to ApiKey)
POST /api/ai/query HTTP/1.1
Host: your-kroov-host
x-api-key: ainx_...
Content-Type: application/json
{ "prompt": "Your query here", "projectId": 1 }
Security:
- Store keys in a secrets manager / environment variables — never in source control
- Rotate periodically; use separate keys for dev and production
- Never put keys in URL query strings except for WebSocket clients that cannot set headers (prefer first-message
authwhen possible) - Never log raw keys
Create / list API keys (JWT required)
# Create — secret returned once
curl -X POST https://your-kroov-host/api/auth/apikey \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{ "name": "EMR Integration", "projectId": 1, "expiresAt": null }'
# List summaries (no secrets)
curl "https://your-kroov-host/api/auth/apikey?projectId=1" \
-H "Authorization: Bearer $JWT"
{
"key": "ainx_...",
"name": "EMR Integration",
"expiresAt": null
}
2.3 JWT Authentication
Obtain via local login (dev):
curl -X POST https://your-kroov-host/api/auth/login \
-H "Content-Type: application/json" \
-d '{ "username": "your-username", "password": "your-password" }'
Google OAuth: GET /api/auth/google (browser redirect).
Refresh / logout:
curl -X POST .../api/auth/refresh -H 'Content-Type: application/json' \
-d '{ "refreshToken": "..." }'
curl -X POST .../api/auth/logout -H 'Content-Type: application/json' \
-d '{ "refreshToken": "..." }'
Using JWT:
GET /api/project HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Typical lifetimes: access token ~60 minutes; refresh token ~7 days (deployment-configurable).
Admin policy (AdminOnly): JWT Admin role, or Google admin email allow-list. Many write endpoints require it.
2.4 Google OAuth
GET /api/auth/google— start Google OAuth (browser redirect). The SPA continues to receive tokens in the URL fragment (#token=…&refreshToken=…) after Google returns to/api/auth/google/callback.
SPA token hand-off (useful in headless / local testing): navigate to
/login?token=<JWT>&refreshToken=<REFRESH> after calling the login API.
Native / mobile Google login (PKCE)
When GET /api/auth/options reports mobileOAuthSupported: true, a native app can complete Google sign-in without putting JWTs in a URL. Google still uses the existing Web OAuth client and the HTTPS callback registered in Google Cloud Console ({PublicOrigin}/api/auth/google/callback). The app never talks to Google directly.
-
Open the system browser (not a WebView) on:
GET /api/auth/google?client=mobile&code_challenge=<S256>&code_challenge_method=S256&redirect_uri=kroov%3A%2F%2Fauth-callbackcode_challenge/code_challenge_method— PKCE S256 (RFC 7636)redirect_uri— must match the server allowlist (Authentication:Google:MobileRedirectUris, defaultkroov://auth-callback). This URI is not added to Google Cloud Console.
-
After Google sign-in, the API redirects the browser to
kroov://auth-callback?code=…(errors:?error=…). The code is a ~60 second, one-shot ticket; it is not a JWT. -
Exchange once:
curl -s -X POST https://your-kroov-host/api/auth/google/exchange \
-H 'Content-Type: application/json' \
-d '{ "code": "<from-callback>", "codeVerifier": "<pkce-verifier>" }'
The JSON is the same as local login: { "token", "refreshToken", "username" }. Replay, a wrong verifier, or an expired ticket all return 400 with the same message.
Afterwards use Authorization: Bearer, POST /api/auth/refresh, and POST /api/auth/logout as for any other session. Google is not called again until logout, refresh expiry (~7 days of inactivity, sliding window), or revocation. Signing in on another device no longer revokes existing Google sessions; use DELETE /api/me/sessions/{id} to drop a specific device.
2.5 Delegation tokens (federation)
External apps exchange verified service + user identity for a short-lived X-AINexus-Delegation token used with MCP instance APIs and chat:
curl -X POST https://your-kroov-host/api/delegations/exchange \
-H "x-api-key: PROJECT_API_KEY" \
-H "X-AINexus-Service-Token: <gcp-sa-jwt>" \
-H "X-AINexus-User-Token: <google-user-jwt>" \
-H "Content-Type: application/json" \
-d '{ "phoneProof": "<optional-eva-phone-jwt>", "scopes": ["mcp:pair", "mcp:invoke", "mcp:manage"] }'
{ "token": "...", "tokenType": "Kroov-Delegation", "expiresIn": 300 }
See Projects → Federation and MCP.
2.6 Global authorization notes
- The API applies a fallback
RequireAuthenticatedUserpolicy: controllers must explicitly[AllowAnonymous]for public routes (auth options, features, webhooks, health, CSP report, MCP proxy with controller token, etc.). - JSON uses camelCase everywhere.
- Multipart field names match property names (
providerId,projectId,noLog, …).