-
Notifications
You must be signed in to change notification settings - Fork 0
JavaScript Architecture
- Introduction
- Architecture Overview
- Directory Structure
- Main Entry Point
- Authentication Flows
- Shared Utility Modules
- Advanced Features
- Integration Patterns
- Error Handling and Debugging
- Extending the Architecture
- Best Practices
The JavaScript architecture of the post-quantum WebAuthn platform follows a modular, separation-of-concerns design pattern that organizes functionality across three main categories: simple authentication, advanced authentication, and shared utilities. The architecture is built around the WebAuthn API and provides comprehensive support for post-quantum cryptographic algorithms while maintaining a clean separation between authentication flows and shared functionality.
The frontend implementation demonstrates sophisticated module organization with clear boundaries between simple and advanced authentication scenarios, robust state management, and extensive utility functions for WebAuthn operations, binary data handling, and user interface management.
The JavaScript architecture is structured around a hierarchical module system that promotes reusability and maintainability:
graph TB
subgraph "Entry Point"
MainJS[main.js]
end
subgraph "Simple Authentication"
SimpleAuth[auth-simple.js]
SimpleForms[forms.js]
end
subgraph "Advanced Authentication"
AdvancedAuth[auth-advanced.js]
AdvancedForms[forms.js]
JsonEditor[json-editor.js]
CredentialDisplay[credential-display.js]
end
subgraph "Shared Utilities"
State[state.js]
Navigation[navigation.js]
LocalStorage[local-storage.js]
UI[ui.js]
BinaryUtils[binary-utils.js]
Status[status.js]
Loader[loader.js]
Username[username.js]
WebAuthnPonyfill[webauthn-json.browser-ponyfill.js]
CredentialArtifacts[credential-artifacts-client.js]
AuthDebug[auth-debug.js]
end
MainJS --> SimpleAuth
MainJS --> AdvancedAuth
MainJS --> State
MainJS --> Navigation
MainJS --> UI
MainJS --> Loader
SimpleAuth --> WebAuthnPonyfill
SimpleAuth --> LocalStorage
SimpleAuth --> Status
AdvancedAuth --> WebAuthnPonyfill
AdvancedAuth --> JsonEditor
AdvancedAuth --> CredentialDisplay
AdvancedAuth --> Forms
AdvancedAuth --> Status
JsonEditor --> BinaryUtils
JsonEditor --> CredentialDisplay
JsonEditor --> Forms
CredentialDisplay --> LocalStorage
CredentialDisplay --> CredentialArtifacts
CredentialDisplay --> BinaryUtils
Forms --> BinaryUtils
Forms --> Status
Forms --> Username
Diagram sources
- main.js
- auth-simple.js
- auth-advanced.js
The JavaScript modules are organized into three primary directories within the scripts/static/scripts folder:
Contains the basic authentication implementation:
- auth-simple.js: Core authentication flow for simple scenarios
- forms.js: Form validation and manipulation utilities
Contains sophisticated authentication features:
- auth-advanced.js: Advanced authentication with WebAuthn extensions
- forms.js: Enhanced form handling with validation
- json-editor.js: JSON configuration editor
- credential-display.js: Credential management and display
- credential-utils.js: Credential utility functions
- display-utils.js: UI display utilities
- exclude-credentials.js: Credential exclusion management
- hints.js: Authenticator hint processing
- constants.js: Shared constants
- mds.js: Metadata service integration
- settings-nav.js: Settings navigation
Contains reusable utilities and state management:
- state.js: Global application state
- navigation.js: UI navigation and routing
- local-storage.js: Persistent storage management
- ui.js: User interface utilities
- binary-utils.js: Binary data conversion
- status.js: Status and progress indicators
- loader.js: Application loading state
- username.js: Username generation utilities
- webauthn-json.browser-ponyfill.js: WebAuthn API compatibility
- credential-artifacts-client.js: Credential artifact management
- auth-debug.js: Debugging utilities
Section sources
- main.js
The main.js file serves as the central orchestrator for the entire JavaScript architecture, establishing the foundation for all authentication flows and UI interactions.
The main entry point imports and initializes all core modules:
sequenceDiagram
participant Browser as Browser
participant MainJS as main.js
participant State as state.js
participant Navigation as navigation.js
participant UI as ui.js
participant AuthSimple as auth-simple.js
participant AuthAdvanced as auth-advanced.js
Browser->>MainJS : DOMContentLoaded
MainJS->>State : Initialize global state
MainJS->>Navigation : Initialize navigation menu
MainJS->>UI : Initialize UI components
MainJS->>AuthSimple : Import simple auth functions
MainJS->>AuthAdvanced : Import advanced auth functions
MainJS->>MainJS : Expose global functions
MainJS->>Browser : Ready for user interaction
Diagram sources
- main.js
The main entry point exposes all public functions to the global window object, enabling seamless integration with Flask backend routes:
| Function Category | Functions | Purpose |
|---|---|---|
| Authentication |
simpleRegister, simpleAuthenticate
|
Basic authentication operations |
| Authentication |
advancedRegister, advancedAuthenticate
|
Advanced authentication with extensions |
| Navigation |
switchTab, switchSubTab, toggleSection
|
UI navigation and routing |
| UI Management |
showInfoPopup, hideInfoPopup, closeModal
|
User interface interactions |
| Form Management |
randomizeChallenge, randomizeUserIdentity
|
Form data manipulation |
| Credential Management |
loadSavedCredentials, showCredentialDetails
|
Credential operations |
The architecture includes sophisticated spellcheck management to prevent browser interference with cryptographic data:
// Disable browser spellchecking globally (including dynamically added inputs)
const shouldDisableSpellcheck = (element) => {
if (element instanceof HTMLTextAreaElement) {
return true;
}
if (element instanceof HTMLInputElement) {
return TEXT_INPUT_TYPES.has((element.type || 'text').toLowerCase());
}
if (!(element instanceof HTMLElement)) {
return false;
}
if (element.isContentEditable) {
return true;
}
return element.getAttribute('role') === 'textbox';
};Section sources
- main.js
The authentication architecture implements two distinct flows: simple and advanced, each serving different use cases and security requirements.
The simple authentication flow provides streamlined WebAuthn operations with minimal configuration:
sequenceDiagram
participant User as User
participant UI as UI Layer
participant SimpleAuth as auth-simple.js
participant WebAuthn as WebAuthn API
participant Backend as Flask Backend
participant Storage as Local Storage
User->>UI : Enter email and click register/authenticate
UI->>SimpleAuth : Call simpleRegister/simpleAuthenticate
SimpleAuth->>Backend : POST /api/register/begin
Backend-->>SimpleAuth : Registration options
SimpleAuth->>WebAuthn : navigator.credentials.create/get
WebAuthn-->>SimpleAuth : Credential/Assertion
SimpleAuth->>Backend : POST /api/register/complete
Backend-->>SimpleAuth : Registration result
SimpleAuth->>Storage : Save credential locally
SimpleAuth-->>UI : Success/failure message
UI-->>User : Display result
Diagram sources
- auth-simple.js
The simple authentication flow handles basic registration and authentication with automatic credential management:
// Registration flow example
export async function simpleRegister() {
const email = document.getElementById('simple-email').value;
if (!email) {
showStatus('simple', 'Please enter a username.', 'error');
return;
}
try {
// Fetch registration options from backend
const response = await fetch(`/api/register/begin?email=${encodeURIComponent(email)}`);
// Parse and process WebAuthn options
const json = await response.json();
const createOptions = parseCreationOptionsFromJSON(optionsWithoutState);
// Execute WebAuthn registration
const credential = await create(createOptions);
// Complete registration with backend
const result = await fetch(`/api/register/complete?email=${encodeURIComponent(email)}`, {
method: 'POST',
body: JSON.stringify(credentialJson)
});
// Handle success and save credential
if (result.ok) {
const data = await result.json();
saveSimpleCredential({ ...data.storedCredential, email });
loadSavedCredentials();
}
} catch (error) {
// Handle errors gracefully
showStatus('simple', errorMessage, 'error');
}
}The advanced authentication flow supports complex WebAuthn configurations with extensive extension capabilities:
sequenceDiagram
participant User as User
participant UI as UI Layer
participant AdvancedAuth as auth-advanced.js
participant JsonEditor as json-editor.js
participant WebAuthn as WebAuthn API
participant Backend as Flask Backend
participant Storage as Local Storage
User->>UI : Configure advanced options
UI->>JsonEditor : Update JSON configuration
JsonEditor->>AdvancedAuth : Trigger validation
AdvancedAuth->>Backend : POST /api/advanced/register/begin
Backend-->>AdvancedAuth : Advanced options
AdvancedAuth->>WebAuthn : navigator.credentials.create/get
WebAuthn-->>AdvancedAuth : Credential/Assertion
AdvancedAuth->>Backend : POST /api/advanced/register/complete
Backend-->>AdvancedAuth : Extended result
AdvancedAuth->>Storage : Store with artifacts
AdvancedAuth-->>UI : Display detailed result
UI-->>User : Show registration details
Diagram sources
- auth-advanced.js
The advanced authentication flow supports:
| Feature | Description | Implementation |
|---|---|---|
| JSON Configuration | Full WebAuthn option customization | Dynamic JSON editor with validation |
| Extension Support | PRF, Large Blob, Cred Props extensions | Automatic extension detection and processing |
| Credential Artifacts | Persistent credential metadata | Server-side artifact storage |
| Multi-algorithm Support | Post-quantum and classical algorithms | Algorithm selection and validation |
| Hint Processing | Authenticator hint filtering | Intelligent authenticator selection |
Section sources
- auth-simple.js
- auth-advanced.js
The shared utility modules provide foundational functionality that supports both simple and advanced authentication flows.
The global state management system maintains application-wide data:
export const state = {
currentSubTab: 'registration',
storedCredentials: [],
currentJsonMode: null,
currentJsonData: null,
lastFakeCredLength: 0,
generatedExcludeCredentials: [],
generatedAllowCredentials: [],
utf8Decoder: typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8') : null,
};The navigation system handles UI routing and tab management:
flowchart TD
Start([User Interaction]) --> CheckViewport{Mobile Viewport?}
CheckViewport --> |Yes| MobileNav[Mobile Navigation]
CheckViewport --> |No| DesktopNav[Desktop Navigation]
MobileNav --> ToggleMenu[Toggle Menu]
DesktopNav --> TabSwitch[Switch Tab]
ToggleMenu --> UpdateState[Update Body Classes]
TabSwitch --> UpdateState
UpdateState --> HideAllMessages[Hide Transient Messages]
HideAllMessages --> ScrollReset[Reset Scroll Position]
ScrollReset --> DispatchEvent[Dispatch Tab Changed Event]
DispatchEvent --> End([Navigation Complete])
Diagram sources
- navigation.js
The local storage system manages credential persistence with artifact synchronization:
classDiagram
class LocalStorageManager {
+saveSimpleCredential(credential)
+saveAdvancedCredential(credential)
+getAllStoredCredentialsInOrder()
+removeAdvancedCredential(id)
+ensureAdvancedCredentialArtifactsSynced()
-persistUnifiedCredentialRecords(records)
-synchroniseAdvancedCredentialArtifacts()
}
class CredentialArtifactsClient {
+fetchCredentialArtifact(storageId)
+uploadCredentialArtifact(storageId, artifact)
+updateCredentialSnapshot(storageId, snapshot)
+deleteCredentialArtifact(storageId)
}
class StorageConstants {
+SHARED_STORAGE_KEY : "postquantum-webauthn.credentials"
+LEGACY_SIMPLE_STORAGE_KEY : "postquantum-webauthn.simpleCredentials"
+LEGACY_ADVANCED_STORAGE_KEY : "postquantum-webauthn.advancedCredentials"
}
LocalStorageManager --> CredentialArtifactsClient : uses
LocalStorageManager --> StorageConstants : references
Diagram sources
- local-storage.js
- credential-artifacts-client.js
The binary utilities provide comprehensive data conversion capabilities:
| Function Category | Purpose | Examples |
|---|---|---|
| Hex Operations | Hexadecimal data handling |
hexToBase64Url, base64UrlToHex, generateRandomHex
|
| Base64 Encoding | Base64 URL encoding |
base64ToBase64Url, base64UrlToUtf8String
|
| Binary Conversion | Type conversions |
bufferSourceToUint8Array, arrayBufferToHex
|
| Extension Processing | WebAuthn extension handling |
convertExtensionsForClient, normalizeClientExtensionResults
|
The WebAuthn ponyfill ensures cross-browser compatibility:
// Core WebAuthn API wrapper
export async function create(options) {
const response = await navigator.credentials.create(options);
response.toJSON = () => createResponseToJSON(response);
return response;
}
export async function get(options) {
const response = await navigator.credentials.get(options);
response.toJSON = () => getResponseToJSON(response);
return response;
}Section sources
- state.js
- navigation.js
- local-storage.js
- binary-utils.js
- webauthn-json.browser-ponyfill.js
The advanced authentication system provides sophisticated features for complex WebAuthn scenarios.
The JSON editor enables dynamic WebAuthn configuration:
flowchart TD
UserInput[User Input] --> Validation[JSON Validation]
Validation --> SchemaCheck{Valid Schema?}
SchemaCheck --> |No| ErrorDisplay[Display Error]
SchemaCheck --> |Yes| MergeLogic[Merge with Current Config]
MergeLogic --> KnownKeys[Validate Known Keys]
KnownKeys --> UnknownKeys[Handle Unknown Keys]
UnknownKeys --> PruneUnsupported[Prune Unsupported Properties]
PruneUnsupported --> RenderOutput[Render JSON Output]
ErrorDisplay --> UserInput
RenderOutput --> ApplyChanges[Apply Changes]
ApplyChanges --> UpdateForm[Update Form Fields]
UpdateForm --> TriggerValidation[Trigger Validation]
Diagram sources
- json-editor.js
The credential management system handles complex credential lifecycle operations:
sequenceDiagram
participant UI as UI Layer
participant CredentialDisplay as credential-display.js
participant LocalStorage as local-storage.js
participant ArtifactClient as credential-artifacts-client.js
participant Backend as Flask Backend
UI->>CredentialDisplay : Load saved credentials
CredentialDisplay->>LocalStorage : getAllStoredCredentialsInOrder()
LocalStorage-->>CredentialDisplay : Credential list
loop For each credential
CredentialDisplay->>ArtifactClient : fetchCredentialArtifact(storageId)
ArtifactClient->>Backend : GET /api/advanced/credential-artifacts/{id}
Backend-->>ArtifactClient : Credential artifact
ArtifactClient-->>CredentialDisplay : Hydrated credential
end
CredentialDisplay-->>UI : Display credential list
Diagram sources
- credential-display.js
The form validation system provides comprehensive input validation:
| Validation Type | Scope | Implementation |
|---|---|---|
| Hex Validation | Binary inputs | Format detection and length validation |
| Challenge Validation | Authentication challenges | Minimum length requirements |
| Algorithm Validation | Signature algorithms | Supported algorithm checking |
| Extension Validation | WebAuthn extensions | Capability detection and validation |
Section sources
- json-editor.js
- credential-display.js
- forms.js
The JavaScript architecture demonstrates several key integration patterns that enable seamless communication between frontend and backend systems.
The frontend integrates with Flask backend routes through standardized HTTP APIs:
sequenceDiagram
participant Frontend as JavaScript Frontend
participant Flask as Flask Backend
participant Database as Database
participant WebAuthn as WebAuthn Service
Frontend->>Flask : POST /api/register/begin
Flask->>Database : Check user existence
Database-->>Flask : User status
Flask->>WebAuthn : Generate challenge
WebAuthn-->>Flask : Registration options
Flask-->>Frontend : JSON registration options
Frontend->>Flask : POST /api/register/complete
Flask->>WebAuthn : Verify credential
WebAuthn-->>Flask : Verification result
Flask->>Database : Store credential
Database-->>Flask : Confirmation
Flask-->>Frontend : Registration success
The architecture implements comprehensive error handling across all modules:
// Consistent error handling pattern
try {
// WebAuthn operation
const result = await fetch('/api/endpoint', {
method: 'POST',
body: JSON.stringify(data)
});
if (!result.ok) {
const errorText = await result.text();
throw new Error(`Server error: ${errorText}`);
}
return await result.json();
} catch (error) {
// Graceful error handling
let errorMessage = error.message;
if (error.name === 'NotAllowedError') {
errorMessage = 'User cancelled or authenticator not available';
}
showStatus('tab', errorMessage, 'error');
}The architecture manages complex asynchronous operations with proper state handling:
flowchart TD
AsyncOperation[Async Operation] --> ShowProgress[Show Progress Indicator]
ShowProgress --> ExecuteOperation[Execute Operation]
ExecuteOperation --> CheckResult{Operation Successful?}
CheckResult --> |Yes| ProcessResult[Process Result]
CheckResult --> |No| HandleError[Handle Error]
ProcessResult --> UpdateUI[Update UI]
HandleError --> ShowError[Show Error Message]
UpdateUI --> HideProgress[Hide Progress Indicator]
ShowError --> HideProgress
HideProgress --> CleanupState[Cleanup State]
CleanupState --> End([Operation Complete])
Section sources
- auth-simple.js
- auth-advanced.js
The architecture provides comprehensive error handling and debugging capabilities.
The error handling system categorizes errors by type and provides appropriate user feedback:
| Error Category | WebAuthn Error | User Message | Recovery Action |
|---|---|---|---|
| User Cancellation | NotAllowedError |
"User cancelled or authenticator not available" | Retry operation |
| Authentication Issue | InvalidStateError |
"Authenticator is already registered for this account" | Check existing credentials |
| Security Error | SecurityError |
"Security error - check your connection and try again" | Verify network connection |
| Browser Support | NotSupportedError |
"WebAuthn is not supported in this browser" | Use supported browser |
The debugging system provides detailed logging for development and troubleshooting:
// Registration debug output
export function printRegistrationDebug(credential, createOptions, serverResponse) {
const clientExtensions = credential.getClientExtensionResults
? credential.getClientExtensionResults()
: (credential.clientExtensionResults || {});
console.log('Resident key:', residentKey);
console.log('Attestation (retrieve or not, plus the format):', `${attestationRetrieved}, ${attestationFormat}`);
console.log('exclude credentials:', excludeCredentials);
console.log('challenge hex code:', challengeHex);
console.log('pubkeycredparam used:', pubKeyCredParams);
console.log('hints:', hints);
console.log('credprotect setting:', credProtectDisplay);
console.log('largeblob:', largeBlob);
console.log('prf:', prfEnabled);
}The status system provides real-time feedback during operations:
stateDiagram-v2
[*] --> Hidden
Hidden --> Showing : showStatus()
Showing --> Visible : Animation
Visible --> Dismissed : Auto-hide (10s)
Showing --> Dismissed : hideStatus()
Dismissed --> Hidden : Reset
[*] --> HiddenProgress
HiddenProgress --> ShowingProgress : showProgress()
ShowingProgress --> VisibleProgress : Display
VisibleProgress --> HiddenProgress : hideProgress()
Diagram sources
- status.js
Section sources
- auth-debug.js
- status.js
The modular architecture supports extension through well-defined interfaces and patterns.
To add new authentication features, follow the established patterns:
- Create Feature Module: Add new functionality to appropriate directory
- Extend State Management: Update state.js with new state properties
- Integrate with Navigation: Add navigation handlers if needed
- Add UI Components: Create corresponding UI elements
- Update Backend Integration: Extend Flask routes as needed
The architecture supports adding new WebAuthn extensions through the extension processing system:
// Example extension processing pattern
export function convertExtensionsForClient(extensionsJson) {
if (!extensionsJson || typeof extensionsJson !== 'object') {
return undefined;
}
const converted = {};
Object.entries(extensionsJson).forEach(([name, value]) => {
switch (name) {
case 'newExtension':
converted.newExtension = processNewExtension(value);
break;
// ... other extensions
}
});
return Object.keys(converted).length > 0 ? converted : undefined;
}The authentication flows can be customized by extending the base functionality:
// Custom authentication flow extension
export async function customRegister() {
// Pre-processing
await preprocessCustomRequirements();
// Standard registration flow
const result = await simpleRegister();
// Post-processing
await postprocessCustomResults(result);
return result;
}The architecture demonstrates several JavaScript best practices that contribute to maintainability and reliability.
- Single Responsibility: Each module has a clear, focused purpose
- Loose Coupling: Modules communicate through well-defined interfaces
- High Cohesion: Related functionality is grouped within modules
- Separation of Concerns: Authentication logic is separated from UI logic
// Consistent export/import pattern
import { create, get } from './webauthn-json.browser-ponyfill.js';
import { showStatus, hideStatus } from './status.js';
export async function advancedRegister() {
// Implementation
}
// Clear function naming and documentation
/**
* Performs advanced registration with WebAuthn extensions
* @returns {Promise<Object>} Registration result
*/
export async function advancedRegister() {
// Implementation
}- Graceful Degradation: Provide fallbacks for unsupported features
- User-Friendly Messages: Convert technical errors to understandable messages
- Consistent Logging: Use structured logging for debugging
- Recovery Mechanisms: Provide clear paths for error recovery
- Lazy Loading: Load modules only when needed
- Efficient State Management: Minimize unnecessary state updates
- Memory Management: Properly clean up event listeners and timers
- Optimized Rendering: Use efficient DOM manipulation techniques
- Input Validation: Validate all user inputs and API responses
- Secure Storage: Use appropriate storage mechanisms for sensitive data
- Error Information: Avoid exposing sensitive information in error messages
- Cross-Site Scripting: Sanitize all user-generated content
The JavaScript architecture successfully demonstrates a sophisticated, maintainable approach to WebAuthn implementation that balances simplicity for basic use cases with powerful capabilities for advanced scenarios. The modular design, comprehensive error handling, and robust integration patterns provide a solid foundation for future enhancements and extensions.