Authentication

How authentication works on the GPCN™ platform, including session-based auth, API keys, and two-factor requirements.

Authentication Methods

GPCN™ supports three authentication methods. Which one you use depends on your use case.

Session-Based Authentication (Browser)

Session auth is the default for browser-based applications. When you sign in via POST /auth/sign-in/email, the server sets a secure HTTP-only cookie on your browser. Subsequent requests automatically include this cookie.

You don't need to manage tokens manually. The session cookie is refreshed on each request, so active sessions won't expire unexpectedly.

# 1. Sign in (cookie is set automatically)
curl -X POST https://api.gpcn.com/v1/auth/sign-in/email \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"email": "you@company.com", "password": "your-password"}'

# 2. Use the cookie for subsequent requests
curl https://api.gpcn.com/v1/resource/virtual-machines \
  -b cookies.txt

See the API Reference → for examples in TypeScript, Python, Go, and C#.

API Key Authentication (Scripts and Integrations)

API keys are the recommended method for scripts, CI/CD pipelines, and server-to-server integrations. Pass your key in the X-API-Key header.

API keys start with the gpcn_ prefix and are 32 characters long. You can create them in the dashboard or via the API.

curl https://api.gpcn.com/v1/resource/virtual-machines \
  -H "X-API-Key: gpcn_your_api_key_here"

See the API Reference → for examples in TypeScript, Python, Go, and C#.

Bearer Token Authentication

You can also pass a session token in the Authorization header. This is useful for mobile apps or SPAs that store the token rather than relying on cookies.

curl https://api.gpcn.com/v1/resource/virtual-machines \
  -H "Authorization: Bearer your_session_token_here"

See the API Reference → for examples in TypeScript, Python, Go, and C#.

Choosing an Auth Method

Two-Factor Authentication (2FA)

Two-factor authentication is mandatory for all users on the GPCN™ platform. After signing in with your email and password, you'll need to complete a second verification step.

Supported 2FA Methods

Method Description When used
TOTP (Authenticator App) 6-digit code from an app like Google Authenticator or Authy Primary method when configured
Email OTP One-time code sent to your email Automatic fallback when TOTP isn't set up
Backup Codes Single-use recovery codes Emergency access when other methods are unavailable

2FA Login Flow

  1. Send credentials to POST /auth/sign-in/email
  2. If 2FA is required, the response includes TWO_FACTOR_REQUIRED with a totpConfigured flag
  3. If TOTP is configured, verify with POST /auth/two-factor/verify-totp
  4. If TOTP isn't configured, request an email OTP via POST /auth/two-factor/send-otp, then verify with POST /auth/two-factor/verify-otp
  5. As a last resort, use a backup code via POST /auth/two-factor/verify-backup-code

API Keys and 2FA

API keys bypass the 2FA flow entirely. Once you've created an API key (which requires an authenticated session with 2FA completed), the key authenticates requests directly. This makes API keys ideal for automated systems.

Multi-Entity Support

Users can belong to multiple organizations (entities) on GPCN™. Your permissions are scoped to the entity you're currently operating in.

How It Works

  • Each user has a primary entity set at account creation
  • Your session tracks which entity is currently active
  • Permissions are evaluated against the active entity
  • API keys inherit the entity context from the user who created them

Checking Your Current Context

Use GET /auth/check to see your current user info, active entity, and permissions.

curl https://api.gpcn.com/v1/auth/check \
  -H "X-API-Key: gpcn_your_api_key_here"

See the API Reference → for examples in TypeScript, Python, Go, and C#.

Session Lifecycle

Event What happens
Sign in Session created, cookie set (browser) or token returned
Active use Session automatically refreshed on each request
Sign out Session invalidated, cookie cleared
Idle timeout Session expires after the configured period
Password change All other sessions for the user are invalidated

Sign In

POST /auth/sign-in/email

Authenticate with email and password. Returns user data and sets a session cookie, or returns a 2FA challenge.

Auth required: No

Request body:

Field Type Required Description
email string Yes Account email address
password string Yes Account password
rememberMe boolean No Extend session duration

Example:

curl -X POST https://api.gpcn.com/v1/auth/sign-in/email \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{
    "email": "you@company.com",
    "password": "your-password"
  }'

See the API Reference → for examples in TypeScript, Python, Go, and C#.

Success response (200):

{
  "success": true,
  "data": {
    "user": {
      "id": "user-uuid",
      "email": "you@company.com",
      "name": "Your Name"
    }
  }
}

2FA required response:

{
  "success": false,
  "error": "TWO_FACTOR_REQUIRED",
  "data": {
    "userId": "user-uuid",
    "totpConfigured": true,
    "email": "you@company.com"
  }
}

Status codes: 200 Success | 401 Invalid credentials | 429 Rate limited

Sign Out

POST /auth/sign-out — End the current session and clear the session cookie. Requires session auth.

Check Session

