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
- Send credentials to
POST /auth/sign-in/email - If 2FA is required, the response includes
TWO_FACTOR_REQUIREDwith atotpConfiguredflag - If TOTP is configured, verify with
POST /auth/two-factor/verify-totp - If TOTP isn't configured, request an email OTP via
POST /auth/two-factor/send-otp, then verify withPOST /auth/two-factor/verify-otp - 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
httpOnlyand use thelaxSameSite 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
- API Conventions → — pagination, sorting, error format
- API Keys in the Portal → — create and manage keys from the dashboard
- Two-Factor Authentication → — set up 2FA from the portal
- Error Codes — handle authentication errors
.png)