-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
- Introduction
- Server Architecture
- REST API Endpoints
- WebAuthn JavaScript API
- Data Schemas
- Authentication and Security
- Error Handling
- Rate Limiting and Versioning
- Client Implementation Guidelines
- Examples and Usage Patterns
The Post-Quantum WebAuthn Platform provides a comprehensive authentication solution that combines traditional WebAuthn protocols with post-quantum cryptographic algorithms. The platform exposes RESTful APIs for credential registration and authentication, along with a sophisticated JavaScript client library that handles WebAuthn flows seamlessly.
The platform supports both simple and advanced authentication flows, with extensive customization capabilities for enterprise deployments. It includes built-in support for various attestation formats, CBOR-encoded CTAP2 messages, and JSON-encoded WebAuthn options.
The server follows a modular Flask-based architecture with clear separation of concerns:
graph TB
subgraph "Client Layer"
Browser[Web Browser]
JS[JavaScript API]
end
subgraph "Server Layer"
App[Flask Application]
Routes[Route Handlers]
Config[Configuration]
end
subgraph "Processing Layer"
General[General Routes]
Simple[Simple Routes]
Advanced[Advanced Routes]
end
subgraph "Storage Layer"
Local[Local Storage]
GCS[Google Cloud Storage]
Metadata[Metadata Cache]
end
Browser --> JS
JS --> App
App --> Routes
Routes --> General
Routes --> Simple
Routes --> Advanced
General --> Metadata
Simple --> Local
Simple --> GCS
Advanced --> Local
Advanced --> GCS
Diagram sources
- server/server/app.py
- server/server/config.py
Section sources
- server/server/app.py
- server/server/config.py
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/mds/metadata/base |
GET | Retrieve verified FIDO MDS metadata snapshot | None |
/api/mds/metadata/custom |
GET | List custom uploaded metadata entries | None |
/api/mds/metadata/upload |
POST | Upload custom metadata files | None |
/api/mds/metadata/custom/<filename> |
DELETE | Delete custom metadata entry | None |
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/codec |
POST | Encode/decode payloads between formats | None |
/api/decode |
POST | Decode WebAuthn-related payloads | None |
/api/mds/decode-certificate |
POST | Decode X.509 certificates | None |
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/deletepub |
POST | Delete public key material | Session-based |
/api/downloadcred |
GET | Download credential data | Session-based |
Section sources
- server/server/routes/general.py
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/register/begin |
POST | Begin registration process | Session-based |
/api/register/complete |
POST | Complete registration process | Session-based |
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/authenticate/begin |
POST | Begin authentication process | Session-based |
/api/authenticate/complete |
POST | Complete authentication process | Session-based |
Section sources
- server/server/routes/simple.py
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/advanced/register/begin |
POST | Begin advanced registration | Session-based |
/api/advanced/register/complete |
POST | Complete advanced registration | Session-based |
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/advanced/authenticate/begin |
POST | Begin advanced authentication | Session-based |
/api/advanced/authenticate/complete |
POST | Complete advanced authentication | Session-based |
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/advanced/credential-artifacts/<storage_id> |
DELETE | Delete credential artifact | Session-based |
Section sources
- server/server/routes/advanced.py
The platform provides a comprehensive JavaScript API for handling WebAuthn flows:
// Registration Functions
window.simpleRegister(); // Simple registration flow
window.advancedRegister(); // Advanced registration flow
// Authentication Functions
window.simpleAuthenticate(); // Simple authentication flow
window.advancedAuthenticate(); // Advanced authentication flow
// Utility Functions
window.parseCreationOptionsFromJSON(); // Parse registration options
window.parseRequestOptionsFromJSON(); // Parse authentication options
window.create(); // Create credential
window.get(); // Get assertionasync function simpleRegister(): Promise<void>
async function advancedRegister(): Promise<void>async function simpleAuthenticate(): Promise<void>
async function advancedAuthenticate(): Promise<void>async function create(options: CredentialCreationOptions): Promise<PublicKeyCredential>
async function get(options: CredentialRequestOptions): Promise<PublicKeyCredential>Section sources
- server/server/static/scripts/main.js
- server/server/static/scripts/simple/auth-simple.js
- server/server/static/scripts/advanced/auth-advanced.js
interface CredentialCreationOptions {
publicKey: {
rp: {
name: string;
id?: string;
};
user: {
id: Uint8Array | string;
name: string;
displayName: string;
};
challenge: Uint8Array | string;
pubKeyCredParams: Array<{
type: "public-key";
alg: number;
}>;
timeout?: number;
excludeCredentials?: Array<{
type: "public-key";
id: Uint8Array | string;
transports?: AuthenticatorTransport[];
}>;
authenticatorSelection?: {
authenticatorAttachment?: "platform" | "cross-platform";
residentKey?: "required" | "preferred" | "discouraged";
userVerification?: "required" | "preferred" | "discouraged";
};
attestation?: "none" | "direct" | "indirect" | "enterprise";
extensions?: {
appid?: string;
appidExclude?: string;
credProps?: boolean;
};
};
signal?: AbortSignal;
}interface CredentialRequestOptions {
mediation?: "silent" | "optional" | "required";
publicKey: {
challenge: Uint8Array | string;
timeout?: number;
rpId?: string;
allowCredentials?: Array<{
type: "public-key";
id: Uint8Array | string;
transports?: AuthenticatorTransport[];
}>;
userVerification?: "required" | "preferred" | "discouraged";
extensions?: {
appid?: string;
appidExclude?: string;
credProps?: boolean;
};
};
signal?: AbortSignal;
}interface MakeCredentialRequest {
1: Uint8Array; // clientDataHash (32 bytes)
2: PublicKeyCredentialRpEntity; // rp
3: PublicKeyCredentialUserEntity; // user
4: Array<PublicKeyCredentialParameters>; // pubKeyCredParams
5?: Array<PublicKeyCredentialDescriptor>; // excludeList
6?: Record<string, any>; // extensions
7?: AuthenticatorSelectionCriteria; // options
8?: Uint8Array; // pinUvAuthParam
9?: number; // pinUvAuthProtocol
10?: number; // enterpriseAttestation
11?: Uint8Array; // largeBlobKey
}interface GetAssertionRequest {
1: Uint8Array; // rpId (32 bytes)
2: Uint8Array; // clientDataHash (32 bytes)
3: Array<PublicKeyCredentialDescriptor>; // allowList
4?: Record<string, any>; // extensions
5?: Record<string, any>; // options
6?: Uint8Array; // pinUvAuthParam
7?: number; // pinUvAuthProtocol
8?: Uint8Array; // largeBlobKey
}interface RegistrationResponse {
id: string;
rawId: Uint8Array | string;
type: "public-key";
authenticatorAttachment?: "platform" | "cross-platform";
response: {
clientDataJSON: Uint8Array | string;
attestationObject: Uint8Array | string;
transports?: AuthenticatorTransport[];
};
clientExtensionResults?: Record<string, any>;
}interface AuthenticationResponse {
id: string;
rawId: Uint8Array | string;
type: "public-key";
authenticatorAttachment?: "platform" | "cross-platform";
response: {
clientDataJSON: Uint8Array | string;
authenticatorData: Uint8Array | string;
signature: Uint8Array | string;
userHandle: Uint8Array | string;
};
clientExtensionResults?: Record<string, any>;
}Section sources
- server/server/static/scripts/shared/webauthn-json.browser-ponyfill.js
- server/server/decoder/decode.py
The platform implements multiple layers of authentication and security:
- Session cookies for maintaining user state
- CSRF protection through Flask-WTF
- Secure cookie attributes (HttpOnly, Secure, SameSite)
- Support for post-quantum algorithms (ML-DSA variants)
- Traditional algorithms (ECDSA, RSA) for backward compatibility
- Hardware security module integration
- FIDO Metadata Service (MDS) integration
- Trusted attestation certificate validation
- Custom CA certificate support
The server implements comprehensive security headers:
# Security headers configuration
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
PERMANENT_SESSION_LIFETIME=timedelta(hours=24),
EXPLAIN_TEMPLATE_LOADING=False,
)Section sources
- server/server/config.py
All API endpoints return standardized error responses:
interface ErrorResponse {
error: string;
status?: number;
details?: Record<string, any>;
}| Status Code | Error Type | Description |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or malformed JSON |
| 401 | Unauthorized | Authentication required or invalid session |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource not found |
| 422 | Unprocessable Entity | Validation errors |
| 500 | Internal Server Error | Server-side processing errors |
The JavaScript API provides comprehensive error handling:
try {
await simpleRegister();
} catch (error) {
if (error.name === 'NotAllowedError') {
// User cancelled operation
} else if (error.name === 'InvalidStateError') {
// Authenticator error
} else if (error.name === 'SecurityError') {
// Security violation
}
}Section sources
- server/server/routes/simple.py
- server/server/routes/advanced.py
The platform implements rate limiting at multiple levels:
- Registration endpoints: 10 requests per minute per IP
- Authentication endpoints: 20 requests per minute per IP
- Metadata endpoints: 50 requests per minute per IP
- Maximum 10 concurrent sessions per user
- Session timeout after 24 hours of inactivity
The API follows semantic versioning principles:
- Version:
1.0.0 - Stability: Stable
- Backward Compatibility: Guaranteed for minor versions
Accept-Version: 1.0- New optional fields are added without breaking changes
- Deprecated fields are maintained for at least 6 months
- Major version updates include migration guides
Section sources
- server/server/config.py
// Import the WebAuthn API
import { create, get } from './shared/webauthn-json.browser-ponyfill.js';
// Initialize the application
document.addEventListener('DOMContentLoaded', () => {
// Setup event listeners and UI
setupWebAuthnHandlers();
});async function registerCredential() {
try {
// Begin registration
const beginResponse = await fetch('/api/register/begin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});
const options = await beginResponse.json();
// Convert to WebAuthn format
const createOptions = parseCreationOptionsFromJSON(options);
// Create credential
const credential = await create(createOptions);
// Complete registration
const completeResponse = await fetch('/api/register/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential.toJSON())
});
const result = await completeResponse.json();
console.log('Registration successful:', result);
} catch (error) {
console.error('Registration failed:', error);
}
}async function authenticate() {
try {
// Begin authentication
const beginResponse = await fetch('/api/authenticate/begin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
credentials: storedCredentials
})
});
const options = await beginResponse.json();
const getOptions = parseRequestOptionsFromJSON(options);
// Get assertion
const assertion = await get(getOptions);
// Complete authentication
const completeResponse = await fetch('/api/authenticate/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(assertion.toJSON())
});
const result = await completeResponse.json();
console.log('Authentication successful:', result);
} catch (error) {
console.error('Authentication failed:', error);
}
}const advancedOptions = {
publicKey: {
// ... standard options
extensions: {
credProps: true,
appid: 'https://example.com',
minPinLength: true
}
}
};const blobOptions = {
publicKey: {
extensions: {
largeBlob: {
support: 'preferred',
write: 'your-large-blob-data'
}
}
}
};const hsmOptions = {
publicKey: {
authenticatorSelection: {
authenticatorAttachment: 'platform',
requireResidentKey: true
}
}
};Section sources
- server/server/static/scripts/simple/auth-simple.js
- server/server/static/scripts/advanced/auth-advanced.js
// Example: Complete registration with error handling
async function completeRegistrationExample() {
try {
// Step 1: Begin registration
const beginResponse = await fetch('/api/register/begin?email=user@example.com', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
credentials: existingCredentials
})
});
if (!beginResponse.ok) {
throw new Error(`Begin registration failed: ${await beginResponse.text()}`);
}
const beginData = await beginResponse.json();
// Step 2: Create credential
const createOptions = parseCreationOptionsFromJSON(beginData);
const credential = await create(createOptions);
// Step 3: Complete registration
const completeResponse = await fetch('/api/register/complete?email=user@example.com', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential.toJSON())
});
if (!completeResponse.ok) {
throw new Error(`Complete registration failed: ${await completeResponse.text()}`);
}
const completeData = await completeResponse.json();
// Handle success
console.log('Registration completed successfully');
console.log('Algorithm used:', completeData.algo);
console.log('Stored credential:', completeData.storedCredential);
} catch (error) {
console.error('Registration failed:', error);
// Handle specific error types
if (error.name === 'NotAllowedError') {
console.log('User cancelled registration');
} else if (error.name === 'InvalidStateError') {
console.log('Authenticator already registered');
}
}
}// Example: Advanced authentication with custom options
async function advancedAuthenticationExample() {
try {
// Prepare authentication request
const authRequest = {
publicKey: {
challenge: base64UrlEncode(generateChallenge()),
rpId: window.location.hostname,
allowCredentials: availableCredentials,
userVerification: 'preferred',
hints: ['client-device', 'hybrid'],
extensions: {
credProps: true,
largeBlob: {
support: 'preferred'
}
}
}
};
// Begin authentication
const beginResponse = await fetch('/api/advanced/authenticate/begin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(authRequest)
});
const beginData = await beginResponse.json();
// Parse and customize options
const requestOptions = parseRequestOptionsFromJSON(beginData);
// Perform authentication
const assertion = await get(requestOptions);
// Complete authentication
const completeResponse = await fetch('/api/advanced/authenticate/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...authRequest,
__assertion_response: assertion.toJSON(),
__storedCredentials: availableCredentials
})
});
const completeData = await completeResponse.json();
// Verify result
if (completeData.status === 'OK') {
console.log('Authentication successful');
console.log('Authenticated credential:', completeData.authenticatedCredentialId);
console.log('New sign count:', completeData.signCount);
}
} catch (error) {
console.error('Advanced authentication failed:', error);
}
}// Example: Upload and manage custom metadata
async function manageCustomMetadata() {
try {
// Upload custom metadata file
const formData = new FormData();
const fileInput = document.getElementById('metadata-file');
formData.append('files', fileInput.files[0]);
const uploadResponse = await fetch('/api/mds/metadata/upload', {
method: 'POST',
body: formData
});
const uploadData = await uploadResponse.json();
if (uploadData.errors && uploadData.errors.length > 0) {
console.error('Upload errors:', uploadData.errors);
} else {
console.log('Successfully uploaded metadata:', uploadData.items);
}
// List custom metadata
const listResponse = await fetch('/api/mds/metadata/custom');
const listData = await listResponse.json();
console.log('Available custom metadata:', listData.items);
} catch (error) {
console.error('Metadata management failed:', error);
}
}Section sources
- server/server/routes/simple.py
- server/server/routes/advanced.py
- server/server/routes/general.py