GET /auth/check — Validate the current session and return user info, roles, and active entity. Works with session or API key auth. Returns user.id, user.email, user.name, user.roles, user.permissions, activeEntity.id, activeEntity.name.

Two-Factor Authentication Endpoints

All 2FA endpoints use the /auth/two-factor/ prefix. During login, 2FA verification endpoints use the temporary 2FA cookie set by /auth/sign-in/email.

2FA Endpoints

Method Path Auth Body Description
POST /auth/two-factor/enable Session { password } Enable TOTP 2FA. Returns secret, qrCode, uri, and backupCodes.
POST /auth/two-factor/verify-totp 2FA cookie { code, trustDevice? } Verify a TOTP code during login.
POST /auth/two-factor/send-otp 2FA cookie None Send a one-time code to the user's email.
POST /auth/two-factor/verify-otp 2FA cookie { code, trustDevice? } Verify an email OTP during login.
POST /auth/two-factor/verify-backup-code 2FA cookie { code, trustDevice? } Verify a backup code during login.
POST /auth/two-factor/disable Session { password } Disable 2FA for the account.
POST /auth/two-factor/generate-backup-codes Session { password } Generate new backup codes (invalidates old ones).
GET /auth/two-factor/status Session None Check 2FA configuration status.

Enable 2FA Response

When you call POST /auth/two-factor/enable, you'll receive everything needed to set up your authenticator app:

{
  "success": true,
  "data": {
    "secret": "JBSWY3DPEBLW64TMMQ======",
    "qrCode": "data:image/png;base64,...",
    "uri": "otpauth://totp/gpcn:you@company.com?...",
    "backupCodes": ["ABC123DEF456", "GHI789JKL012"]
  }
}

2FA Status Response

{
  "success": true,
  "data": {
    "twoFactorEnabled": true,
    "totpConfigured": true,
    "method": "totp",
    "setupAt": "2025-12-01T10:30:00Z"
  }
}

Common 2FA Status Codes

Status Meaning
200 Operation successful
400 Invalid code or invalid password
401 Not authenticated
429 Too many attempts

Password Management

Change Password

PUT /users/:id/password

Change your own password. Requires your current password.

Auth required: Session

Field Type Required Description
oldPassword string Yes Current password
password string Yes New password
curl -X PUT https://api.gpcn.com/v1/users/YOUR_USER_ID/password \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"oldPassword": "current-pass", "password": "new-pass"}'

Status codes: 200 Changed | 400 Doesn't meet requirements | 401 Wrong current password

Request Password Reset

POST /auth/forget-password

Send a password reset email. Always returns success, even if the email isn't registered (to prevent email enumeration).

Auth required: No

Field Type Required Description
email string Yes Account email address
curl -X POST https://api.gpcn.com/v1/auth/forget-password \
  -H "Content-Type: application/json" \
  -d '{"email": "you@company.com"}'

Status codes: 200 Email sent (always) | 429 Rate limited

Complete Password Reset

POST /auth/reset-password

Set a new password using the token from the reset email.

Auth required: No

Field Type Required Description
token string Yes Reset token from the email link
password string Yes New password

Status codes: 200 Password reset | 400 Token expired or invalid password

API Key Management

API key endpoints use the /auth/api-keys prefix.

Self-Service Endpoints

Method Path Permission Description
POST /auth/api-keys/me api-keys:self Create an API key for yourself
DELETE /auth/api-keys/me/:keyId api-keys:self Delete your own API key

Admin Endpoints

Method Path Permission Description
GET /auth/api-keys api-keys:self List all API keys in entity (paginated)
DELETE /auth/api-keys/:keyId api-keys:manage Delete any API key in entity
POST /auth/api-keys/:keyId/rotate api-keys:self or api-keys:manage Rotate key (generate new secret)
PATCH /auth/api-keys/:keyId/status api-keys:self or api-keys:manage Suspend or reactivate a key
PUT /auth/api-keys/:keyId/rate-limit api-keys:manage Override rate limits (platform admin only)

Helper Endpoints

Method Path Auth Description
GET /auth/api-keys/options/roles Session Get available roles for key creation
GET /auth/api-keys/options/expiration Session Get available expiration options

Create API Key

POST /auth/api-keys/me

Field Type Required Description
name string Yes Display name for the key
roleId string No Scope key to a specific role
expiresInDays number No Days until the key expires
metadata object No Arbitrary metadata to attach
curl -X POST https://api.gpcn.com/v1/auth/api-keys/me \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"name": "CI Pipeline", "expiresInDays": 90}'

Response (201):

{
  "success": true,
  "data": {
    "id": "key-uuid",
    "name": "CI Pipeline",
    "key": "gpcn_a1b2c3d4e5f6g7h8i9j0...",
    "start": "gpcn_a1b2",
    "expiresAt": "2026-05-17T00:00:00Z",
    "createdAt": "2026-02-16T00:00:00Z"
  }
}

The full key value is only returned at creation time. Store it securely.

Suspend / Reactivate

PATCH /auth/api-keys/:keyId/status — Send { "enabled": false } to suspend or { "enabled": true } to reactivate.

