Skip to main content

JavaScript Package

Synced from the repository

This page mirrors modules/authentication/pkg/javascript/README.md in the experimental-modules repo (branch develop). The repository is the source of truth.

Auki Network authentication library for JavaScript/TypeScript. Works in both Browser and Node.js environments.

This library provides authentication to the Auki Network's hierarchical authentication system:

  1. Network Authentication - Authenticate to the Auki API
  2. Discovery Authentication - Authenticate to the Discovery service
  3. Domain Access - Get access tokens for specific domains

Installation

npm install @auki/authentication

Or from a local package:

npm install /path/to/auki-authentication-0.1.0.tgz

Usage

There are two ways to use this library:

  1. High-level Client API (recommended) - Async/await API that handles HTTP requests automatically
  2. Low-level Bindings API - Sans-I/O core for custom HTTP client integration

The Client class provides a convenient async API that manages HTTP requests internally.

Quick Start

import { Client } from "@auki/authentication";

// Create client with configuration (async for Node.js)
const client = await Client.create({
apiUrl: "https://api.aukiverse.com",
refreshUrl: "https://api.aukiverse.com/user/refresh",
ddsUrl: "https://dds.posemesh.org",
clientId: "my-app",
});

// Set credentials
client.setCredentials({
type: "email",
email: "user@example.com",
password: "secret",
});

// Get domain access - automatically handles full authentication chain!
const domainAccess = await client.getDomainAccess("my-domain-id");
console.log("Connected to:", domainAccess.domain_server.url);
console.log("Access token:", domainAccess.access_token);

Alternative: Use the convenience method to create a client with credentials in one step:

const client = await Client.withCredentials(
{ type: "email", email: "user@example.com", password: "secret" },
{
apiUrl: "https://api.aukiverse.com",
refreshUrl: "https://api.aukiverse.com/user/refresh",
ddsUrl: "https://dds.posemesh.org",
clientId: "my-app",
}
);

Basic Usage

import { Client } from "@auki/authentication";

// 1. Configure the client
const config = {
apiUrl: "https://api.aukiverse.com",
refreshUrl: "https://api.aukiverse.com/user/refresh",
ddsUrl: "https://dds.posemesh.org",
clientId: "my-app",
refreshThresholdMs: 300000, // Optional: 5 minutes
};

// 2. Create client (async for Node.js)
const client = await Client.create(config);

// 3. Set credentials
const credentials =
// Email/Password
{ type: "email", email: "user@example.com", password: "secret" };
// OR App Key
// { type: 'appKey', appKey: 'your-key', appSecret: 'your-secret' }
// OR Opaque Token
// { type: 'opaque', token: 'your-token', expiryMs: 1234567890 }
client.setCredentials(credentials);

try {
// Get domain access - automatically handles the full chain:
// 1. Network authentication (if needed)
// 2. Discovery authentication (if needed)
// 3. Domain access request
const domainAccess = await client.getDomainAccess("my-domain-id");

console.log("Domain access granted!");
console.log(" Server:", domainAccess.domain_server.name);
console.log(" URL:", domainAccess.domain_server.url);
console.log(" Region:", domainAccess.domain_server.cloud_region);
console.log(" Access Token:", domainAccess.access_token);
} catch (error) {
console.error("Authentication failed:", error.message);
if (error.retryable) {
// This error can be retried
}
}

Manual Step-by-Step (Optional)

For advanced use cases where you need explicit control over each step:

// Step 1: Authenticate to network only
const networkToken = await client.authenticate();
console.log("Network token expires at:", networkToken.expires_at);

// Step 2: Authenticate to discovery only
const discoveryToken = await client.authenticateDiscovery();
console.log("Discovery token expires at:", discoveryToken.expires_at);

// Step 3: Get domain access (assumes prior auth)
const domainAccess = await client.getDomainAccess("my-domain-id");

Note: In most cases, you only need getDomainAccess() - it automatically handles all prerequisite authentication steps.

State Persistence

Save and restore authentication state (useful for avoiding re-authentication):

// Save state (excludes credentials)
const stateJson = client.saveState();
localStorage.setItem("auth-state", stateJson);

// Restore state later
const savedState = localStorage.getItem("auth-state");
const client = Client.fromState(savedState, config);

// Validate state (clears expired tokens)
client.validateState();

// Check if still authenticated
if (client.isAuthenticated()) {
console.log("Still authenticated!");
} else {
// Need to re-authenticate
await client.authenticate();
}

Token Refresh Monitoring

client.onRefreshFailed((info) => {
console.log(`${info.tokenType} token refresh failed:`, info.reason);

if (info.requiresReauth) {
// Token expired, need full re-authentication
client.authenticate().catch(console.error);
}
});

Checking Tokens

// Check authentication state
const isAuth = client.isAuthenticated();

// Get cached tokens
const networkToken = client.getNetworkToken();
if (networkToken) {
console.log("Token:", networkToken.token);
console.log("Expires:", networkToken.expires_at);
}

const discoveryToken = client.getDiscoveryToken();
const domainAccess = client.getCachedDomainAccess("domain-id");

Utility Functions

import { currentTimeMs, isExpired, isNearExpiry } from "@auki/authentication";

// Get current time
const now = currentTimeMs();

// Check if token is expired
if (isExpired(token.expires_at)) {
console.log("Token expired");
}

// Check if token needs refresh
if (isNearExpiry(token.expires_at, 300000)) {
// 5 minutes
console.log("Token expires soon");
}

