Authentication#
TerraNexus supports multiple authentication methods for different use cases. This section covers how to authenticate with the TerraNexus API using JWT tokens, API keys, and web session authentication.
API Authentication Guide#
TerraNexus API Authentication Guide#
Overview#
TerraNexus provides two authentication methods for programmatic API access:
Method |
Best For |
Expiry |
MFA Required |
|---|---|---|---|
JWT Tokens |
Interactive apps, short-term access |
15 min / 30 days |
Yes (if enabled) |
API Keys |
Servers, CI/CD, M2M automation |
Never (until revoked) |
No |
Method 1: JWT Token Authentication#
JWT (JSON Web Tokens) provide short-lived, secure authentication. Use this method for interactive applications or when you need fine-grained session control.
Token Expiry#
Token Type |
Expiry |
Purpose |
|---|---|---|
Access Token |
15 minutes |
API request authentication |
Refresh Token |
30 days |
Obtain new access tokens |
1.1 Login - Obtain Tokens#
Endpoint: POST /api/auth/login/
Without MFA:
curl -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{"email": "user@example.com", "password": "your_password"}' \
'https://terranexus.pangaeainnovations.com/api/auth/login/'
With MFA enabled:
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"email": "user@example.com", "password": "your_password", "mfa_code": "123456"}' \
'https://terranexus.pangaeainnovations.com/api/auth/login/'
The mfa_code can be either:
A 6-digit TOTP code from your authenticator app
A backup code in format
XXXX-XXXX(single-use)
Python example:
>>> import requests
>>> api_path = 'https://terranexus.pangaeainnovations.com/api/auth/login/'
>>> api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
>>> api_data = {"email": "user@example.com", "password": "your_password"}
>>> response = requests.post(api_path, headers=api_headers, json=api_data)
>>> response.json()
Success response:
{
"success": true,
"tokens": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 900
},
"user": {
"id": "usr_abc123def456",
"email": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"requires_mfa": false
}
}
MFA required response (HTTP 202):
{
"success": false,
"error": "MFA verification required",
"mfa_required": true,
"mfa_methods": ["totp"]
}
1.2 Using JWT Tokens#
Option 1: Authorization Header (recommended)
curl -H "Authorization: Bearer <access_token>" \
https://terranexus.pangaeainnovations.com/ogcapi/collections
Option 2: Query Parameter
https://terranexus.pangaeainnovations.com/ogcapi/collections?token=<access_token>
Use query parameters for browser links or embedding.
1.3 Refresh Token#
Endpoint: POST /api/auth/refresh/
When your access token expires (after 15 minutes), use the refresh token to obtain a new one:
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"refresh_token": "<refresh_token>"}' \
'https://terranexus.pangaeainnovations.com/api/auth/refresh/'
Response:
{
"success": true,
"tokens": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.NEW...",
"token_type": "Bearer",
"expires_in": 900
}
}
1.4 Get User Info#
Endpoint: GET /api/user/info/
curl -H "Authorization: Bearer <access_token>" \
https://terranexus.pangaeainnovations.com/api/user/info/
1.5 Logout#
Endpoint: POST /api/auth/logout/
curl -X POST \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"refresh_token": "<refresh_token>"}' \
https://terranexus.pangaeainnovations.com/api/auth/logout/
Method 2: API Key Authentication#
API Keys provide long-lived, simple authentication for machine-to-machine (M2M) access. Ideal for servers, CI/CD pipelines, and automated scripts.
Key Features#
No expiry – keys remain valid until manually revoked
No MFA required – MFA is verified when the key is created via web UI
Revocable – disable keys instantly if compromised
Auditable – usage tracking (last used, use count)
2.1 Creating API Keys#
API Keys are created from the Profile page in the web UI:
Login to TerraNexus (complete MFA if enabled)
Go to Profile → API Access → Method 2: API Keys tab
Enter a name (e.g., “Production Server”)
Click Generate API Key
Copy the key immediately – it will not be shown again!
Or via API (requires JWT authentication):
curl -X POST \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"name": "CI/CD Pipeline", "scopes": ["read", "write"]}' \
https://terranexus.pangaeainnovations.com/api/keys/create/
Response:
{
"success": true,
"key_id": "key_abc123def456",
"api_key": "tnx_a1b2c3d4e5f6g7h8i9j0...",
"name": "CI/CD Pipeline",
"scopes": ["read", "write"],
"created_at": "2025-02-01T10:00:00Z",
"message": "Save this API key now - it won't be shown again!"
}
2.2 Using API Keys#
Option 1: X-API-Key Header (recommended)
curl -H "X-API-Key: tnx_your_api_key_here" \
https://terranexus.pangaeainnovations.com/ogcapi/collections
Option 2: Query Parameter
https://terranexus.pangaeainnovations.com/ogcapi/collections?api_key=tnx_your_api_key_here
Python example:
>>> import requests
>>> api_key = 'tnx_your_api_key_here'
>>>
>>> # Using header
>>> response = requests.get(
... 'https://terranexus.pangaeainnovations.com/ogcapi/collections',
... headers={'X-API-Key': api_key}
... )
>>>
>>> # Using query parameter
>>> response = requests.get(
... 'https://terranexus.pangaeainnovations.com/ogcapi/collections',
... params={'api_key': api_key}
... )
2.3 Listing API Keys#
Endpoint: GET /api/keys/
curl -H "Authorization: Bearer <access_token>" \
https://terranexus.pangaeainnovations.com/api/keys/
Response:
{
"success": true,
"keys": [
{
"key_id": "key_abc123",
"key_prefix": "tnx_a1b2c3d4",
"name": "Production Server",
"scopes": ["read", "write"],
"created_at": "2025-02-01T10:00:00Z",
"last_used_at": "2025-02-01T14:30:00Z",
"use_count": 42,
"is_active": true
}
]
}
2.4 Revoking API Keys#
Endpoint: POST /api/keys/<key_id>/revoke/
curl -X POST \
-H "Authorization: Bearer <access_token>" \
https://terranexus.pangaeainnovations.com/api/keys/key_abc123/revoke/
Response:
{
"success": true,
"message": "API key revoked successfully"
}
Complete Python Client Example#
import requests
class TerraNexusAPI:
"""
TerraNexus API client supporting both JWT and API Key authentication.
Examples
--------
Using API Key (recommended for M2M)::
>>> api = TerraNexusAPI(api_key='tnx_your_api_key_here')
>>> collections = api.get_collections()
Using JWT (for interactive apps)::
>>> api = TerraNexusAPI(email='user@example.com', password='***')
>>> collections = api.get_collections()
Using JWT with MFA::
>>> api = TerraNexusAPI(email='user@example.com', password='***', mfa_code='123456')
>>> collections = api.get_collections()
"""
def __init__(self, api_key=None, email=None, password=None, mfa_code=None):
self.base_url = 'https://terranexus.pangaeainnovations.com'
self.api_key = api_key
self.access_token = None
self.refresh_token = None
# If credentials provided, login with JWT
if email and password:
self._login(email, password, mfa_code)
def _login(self, email, password, mfa_code=None):
"""Authenticate with JWT tokens."""
payload = {'email': email, 'password': password}
if mfa_code:
payload['mfa_code'] = mfa_code
response = requests.post(
f'{self.base_url}/api/auth/login/',
headers={'Content-Type': 'application/json'},
json=payload
)
data = response.json()
if data.get('mfa_required'):
raise Exception('MFA required - provide mfa_code parameter')
if data.get('success'):
self.access_token = data['tokens']['access_token']
self.refresh_token = data['tokens']['refresh_token']
else:
raise Exception(f"Login failed: {data.get('error')}")
def _refresh_tokens(self):
"""Refresh expired JWT access token."""
if not self.refresh_token:
return False
response = requests.post(
f'{self.base_url}/api/auth/refresh/',
json={'refresh_token': self.refresh_token}
)
data = response.json()
if data.get('success'):
self.access_token = data['tokens']['access_token']
return True
return False
def _get_headers(self):
"""Get authentication headers."""
if self.api_key:
return {'X-API-Key': self.api_key}
elif self.access_token:
return {'Authorization': f'Bearer {self.access_token}'}
return {}
def _request(self, method, endpoint, **kwargs):
"""Make authenticated request with auto-refresh for JWT."""
url = f'{self.base_url}{endpoint}'
headers = kwargs.pop('headers', {})
headers.update(self._get_headers())
response = requests.request(method, url, headers=headers, **kwargs)
# Auto-refresh JWT on 401 (not applicable for API keys)
if response.status_code == 401 and self.access_token:
if self._refresh_tokens():
headers.update(self._get_headers())
response = requests.request(method, url, headers=headers, **kwargs)
return response
def get_collections(self):
"""Get all collections."""
response = self._request('GET', '/ogcapi/collections')
return response.json()
def get_collection_items(self, collection_id, limit=10):
"""Get items from a collection."""
response = self._request(
'GET',
f'/ogcapi/collections/{collection_id}/items',
params={'limit': limit}
)
return response.json()
def get_dggs_zones(self, collection_id, dggs_id='H3'):
"""Get DGGS zones for a collection."""
response = self._request(
'GET',
f'/ogcapi/collections/{collection_id}/dggs/{dggs_id}/zones'
)
return response.json()
# Usage examples
if __name__ == '__main__':
# Option 1: API Key (recommended for automation)
api = TerraNexusAPI(api_key='tnx_your_api_key_here')
# Option 2: JWT without MFA
# api = TerraNexusAPI(email='user@example.com', password='your_password')
# Option 3: JWT with MFA
# api = TerraNexusAPI(email='user@example.com', password='your_password', mfa_code='123456')
# Get collections
collections = api.get_collections()
print(f"Found {len(collections.get('collections', []))} collections")
OGC API Endpoints#
All these endpoints support both JWT and API Key authentication:
Endpoint |
Method |
Description |
|---|---|---|
|
GET |
API landing page |
|
GET |
List collections |
|
GET |
Collection metadata |
|
GET |
Collection items (features) |
|
GET |
Single feature |
|
GET |
DGGS zones |
|
GET |
List processes |
|
POST |
Execute process |
MFA (Multi-Factor Authentication)#
MFA with JWT Tokens#
If MFA is enabled on your account, the /api/auth/login/ endpoint requires an mfa_code:
{
"email": "user@example.com",
"password": "your_password",
"mfa_code": "123456"
}
The mfa_code can be:
TOTP code: 6-digit code from your authenticator app (e.g.,
123456)Backup code: One-time code in format
XXXX-XXXX(e.g.,ABCD-1234)
Important: Backup codes are single-use. Once used, they are automatically invalidated and cannot be used again.
MFA with API Keys#
API Keys do not require MFA during API calls because:
MFA was verified when you created the key via the web UI
Keys are designed for automated/M2M access where no human is present
This makes API Keys the preferred method for servers, CI/CD pipelines, and automated scripts.
Error Responses#
Status |
Error |
Description |
|---|---|---|
400 |
Invalid JSON payload |
Request body is not valid JSON |
400 |
Email and password are required |
Missing credentials |
401 |
Invalid email or password |
Authentication failed |
401 |
Invalid MFA code |
MFA verification failed |
401 |
Invalid or expired token |
JWT token is invalid/expired |
401 |
Invalid API key |
API key is invalid or revoked |
401 |
Authorization header required |
Missing authentication |
429 |
Account temporarily locked |
Too many failed attempts (5 min lockout) |
Security Best Practices#
Use API Keys for M2M – Avoid storing passwords in scripts
Use JWT for interactive apps – Short expiry limits exposure
Enable MFA – Adds security layer for JWT login
Store credentials securely – Never commit keys/tokens to git
Use HTTPS – All API calls must use HTTPS
Revoke compromised keys – Disable immediately from Profile page
Use descriptive key names – Makes auditing easier
Monitor usage – Check
last_used_atanduse_countfor anomalies
Web Authentication Guide#
TerraNexus Web Authentication Guide#
Overview#
This guide covers the browser-based (interactive) authentication flows for TerraNexus. These are used when accessing the platform through a web browser.
Authentication Method |
URL |
Description |
|---|---|---|
Email/Password |
|
Standard login with optional MFA |
Google OAuth2 |
|
Sign in with Google |
GitHub OAuth2 |
|
Sign in with GitHub |
Authentication Flow Diagram#
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Login │────▶│ Password │────▶│ MFA │────▶│ Dashboard │
│ Page │ │ Check │ │ (if enabled)│ │ │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │
│ ┌─────────────┐ │
└───────────▶│ OAuth2 │────────────┘
│ Provider │
└─────────────┘
1. Standard Login (Email/Password)#
URL: /user/login/#
Flow#
User navigates to login page
User enters email and password
System validates credentials
If MFA enabled → redirect to MFA verification
If MFA not enabled → create session and redirect to dashboard
Form Fields#
Field |
Type |
Required |
Description |
|---|---|---|---|
|
Yes |
User’s email address |
|
|
password |
Yes |
User’s password |
|
checkbox |
No |
Extend session duration |
|
hidden |
No |
URL to redirect after login |
Success Response#
Session cookie set
Redirect to
nextURL or dashboard (/)
Error Responses#
Error |
Message |
Action |
|---|---|---|
Invalid credentials |
“Invalid email or password” |
Re-display form |
Account locked |
“Account temporarily locked. Try again in 5 minutes.” |
Wait and retry |
Account inactive |
“Account is not active” |
Contact support |
MFA required |
N/A |
Redirect to |
2. MFA Verification#
URL: /user/mfa/verify/#
When Required#
MFA verification is required when:
User has MFA enabled on their account
User successfully entered email/password
User has not yet verified MFA in current login flow
Flow#
User is redirected from login page
User enters 6-digit TOTP code OR backup code
System verifies code
If valid → create session and redirect
If invalid → show error and allow retry
Form Fields#
Field |
Type |
Required |
Description |
|---|---|---|---|
|
text |
Yes |
6-digit TOTP code or backup code |
Accepted Code Formats#
Code Type |
Format |
Example |
Notes |
|---|---|---|---|
TOTP |
6 digits |
|
From authenticator app |
Backup |
XXXX-XXXX |
|
Single-use |
Important: Backup Codes are Single-Use#
When you use a backup code:
The code is verified against your stored backup codes
If valid, the code is permanently deleted from your account
That code can never be used again
You have a limited number of backup codes (10 generated during MFA setup)
Error Responses#
Error |
Message |
Action |
|---|---|---|
Invalid code |
“Invalid MFA code” |
Re-display form |
Expired session |
“Session expired” |
Redirect to login |
No backup codes left |
“No backup codes remaining” |
Disable and re-enable MFA |
3. MFA Setup#
URL: /user/mfa/setup/#
Prerequisites#
User must be logged in
User must not have MFA already enabled
Flow#
User navigates to MFA setup (from Profile page)
System generates TOTP secret and QR code
User scans QR code with authenticator app
User enters verification code to confirm setup
System displays backup codes (one time only!)
MFA is now enabled
Supported Authenticator Apps#
Google Authenticator
Microsoft Authenticator
Authy
1Password
Any TOTP-compatible app
Backup Codes#
During MFA setup, 10 backup codes are generated:
ABCD-1234
EFGH-5678
IJKL-9012
... (10 total)
Critical:
Save these codes in a secure location
Each code can only be used once
They are your only recovery method if you lose your authenticator
4. MFA Disable#
URL: /user/mfa/disable/#
Prerequisites#
User must be logged in
User must have MFA enabled
User must verify current MFA code to disable
Flow#
User navigates to MFA settings
User clicks “Disable MFA”
User enters current TOTP code to confirm
MFA is disabled
All backup codes are deleted
5. OAuth2 Login (Google/GitHub)#
URLs#
Provider |
Initiate URL |
Callback URL |
|---|---|---|
|
|
|
GitHub |
|
|
Flow#
User clicks “Sign in with Google/GitHub” on login page
User is redirected to OAuth provider
User authenticates with provider
Provider redirects back to callback URL
System creates/links account and creates session
User is redirected to dashboard
Account Linking#
Scenario |
Behavior |
|---|---|
New email |
Create new account |
Existing email (same provider) |
Log in to existing account |
Existing email (different provider) |
Link to existing account |
Existing email (password account) |
Link OAuth to existing account |
OAuth2 Permissions Requested#
Google:
openid- Basic identityemail- Email addressprofile- Name and profile picture
GitHub:
read:user- Basic user infouser:email- Email address
6. Signup (Account Registration)#
URL: /user/signup/#
Flow#
User navigates to signup page
User fills out registration form
System validates input
System sends verification email
User clicks verification link
Account is activated
Form Fields#
Field |
Type |
Required |
Validation |
|---|---|---|---|
|
Yes |
Valid email, not already registered |
|
|
password |
Yes |
NIST-compliant (see below) |
|
password |
Yes |
Must match password |
|
text |
Yes |
1-50 characters |
|
text |
Yes |
1-50 characters |
Password Requirements (NIST SP 800-63B)#
Minimum 8 characters
No maximum length restriction
Checked against common password list
No composition rules (uppercase, numbers, etc.)
Context-aware (rejects passwords containing email/name)
Email Verification#
After signup:
Verification email sent to provided address
Email contains unique activation link
Link expires after 24 hours
Clicking link activates account
User can then log in
7. Password Reset#
Request URL: /user/password-reset/#
Confirm URL: /user/password-reset/confirm/<token>/#
Flow#
User clicks “Forgot Password” on login page
User enters email address
System sends reset email (if account exists)
User clicks link in email
User enters new password
Password is updated
User can log in with new password
Security Features#
Reset tokens expire after 1 hour
Tokens are single-use
Rate limited (max 3 requests per 15 minutes)
Same message shown whether email exists or not (prevents enumeration)
8. Password Change (Authenticated)#
URL: /user/password-change/#
Prerequisites#
User must be logged in
Flow#
User navigates to Profile → Security
User enters current password
User enters new password (twice)
System validates and updates password
Existing sessions remain valid
Form Fields#
Field |
Type |
Required |
Description |
|---|---|---|---|
|
password |
Yes |
Current password |
|
password |
Yes |
New password (NIST-compliant) |
|
password |
Yes |
Must match new password |
9. Logout#
URL: /user/logout/#
Flow#
User clicks “Logout”
Session is invalidated in MongoDB
Session cookie is cleared
User is redirected to login page
Session Cleanup#
Server-side session data is deleted
Session cookie is expired
Any cached authentication data is cleared
10. Profile Management#
URL: /user/profile/#
Features#
Section |
Description |
|---|---|
Personal Info |
Update name, email |
Security |
Change password, MFA settings |
API Access |
Generate JWT tokens, manage API keys |
Sessions |
View/revoke active sessions |
Session Management#
Session Duration#
Setting |
Default |
With “Remember Me” |
|---|---|---|
Session timeout |
2 hours |
30 days |
Idle timeout |
30 minutes |
24 hours |
Session Storage#
Sessions are stored in MongoDB with:
Unique session key
User ID
Creation timestamp
Last activity timestamp
IP address
User agent
Concurrent Sessions#
Multiple sessions allowed (different devices)
Sessions can be viewed/revoked from Profile
All sessions invalidated on password change (optional)
Security Features#
Rate Limiting#
Action |
Limit |
Lockout |
|---|---|---|
Login attempts |
5 per 5 minutes |
5 minute lockout |
MFA attempts |
5 per 5 minutes |
5 minute lockout |
Password reset |
3 per 15 minutes |
15 minute cooldown |
CSRF Protection#
All forms include CSRF tokens:
<form method="post">
{% csrf_token %}
<!-- form fields -->
</form>
URL Reference#
URL |
Method |
View |
Description |
|---|---|---|---|
|
GET, POST |
|
Login form and processing |
|
GET, POST |
|
Logout and session cleanup |
|
GET, POST |
|
Registration form |
|
GET, POST |
|
MFA code verification |
|
GET, POST |
|
Enable MFA |
|
GET, POST |
|
Disable MFA |
|
GET |
|
Initiate OAuth2 |
|
GET |
|
OAuth2 callback |
|
GET, POST |
|
Request reset |
|
GET, POST |
|
Reset password |
|
GET, POST |
|
Change password |
|
GET, POST |
|
Profile management |
Error Pages#
Status |
Template |
Description |
|---|---|---|
400 |
|
Bad request |
401 |
|
Unauthorized |
403 |
|
Forbidden |
404 |
|
Not found |
500 |
|
Server error |
Troubleshooting#
“Invalid email or password”#
Check email spelling
Ensure Caps Lock is off
Try password reset if forgotten
“Account temporarily locked”#
Wait 5 minutes
Contact support if persists
“MFA code invalid”#
Ensure authenticator app time is synced
Try a backup code
Wait for next code cycle (30 seconds)
“Session expired”#
Re-login
Check browser cookie settings
Clear browser cache
OAuth2 Errors#
Error |
Cause |
Solution |
|---|---|---|
“State mismatch” |
CSRF protection triggered |
Clear cookies, try again |
“Access denied” |
User cancelled OAuth |
Try again or use password |
“Provider not configured” |
Missing OAuth credentials |
Contact administrator |