Skip to main content

Overview

QFieldCloud API uses token-based authentication. Each request must include a valid authentication token in the Authorization header. The API supports two authentication methods:
  1. Token Authentication - Primary method for API access
  2. Session Authentication - Used by the web interface

Authentication Token

Authentication tokens are 100-character alphanumeric strings that expire after a configurable period (default: varies by instance).

Token Format

Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a123456789...

Token Properties

  • Length: 100 characters
  • Type: Alphanumeric string
  • Expiration: Configurable (instance-specific)
  • Client-specific: Different token policies based on client type
  • Multi-token: Users can have multiple active tokens for different clients

Obtaining an Authentication Token

Username/Password Login

Use the login endpoint to obtain a token with username or email and password:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "your_username",
    "password": "your_password"
  }'

Success Response

{
  "token": "401f7ac837da42b97f613d789819ff93537bee6a1234567890abcdef...",
  "expires_at": "2024-03-11T10:30:00Z"
}
token
string
required
The authentication token to use in subsequent API requests
expires_at
string
required
ISO 8601 timestamp when the token expires

Email Login

You can also authenticate using email instead of username:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "your_password"
  }'

Alternative Token Endpoint

The /api/v1/auth/token/ endpoint provides identical functionality to /api/v1/auth/login/:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/token/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "your_username",
    "password": "your_password"
  }'

Using Authentication Tokens

Include the token in the Authorization header with the Token prefix:
curl https://app.qfieldcloud.com/api/v1/projects/ \
  -H "Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a..."

Token Behavior

  • Tokens are updated with last_used_at timestamp on each request
  • Expired tokens return 401 Unauthorized with error code token_authentication_failed
  • Inactive user accounts return 401 Unauthorized even with valid tokens

SSO Authentication

QFieldCloud supports Single Sign-On (SSO) via OAuth2 providers configured through django-allauth.

Listing Available Providers

Get a list of configured authentication providers:
curl https://app.qfieldcloud.com/api/v1/auth/providers/

Example Response

[
  {
    "type": "credentials",
    "id": "credentials",
    "name": "Username / Password"
  },
  {
    "type": "oauth2",
    "id": "google",
    "name": "Google",
    "grant_flow": 3,
    "scope": "openid email profile",
    "pkce_enabled": true,
    "token_url": "https://oauth2.googleapis.com/token",
    "refresh_token_url": "https://oauth2.googleapis.com/token",
    "request_url": "https://accounts.google.com/o/oauth2/v2/auth",
    "redirect_host": "localhost",
    "redirect_port": 7070,
    "redirect_url": "",
    "client_id": "your-client-id.apps.googleusercontent.com",
    "extra_tokens": {
      "id_token": "X-Id-Token"
    },
    "idp_id_header": "X-Idp-Id"
  }
]

Provider Response Fields

type
string
Provider type: credentials for username/password, oauth2 for SSO providers
id
string
Unique provider identifier (e.g., google, keycloak, github)
name
string
Human-readable provider name
grant_flow
integer
OAuth2 grant flow type:
  • 0: Authorization Code
  • 3: Authorization Code with PKCE
scope
string
OAuth2 scopes requested during authentication
pkce_enabled
boolean
Whether PKCE (Proof Key for Code Exchange) is enabled
token_url
string
OAuth2 token exchange endpoint
request_url
string
OAuth2 authorization endpoint
client_id
string
OAuth2 client ID for this provider

Client Types

QFieldCloud tracks different client types and applies appropriate token policies:
Client TypeUser-Agent PatternToken Behavior
qfieldqfield|*Single token per user (new login invalidates old token)
qfieldsync* QGIS/[34]xxxx*Single token per user
sdksdk|*Multiple tokens allowed
clicli|*Multiple tokens allowed
browserBrowser User-AgentMultiple tokens allowed
workerInternal workersMultiple tokens allowed
unknownOtherSingle token per user

Setting Client Type

Set an appropriate User-Agent header to ensure correct token handling:
# QField mobile app
curl -H "User-Agent: qfield|QField/3.0.0" \
  -H "Authorization: Token YOUR_TOKEN" \
  https://app.qfieldcloud.com/api/v1/projects/