Rotate Key

POST /auth/api-keys/:keyId/rotate — Generates a new secret. The old secret stops working immediately. Returns the new full key value.

Key Statuses

Status Description
Active Key is working normally
Suspended Temporarily disabled — can be reactivated
Expiring Within 30 days of expiration
Expired Past expiration date — no longer authenticates

Using API Keys

API keys skip the session and 2FA flow entirely. Include the key in a request header and you're authenticated.

All GPCN™ API keys start with the gpcn_ prefix.

X-API-Key Header

The recommended way to authenticate:

curl https://api.gpcn.com/v1/resource/virtual-machines \
  -H "X-API-Key: gpcn_your_api_key_here"

See the API Reference → for examples in TypeScript, Python, Go, and C#.

Bearer Token (Alternative)

You can also pass the key as a Bearer token:

curl https://api.gpcn.com/v1/resource/virtual-machines \
  -H "Authorization: Bearer gpcn_your_api_key_here"

Role Scoping

By default, a key inherits all your permissions. To create a key with limited access, pass a roleId when creating the key. A key can't have permissions beyond your own.

Check available roles before creating a scoped key:

curl https://api.gpcn.com/v1/auth/api-keys/options/roles -b cookies.txt

Integration Examples

GitHub Actions

jobs:
  deploy:
    steps:
      - name: Deploy VM
        env:
          GPCN_API_KEY: ${{ secrets.GPCN_API_KEY }}
        run: |
          curl -X POST https://api.gpcn.com/v1/resource/virtual-machines \
            -H "X-API-Key: $GPCN_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{"name": "web-server", "datacenterId": "dc-uuid", "imageId": 12, "configurationId": 45}'

Ansible

- name: List VMs
  uri:
    url: https://api.gpcn.com/v1/resource/virtual-machines
    headers:
      X-API-Key: "{{ lookup('env', 'GPCN_API_KEY') }}"
  register: vms

API Key Security Best Practices

  • Never commit keys to source control — use environment variables or a secret manager
  • Create separate keys for different environments and integrations
  • Use role-scoped keys to follow least privilege
  • Set expiration dates on keys that don't need to live forever
  • Review keys regularly — revoke unused keys
  • Rotate keys if you suspect compromise

Security Notes

  • Session cookies are httpOnly and use the lax SameSite policy
  • API keys should be stored securely and never committed to source control
  • The full API key value is only shown once at creation time
  • You can rotate or suspend API keys at any time without affecting other keys
  • All authentication events are logged for audit purposes

Troubleshooting

Issue Solution
Session expired unexpectedly Sessions are refreshed automatically on each request, but idle sessions expire after the configured timeout. Re-authenticate by calling POST /auth/sign-in/email again. For long-running automations, use API keys instead of sessions.
Invalid API key (401 Unauthorized) Verify the key starts with the gpcn_ prefix and is exactly 32 characters. Check that the key hasn't been revoked or suspended in the dashboard. Ensure you're passing it in the X-API-Key header, not Authorization.
CORS errors when sending credentials When using session cookies from a browser, your fetch calls must include credentials: "include". The GPCN™ API allows credentialed requests only from registered origins — confirm your domain is listed in your entity's CORS settings.
401 Unauthorized vs 403 Forbidden A 401 means the request has no valid authentication — the session expired, the API key is missing, or the token is invalid. A 403 means you are authenticated but lack permission for the requested action in your current entity context. Check your role and active entity with GET /auth/check.
Missing Content-Type header Requests with a JSON body (sign-in, 2FA verification) require Content-Type: application/json. Without it, the server cannot parse your payload and returns a 400 Bad Request. Always set this header on POST and PUT requests.
2FA code rejected Ensure your device clock is accurate — TOTP codes are time-based and a skew of more than 30 seconds will cause rejection. If the code expired, wait for the next one rather than resubmitting the same code.
API key returns 401 Verify the key is not suspended or expired by checking its status in the portal. Confirm you are using the X-API-Key header (not X-Api-Key or another casing variant) or the Authorization: Bearer format.
Rate limited on sign-in (429) Too many failed login attempts trigger a cooldown. Wait the duration indicated in the Retry-After response header before trying again. Verify your credentials are correct to avoid repeat lockouts.
Key rotation breaks integrations When you rotate a key, the old secret stops working immediately. Update all consumers (CI pipelines, scripts, services) with the new key value before or right after rotating. Consider creating a second key, migrating consumers to it, then deleting the old one for zero-downtime rotation.
Session cookie not sent Browser-based clients must set credentials: "include" on fetch requests (or withCredentials: true in Axios). Also check that SameSite and Secure cookie attributes align with your domain — cross-origin requests require SameSite=None; Secure.
Backup codes exhausted If all backup codes have been used, sign in with your TOTP app or email OTP, then call POST /auth/two-factor/generate-backup-codes with your password to generate a fresh set. Old codes are invalidated automatically.

Next Steps