Skip to content

Authentication ​

Secure access to the Go Bananas! API.

Overview ​

Go Bananas! supports two authentication methods, with different surfaces:

SurfaceAuthentication
REST API (/api/*)API key via X-API-Key or Authorization: Bearer sk_*
MCP (/mcp; legacy /sse)API key or OAuth access token via Authorization: Bearer <access_token>

For MCP requests, the server distinguishes API keys (sk_live_*, sk_test_*) from OAuth access tokens.


API Key Authentication ​

API key authentication is used for tenant operations and admin tokens for administrative functions.

API Authentication Flow

Validate format, check cache, query D1, verify active status

API Key Types ​

Live Keys ​

Production keys for live environments:

sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
  • Full access to all features
  • Usage tracked for billing
  • Rate limits enforced

Test Keys ​

Development and testing:

sk_test_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
  • Same functionality as live keys
  • May have relaxed rate limits
  • Useful for integration testing

Authentication Methods ​

bash
curl -X GET "https://gobananasai.com/api/profile" \
  -H "X-API-Key: sk_live_your_key_here"

Authorization Bearer ​

bash
curl -X GET "https://gobananasai.com/api/profile" \
  -H "Authorization: Bearer sk_live_your_key_here"

Priority ​

If both headers are present, X-API-Key takes precedence.

Admin Authentication ​

Administrative endpoints require an admin session token (preferred) or a legacy admin token:

bash
curl -X GET "https://gobananasai.com/admin/tenants" \
  -H "Authorization: Bearer <admin_session_token>"

Legacy header (still supported):

bash
curl -X GET "https://gobananasai.com/admin/tenants" \
  -H "X-Admin-Token: your_admin_token_here"

Security Note

Admin tokens have full system access. Keep them secure and never expose in client-side code.

Key Validation ​

Format Validation ​

Keys must match the pattern:

sk_(live|test)_[a-zA-Z0-9]{32,}

Validation Process ​

  1. Format Check: Key matches expected pattern
  2. Cache Lookup: Check KV cache for fast validation
  3. Database Query: Verify key exists and is active
  4. Tenant Check: Verify tenant is active
  5. Cache Update: Store in KV for future requests

Session Tracking ​

Include X-Session-Id header to track operations across requests:

bash
curl -X POST "https://gobananasai.com/api/images" \
  -H "X-API-Key: sk_live_xxx" \
  -H "X-Session-Id: my-project-session" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "a sunset"}'

If not provided, a new session ID is generated automatically.

Tenant Profile ​

Tenants can validate a key and fetch quotas/profile via:

bash
curl -X GET "https://gobananasai.com/api/profile" \
  -H "X-API-Key: sk_live_xxx"

Response:

json
{
  "tenant": {
    "tenantId": "acme-corp",
    "tenantName": "ACME Corporation",
    "quotas": {
      "monthlyQuotaMb": 10240,
      "monthlyUsedMb": 2880,
      "monthlyRemainingMb": 7360,
      "rateLimitPerMinute": 60
    }
  }
}

Managing API Keys (Admin) ​

Only admins can create additional keys.

Create API Key ​

bash
curl -X POST "https://gobananasai.com/admin/tenants/acme-corp/api-keys" \
  -H "Authorization: Bearer <admin_session_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "key_name": "Mobile App",
    "type": "live"
  }'

Response (key shown only once):

json
{
  "data": {
    "apiKey": "sk_live_x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6",
    "keyName": "Mobile App",
    "type": "live"
  }
}

Important

The full API key is only shown once upon creation. Store it securely immediately.

Authentication Errors ​

401 Unauthorized ​

Missing or invalid API key:

json
{
  "error": "Invalid or missing API key"
}

403 Forbidden ​

Key valid but access denied:

json
{
  "error": "Tenant account is inactive"
}

Security Best Practices ​

1. Secure Key Storage ​

Do:

  • Store keys in environment variables
  • Use secret management services (AWS Secrets Manager, HashiCorp Vault)
  • Encrypt keys at rest

Don't:

  • Commit keys to version control
  • Include keys in client-side code
  • Share keys via unencrypted channels

2. Key Rotation ​

Rotate keys periodically:

  1. Create new key
  2. Update applications to use new key
  3. Monitor for old key usage
  4. Revoke old key

3. Use Appropriate Key Types ​

  • Live keys: Production only
  • Test keys: Development/staging environments

4. Monitor Key Usage ​

bash
# Check usage via API
curl "https://gobananasai.com/api/usage" \
  -H "X-API-Key: sk_live_xxx"

5. Implement Request Signing (Optional) ​

For additional security, sign requests:

typescript
const crypto = require('crypto');

function signRequest(payload: string, secret: string): string {
  return crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
}

// Add signature to headers
headers['X-Signature'] = signRequest(body, signingSecret);
headers['X-Timestamp'] = Date.now().toString();

Example: Complete Auth Flow ​

javascript
class NanoBananaAuth {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://gobananasai.com';
  }

  getHeaders(sessionId = null) {
    const headers = {
      'Content-Type': 'application/json',
      'X-API-Key': this.apiKey,
    };
    if (sessionId) {
      headers['X-Session-Id'] = sessionId;
    }
    return headers;
  }

  async validateKey() {
    const response = await fetch(`${this.baseUrl}/api/profile`, {
      headers: this.getHeaders(),
    });

    if (response.status === 401) {
      throw new Error('Invalid API key');
    }
    if (response.status === 403) {
      throw new Error('Account inactive');
    }

    return response.json();
  }

  async request(method, path, body = null, sessionId = null) {
    const response = await fetch(`${this.baseUrl}${path}`, {
      method,
      headers: this.getHeaders(sessionId),
      body: body ? JSON.stringify(body) : null,
    });

    const data = await response.json();
    if (!response.ok) {
      throw new Error(data.error || 'Request failed');
    }
    return data.data ?? data;
  }
}

// Usage
const client = new NanoBananaAuth('sk_live_xxx');

try {
  const profile = await client.validateKey();
  console.log('Authenticated as:', profile.tenant.tenantName);

  const images = await client.request('GET', '/api/images');
  console.log('Images:', images.length);
} catch (error) {
  console.error('Auth failed:', error.message);
}

OAuth 2.1 Authentication ​

OAuth 2.1 provides secure, standards-compliant authentication with PKCE, dynamic client registration, and refresh token rotation.

Discovery Endpoints ​

Protected Resource Metadata ​

http
GET /.well-known/oauth-protected-resource

Returns scopes and authorization server information.

Authorization Server Metadata ​

http
GET /oauth/.well-known/openid-configuration

Returns OAuth endpoints, supported grants, and scopes.

Authorization Endpoint ​

http
GET /oauth/authorize
ParameterRequiredDescription
response_typeYesMust be code
client_idYesRegistered client identifier
redirect_uriYesMust match registered URI
code_challengeYesBASE64URL(SHA256(code_verifier))
code_challenge_methodYesMust be S256
scopeNoSpace-separated scopes
stateRecommendedCSRF protection token

Login behavior:

  • HTML clients without a session are redirected to /?return_to=....
  • Non-HTML clients receive 401 with login_required JSON and a login_url.

Token Endpoint ​

http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

Authorization Code Grant:

grant_type=authorization_code
code=AUTH_CODE
code_verifier=PKCE_VERIFIER
client_id=CLIENT_ID
redirect_uri=REDIRECT_URI

Refresh Token Grant:

grant_type=refresh_token
refresh_token=REFRESH_TOKEN
client_id=CLIENT_ID

Response:

json
{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "...",
  "scope": "images:generate images:read"
}

Use Access Token (MCP) ​

bash
curl -X POST "https://mcp.gobananasai.com" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"method":"tools/list","params":{}}'

Dynamic Client Registration ​

http
POST /oauth/register
Content-Type: application/json
json
{
  "client_name": "My App",
  "redirect_uris": ["https://myapp.com/callback"],
  "scope": "images:generate images:read"
}

OAuth Scopes ​

ScopePermission
images:generateGenerate images
images:editEdit images
images:readRead image metadata
images:deleteDelete images
characters:manageCRUD characters
characters:readView characters
products:manageCRUD products
products:readView products
styles:manageCRUD style presets
styles:readView style presets
sessions:readView sessions
analytics:readView analytics

OAuth Error Codes ​

CodeDescription
invalid_requestMissing or malformed parameter
invalid_clientClient authentication failed
invalid_grantInvalid or expired code/token
unsupported_grant_typeGrant type not supported
invalid_scopeInvalid scope requested

Full Reference

See OAuth 2.1 API Reference → for complete endpoint documentation.

Next Steps ​

Released under the MIT License.