# Python SDK
curl -H "User-Agent: sdk|my-app/1.0.0" \
  -H "Authorization: Token YOUR_TOKEN" \
  https://app.qfieldcloud.com/api/v1/projects/

# CLI tool
curl -H "User-Agent: cli|qfieldcloud-cli/2.1.0" \
  -H "Authorization: Token YOUR_TOKEN" \
  https://app.qfieldcloud.com/api/v1/projects/

Getting Current User

Retrieve the authenticated user’s information:
curl https://app.qfieldcloud.com/api/v1/auth/user/ \
  -H "Authorization: Token YOUR_TOKEN"

Response

{
  "pk": 123,
  "username": "john_doe",
  "email": "john@example.com",
  "first_name": "John",
  "last_name": "Doe"
}
pk
integer
User’s primary key identifier
username
string
Username
email
string
Email address (read-only)
first_name
string
First name
last_name
string
Last name

Logging Out

Invalidate the current authentication token:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/logout/ \
  -H "Authorization: Token YOUR_TOKEN"

Response

{
  "detail": "Successfully logged out."
}
The logout endpoint:
  • Invalidates all active tokens for single-token clients (QField, QFieldSync, Unknown)
  • Invalidates only the current token for multi-token clients (SDK, CLI, Browser)
  • Sets token expires_at to current time

Error Handling

Invalid Credentials

{
  "non_field_errors": [
    "Unable to log in with provided credentials."
  ]
}
HTTP Status: 401 Unauthorized

Expired Token

{
  "code": "token_authentication_failed",
  "message": "Token authentication failed",
  "detail": "Token has expired."
}
HTTP Status: 401 Unauthorized

Invalid Token

{
  "code": "token_authentication_failed",
  "message": "Token authentication failed",
  "detail": "Invalid token."
}
HTTP Status: 401 Unauthorized

Too Many Login Attempts

{
  "code": "too_many_failed_login_attempts",
  "message": "Too many failed login attempts!",
  "detail": "Account temporarily locked due to too many failed login attempts."
}
HTTP Status: 401 Unauthorized

Inactive User

{
  "non_field_errors": [
    "User account is disabled."
  ]
}
HTTP Status: 401 Unauthorized

Security Best Practices

  • Never commit tokens to version control
  • Use environment variables or secure credential storage
  • Rotate tokens periodically
  • Use different tokens for different applications
  • Always use HTTPS endpoints in production
  • Never send tokens over unencrypted connections
  • Validate SSL certificates
  • Check expires_at timestamp before requests
  • Implement automatic token renewal
  • Handle 401 responses gracefully
  • Re-authenticate when tokens expire
  • Don’t log tokens in plain text
  • Use token prefixes in logs (e.g., Token abc***xyz)
  • Implement token revocation when compromised
  • Monitor for suspicious token usage

Example: Complete Authentication Flow

Here’s a complete example of authenticating and making an API request:
import requests
from datetime import datetime

class QFieldCloudClient:
    def __init__(self, base_url, username, password):
        self.base_url = base_url.rstrip('/')
        self.token = None
        self.expires_at = None
        self.authenticate(username, password)
    
    def authenticate(self, username, password):
        """Obtain authentication token"""
        response = requests.post(
            f'{self.base_url}/api/v1/auth/login/',
            json={'username': username, 'password': password}
        )
        response.raise_for_status()
        
        data = response.json()
        self.token = data['token']
        self.expires_at = datetime.fromisoformat(data['expires_at'].replace('Z', '+00:00'))
        
    def get_headers(self):
        """Get headers with authentication token"""
        return {
            'Authorization': f'Token {self.token}',
            'User-Agent': 'sdk|my-app/1.0.0'
        }
    
    def get_projects(self):
        """Get list of projects"""
        response = requests.get(
            f'{self.base_url}/api/v1/projects/',
            headers=self.get_headers()
        )
        response.raise_for_status()
        return response.json()

# Usage
client = QFieldCloudClient(
    'https://app.qfieldcloud.com',
    'your_username',
    'your_password'
)

projects = client.get_projects()
print(f"Found {len(projects)} projects")

Next Steps

API Overview

Learn about API structure and conventions

Projects API

Start managing projects via API

Users API

Manage user accounts and profiles

OpenAPI Docs

Interactive API documentation