2. Low-Level Bindings API (Advanced)

For advanced use cases where you need full control over HTTP requests, you can use the underlying WASM bindings directly. This is a sans-I/O API where you handle all I/O operations.

Import Raw Bindings

import {
WasmClient,
WasmConfig,
WasmCredentials,
} from "@auki/authentication/dist/bindings/authentication.js";

Sans-I/O Pattern

import {
WasmClient,
WasmConfig,
WasmCredentials,
} from "@auki/authentication/dist/bindings/authentication.js";

// 1. Create credentials
const credentials = WasmCredentials.email_password(
"user@example.com",
"password"
);

// 2. Create config
const config = new WasmConfig(
"https://api.aukiverse.com",
"https://dds.posemesh.org",
"my-client-id"
);
config.refresh_threshold_ms = BigInt(300000); // 5 minutes

// 3. Create client
const client = new WasmClient(credentials, config);

// 4. Get actions to perform
const actions = client.authenticate(BigInt(Date.now()));

// 5. Execute actions (you handle HTTP)
for (const action of actions) {
if (action.type === "HttpRequest") {
// Use your own HTTP client
const response = await yourHttpClient.request({
url: action.url,
method: action.method,
headers: action.headers,
body: action.body,
});

// 6. Feed response back to client
const events = client.handle_response(response.status, response.text);

// 7. Process events
for (const event of events) {
if (event.type === "NetworkAuthSuccess") {
console.log("Token:", event.token);
console.log("Expires:", event.expires_at);
} else if (event.type === "NetworkAuthFailed") {
console.error("Failed:", event.reason);
}
}
} else if (action.type === "Wait") {
// Handle retry delays
await new Promise((r) => setTimeout(r, action.duration_ms));
}
}

Available Methods

// Authentication flow
client.authenticate(now_ms: bigint): Action[]
client.authenticate_discovery(now_ms: bigint): Action[]
client.get_domain_access(domain_id: string, now_ms: bigint): Action[]

// Handle HTTP responses
client.handle_response(status: number, body: string): Event[]

// State queries
client.is_authenticated(now_ms: bigint): boolean
client.check_auth_state(now_ms: bigint): WasmAuthenticationState
client.network_token(): Token | null
client.discovery_token(): Token | null
client.domain_access(domain_id: string): DomainAccess | null
client.all_domains(): DomainAccess[]

// State management
client.save_state(): string
client.validate_state(now_ms: bigint): Event[]
WasmClient.from_state(state_json: string, config: WasmConfig): WasmClient

// Credentials
client.set_credentials(credentials: WasmCredentials): void
client.has_credentials(): boolean
client.requires_credentials(now_ms: bigint): boolean

// Control
client.force_reauth(): Event[]
client.clear_domain_access(domain_id: string): void
client.clear_all_domain_accesses(): void

Action Types

type Action =
| {
type: "HttpRequest";
url: string;
method: string;
headers: object;
body?: string;
}
| { type: "Wait"; duration_ms: number };

Event Types

type Event =
| { type: "NetworkAuthSuccess"; token: string; expires_at: number }
| { type: "NetworkAuthFailed"; reason: string; retry_possible: boolean }
| { type: "NetworkTokenRefreshed"; token: string; expires_at: number }
| {
type: "NetworkTokenRefreshFailed";
reason: string;
requires_reauth: boolean;
}
| { type: "DiscoveryAuthSuccess"; token: string; expires_at: number }
| { type: "DiscoveryAuthFailed"; reason: string }
| { type: "DomainAccessGranted"; domain: DomainAccess }
| { type: "DomainAccessDenied"; domain_id: string; reason: string }
| { type: "AuthenticationRequired" }
| { type: "TokensInvalidated" };

TypeScript Support

This library is written in TypeScript and includes full type definitions.

import type {
Client,
ClientConfig,
Credentials,
Token,
DomainAccess,
DomainServer,
AuthenticationError,
RefreshFailureInfo,
} from "@auki/authentication";

Examples

See the examples/ directory for complete working examples:

  • basic.js - Complete authentication flow with all steps
  • test_refresh.js - Token refresh testing
  • test_refresh_expired.js - Expired token handling

Running Examples

  1. Copy environment template:

    cp ../../.env.example ../../.env
  2. Edit .env with your credentials:

    API_URL=https://api.aukiverse.com
    DDS_URL=https://dds.posemesh.org
    EMAIL=your@email.com
    PASSWORD=your-password
    DOMAIN_ID=your-domain-id
  3. Run example:

    npm run example:basic

Error Handling

import { AuthenticationError } from "@auki/authentication";

try {
await client.authenticate();
} catch (error) {
if (error instanceof AuthenticationError) {
console.error("Auth error:", error.message);
console.log("Retryable:", error.retryable);
}
}

Browser vs Node.js

This library works in both environments:

Browser:

<script type="module">
import { Client } from "@auki/authentication";
// Use client...
</script>

Node.js:

import { Client } from "@auki/authentication";
// or
const { Client } = require("@auki/authentication");

Architecture

This library uses a sans-I/O architecture at its core:

  • The Rust core implements all authentication logic without I/O
  • The TypeScript wrapper provides a convenient async API
  • HTTP requests use the standard fetch API (works in browsers and Node.js 18+)
  • You can use the low-level bindings for custom HTTP client integration

Building from Source

# From repository root
make javascript

# Package it
cd pkg/javascript && npm pack

See the root README.md for more details.


Support