@tenxyte/core
The official core JavaScript/TypeScript SDK for the Tenxyte API — a unified platform for authentication, multi-tenant organizations, RBAC, GDPR compliance, and AI agent security.
Features
- Authentication — Email/password, phone, magic link, social OAuth2, registration
- Security — 2FA/TOTP, OTP verification, WebAuthn/Passkeys (FIDO2), password management
- RBAC — Role & permission management, synchronous JWT checks, user role assignment
- User Management — Profile CRUD, avatar upload, admin user operations
- B2B Multi-Tenancy — Organization CRUD, member management, invitations, context switching
- AI Agent Security (AIRS) — Agent tokens, circuit breakers, Human-in-the-Loop, usage reporting
- Applications — API client management, credential regeneration
- Admin — Audit logs, login attempts, blacklisted/refresh token management
- GDPR — Account deletion flows, data export, admin deletion request processing
- Dashboard — Global, auth, security, GDPR, and per-org statistics
Installation
npm install @tenxyte/core
yarn add @tenxyte/core
pnpm add @tenxyte/core
Quick Start
import { TenxyteClient } from '@tenxyte/core';
const tx = new TenxyteClient({
baseUrl: 'https://api.my-backend.com',
headers: { 'X-Access-Key': 'your-public-app-key' },
});
const tokens = await tx.auth.loginWithEmail({
email: 'user@example.com',
password: 'secure_password!',
device_info: '',
});
const isLoggedIn = await tx.isAuthenticated();
const user = await tx.getCurrentUser();
Important: Never expose X-Access-Secret in frontend bundles. Use it exclusively server-side.
Configuration
The TenxyteClient accepts a single configuration object. Only baseUrl is required.
const tx = new TenxyteClient({
baseUrl: 'https://api.my-service.com',
headers: { 'X-Access-Key': 'pkg_abc123' },
storage: new LocalStorageAdapter(),
autoRefresh: true,
autoDeviceInfo: true,
timeoutMs: 10_000,
retryConfig: { maxRetries: 3, baseDelayMs: 500 },
onSessionExpired: () => router.push('/login'),
logger: console,
logLevel: 'debug',
deviceInfoOverride: { app_name: 'MyApp', app_version: '2.0.0' },
});
Modules
Authentication (tx.auth)
const tokens = await tx.auth.loginWithEmail({
email: 'user@example.com',
password: 'password123',
device_info: '',
totp_code: '123456',
});
const tokens = await tx.auth.loginWithPhone({
phone_country_code: '+1',
phone_number: '5551234567',
password: 'password123',
device_info: '',
});
const result = await tx.auth.register({
email: 'new@example.com',
password: 'StrongP@ss1',
first_name: 'Jane',
last_name: 'Doe',
});
await tx.auth.requestMagicLink({ email: 'user@example.com', validation_url: 'https://myapp.com/verify' });
const tokens = await tx.auth.verifyMagicLink(urlToken);
const tokens = await tx.auth.loginWithSocial('google', { id_token: 'jwt...' });
const tokens = await tx.auth.handleSocialCallback('github', 'auth_code', 'https://myapp.com/cb');
await tx.auth.logout('refresh_token_value');
await tx.auth.logoutAll();
await tx.auth.refreshToken('refresh_token_value');
Security (tx.security)
const status = await tx.security.get2FAStatus();
const { secret, qr_code_url, backup_codes } = await tx.security.setup2FA();
await tx.security.confirm2FA('123456');
await tx.security.disable2FA('123456');
await tx.security.requestOtp({ delivery_method: 'email', purpose: 'login' });
const result = await tx.security.verifyOtp({ otp: '123456', purpose: 'login' });
await tx.security.resetPasswordRequest({ email: 'user@example.com' });
await tx.security.resetPasswordConfirm({ token: '...', new_password: 'NewP@ss1' });
await tx.security.changePassword({ old_password: 'old', new_password: 'new' });
await tx.security.registerWebAuthn('My Laptop');
const session = await tx.security.authenticateWebAuthn('user@example.com');
const creds = await tx.security.listWebAuthnCredentials();
await tx.security.deleteWebAuthnCredential(credentialId);
RBAC (tx.rbac)
tx.rbac.setToken(accessToken);
const isAdmin = tx.rbac.hasRole('admin');
const canEdit = tx.rbac.hasPermission('users.edit');
const hasAny = tx.rbac.hasAnyRole(['admin', 'manager']);
const hasAll = tx.rbac.hasAllRoles(['admin', 'superadmin']);
const roles = await tx.rbac.listRoles();
await tx.rbac.createRole({ code: 'editor', name: 'Editor' });
await tx.rbac.assignRoleToUser('user-id', 'editor');
await tx.rbac.removeRoleFromUser('user-id', 'editor');
const permissions = await tx.rbac.listPermissions();
await tx.rbac.assignPermissionsToUser('user-id', ['posts.create', 'posts.edit']);
await tx.rbac.removePermissionsFromUser('user-id', ['posts.create']);
const userRoles = await tx.rbac.getUserRoles('user-id');
const userPerms = await tx.rbac.getUserPermissions('user-id');
User Management (tx.user)
const profile = await tx.user.getProfile();
await tx.user.updateProfile({ first_name: 'Updated' });
await tx.user.uploadAvatar(fileFormData);
await tx.user.deleteAccount('my-password');
const myRoles = await tx.user.getMyRoles();
const users = await tx.user.listUsers({ page: 1, page_size: 20 });
const user = await tx.user.getUser('user-id');
await tx.user.adminUpdateUser('user-id', { is_active: false });
await tx.user.adminDeleteUser('user-id');
await tx.user.banUser('user-id', 'spam');
B2B Organizations (tx.b2b)
tx.b2b.switchOrganization('acme-corp');
const slug = tx.b2b.getCurrentOrganizationSlug();
tx.b2b.clearOrganization();
const orgs = await tx.b2b.listOrganizations();
const org = await tx.b2b.createOrganization({ name: 'Acme Corp', slug: 'acme-corp' });
await tx.b2b.updateOrganization('acme-corp', { name: 'Acme Corp Inc.' });
await tx.b2b.deleteOrganization('acme-corp');
const members = await tx.b2b.listMembers('acme-corp');
await tx.b2b.addMember('acme-corp', { user_id: 'uid', role_code: 'member' });
await tx.b2b.updateMember('acme-corp', 'uid', { role_code: 'admin' });
await tx.b2b.removeMember('acme-corp', 'uid');
await tx.b2b.inviteMember('acme-corp', { email: 'dev@example.com', role_code: 'admin' });
const roles = await tx.b2b.listOrgRoles('acme-corp');
AI Agent Security (tx.ai)
const agentData = await tx.ai.createAgentToken({
agent_id: 'Invoice-Parser-Bot',
permissions: ['invoices.read', 'invoices.create'],
budget_limit_usd: 5.00,
circuit_breaker: { max_requests: 100, window_seconds: 60 },
});
tx.ai.setAgentToken(agentData.token);
tx.ai.isAgentMode();
tx.ai.clearAgentToken();
const tokens = await tx.ai.listAgentTokens();
const token = await tx.ai.getAgentToken('token-id');
await tx.ai.revokeAgentToken('token-id');
await tx.ai.suspendAgentToken('token-id');
await tx.ai.revokeAllAgentTokens();
const pending = await tx.ai.listPendingActions();
await tx.ai.confirmPendingAction('confirmation-token');
await tx.ai.denyPendingAction('confirmation-token');
await tx.ai.sendHeartbeat('token-id');
await tx.ai.reportUsage('token-id', {
cost_usd: 0.015,
prompt_tokens: 1540,
completion_tokens: 420,
});
tx.ai.setTraceId('trace-1234');
tx.ai.clearTraceId();
Applications (tx.applications)
const apps = await tx.applications.listApplications();
const app = await tx.applications.createApplication({
name: 'My API Client',
description: 'Backend service',
});
const detail = await tx.applications.getApplication('app-id');
await tx.applications.updateApplication('app-id', { name: 'Renamed' });
await tx.applications.patchApplication('app-id', { description: 'Updated desc' });
await tx.applications.deleteApplication('app-id');
const newCreds = await tx.applications.regenerateCredentials('app-id');
Admin (tx.admin)
const logs = await tx.admin.listAuditLogs({ page: 1 });
const log = await tx.admin.getAuditLog('log-id');
const attempts = await tx.admin.listLoginAttempts({ user_id: 'uid' });
const blacklisted = await tx.admin.listBlacklistedTokens();
await tx.admin.cleanupBlacklistedTokens();
const refreshTokens = await tx.admin.listRefreshTokens({ user_id: 'uid' });
await tx.admin.revokeRefreshToken('token-id');
GDPR (tx.gdpr)
await tx.gdpr.requestAccountDeletion({ reason: 'No longer needed' });
await tx.gdpr.confirmAccountDeletion('confirmation-code');
await tx.gdpr.cancelAccountDeletion();
const status = await tx.gdpr.getDeletionStatus();
const data = await tx.gdpr.exportUserData();
const requests = await tx.gdpr.listDeletionRequests({ status: 'pending' });
const request = await tx.gdpr.getDeletionRequest('request-id');
await tx.gdpr.processDeletionRequest('request-id', { action: 'approve' });
await tx.gdpr.processExpiredDeletions();
Dashboard (tx.dashboard)
const global = await tx.dashboard.getStats({ period: 'last_30_days' });
const auth = await tx.dashboard.getAuthStats();
const security = await tx.dashboard.getSecurityStats();
const gdpr = await tx.dashboard.getGdprStats();
const orgStats = await tx.dashboard.getOrganizationStats('acme-corp');
SDK Events
The SDK emits events via a built-in EventEmitter. Use tx.on(), tx.once(), and tx.off() to subscribe.
session:expired | void | Refresh token expired/revoked, session unrecoverable |
token:refreshed | { accessToken: string } | Access token silently rotated via auto-refresh |
token:stored | { accessToken: string; refreshToken?: string } | Tokens persisted after login, register, or refresh |
agent:awaiting_approval | { action: unknown } | AI agent action requires human confirmation (HTTP 202) |
error | { error: unknown } | Unrecoverable SDK error not tied to a specific call |
tx.on('session:expired', () => {
router.push('/login');
});
tx.on('token:refreshed', ({ accessToken }) => {
console.log('Token refreshed silently');
});
tx.on('agent:awaiting_approval', ({ action }) => {
showApprovalDialog(action);
});
High-Level Helpers
const isLoggedIn = await tx.isAuthenticated();
const token = await tx.getAccessToken();
const user = await tx.getCurrentUser();
const expired = await tx.isTokenExpired();
const state = await tx.getState();
Migration Guide: v0.9 → v1.0
Breaking Changes
-
Constructor signature changed — The client now accepts a TenxyteClientConfig object:
const tx = new TenxyteClient({ baseUrl: '...', headers: { ... } });
const tx = new TenxyteClient({
baseUrl: '...',
headers: { ... },
autoRefresh: true,
autoDeviceInfo: true,
retryConfig: { ... },
});
-
loginWithEmail now requires device_info:
await tx.auth.loginWithEmail({ email, password });
await tx.auth.loginWithEmail({ email, password, device_info: '' });
-
requestMagicLink now requires validation_url:
await tx.auth.requestMagicLink({ email });
await tx.auth.requestMagicLink({ email, validation_url: 'https://...' });
-
Auto-session management — Tokens are now automatically stored and the Authorization header is automatically injected. You no longer need to manage this manually.
-
New modules added — tx.applications, tx.admin, tx.gdpr, tx.dashboard are now available.
-
register() return type changed — Now returns RegisterResponse (may include tokens if auto-login is enabled).
New Features in v1.0
- Auto-refresh interceptor (silent 401 → refresh → retry)
- Configurable retry with exponential backoff (429/5xx)
- Device info auto-injection
- Pluggable logger with log levels
- High-level helpers (
isAuthenticated, getCurrentUser, isTokenExpired)
getState() for framework wrapper integration
- EventEmitter for reactive state (
session:expired, token:refreshed, etc.)
License
MIT