@glideidentity/glide-be-node-magical-auth
v2.0.0
Published
Glide Magical Auth SDK for Node.js - Phone number authentication without SMS/OTP
Readme
@glideidentity/glide-be-node-magical-auth
Glide Magical Auth SDK for Node.js — phone number authentication without SMS/OTP using carrier-based digital credentials.
Features
- Zero Dependencies - Uses only Node.js built-ins (Node 18+)
- TypeScript First - Full type safety with excellent IntelliSense support
- BYOC - Bring Your Own HTTP Client for custom implementations
- BYOL - Bring Your Own Logger for custom logging
- Automatic Token Management - OAuth2 tokens cached and refreshed automatically
- Thread-Safe - Safe for concurrent use with promise deduplication
Installation
npm install @glideidentity/glide-be-node-magical-authRequirements
- Node.js 18.0.0 or higher
Quick Start
import { MagicalAuthClient } from '@glideidentity/glide-be-node-magical-auth';
const client = new MagicalAuthClient({
clientId: process.env.GLIDE_CLIENT_ID!,
clientSecret: process.env.GLIDE_CLIENT_SECRET!,
});
// 1. Prepare authentication
const prepareResponse = await client.prepare({
phone_number: '+14155551234',
nonce: crypto.randomUUID(),
use_case: 'VerifyPhoneNumber',
client_info: {
user_agent: navigator.userAgent,
},
});
// 2. Handle based on authentication strategy
switch (prepareResponse.authentication_strategy) {
case 'ts43':
// Direct credential request via the Digital Credentials API
// (available in supported browsers and natively on Android via Credential Manager).
// Use the data.dcql_query payload to invoke the API on your client.
break;
case 'link':
// OAuth-style redirect flow on mobile.
// Redirect the user to prepareResponse.data.url.
break;
case 'desktop':
// QR-code flow for desktop browsers. The `data` field is a
// DesktopStrategyData wrapper; the QR payload lives at `data.data`:
// const desktop = prepareResponse.data; // DesktopStrategyData
// render(desktop.data.qr_image); // base64 PNG data URL
// showChallenge(prepareResponse.challenge?.pattern);
break;
}
// 3. Verify the phone number with credential from device
const verifyResponse = await client.verifyPhoneNumber({
session: prepareResponse.session,
credential: credentialFromDevice,
});
if (verifyResponse.verified) {
console.log('Phone verified:', verifyResponse.phone_number);
}API Reference
MagicalAuthClient
The main client class for interacting with the Magical Auth API.
Constructor
new MagicalAuthClient(config: MagicalAuthConfig)| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| clientId | string | Yes | - | OAuth2 client ID |
| clientSecret | string | Yes | - | OAuth2 client secret |
| baseUrl | string | No | https://api.glideidentity.app | API base URL |
| httpClient | HttpClient | No | Native fetch | Custom HTTP client |
| logger | Logger | No | No-op | Custom logger |
| timeout | number | No | 30000 | Request timeout in ms |
Methods
prepare(request: PrepareRequest): Promise<PrepareResponse>
Prepares authentication by checking carrier eligibility and generating protocol-specific requests.
const response = await client.prepare({
phone_number: '+14155551234', // Required for VerifyPhoneNumber
plmn: { mcc: '310', mnc: '260' }, // Optional carrier identifier
nonce: 'unique-nonce', // Required, base64url encoded
use_case: 'VerifyPhoneNumber', // or 'GetPhoneNumber'
client_info: { user_agent: '...' }, // Optional, helps strategy selection
consent_data: { ... }, // Optional consent text
options: { theme: 'dark' }, // Optional UI theme
});verifyPhoneNumber(request: VerifyPhoneNumberRequest): Promise<VerifyPhoneNumberResponse>
Verifies a phone number using digital credentials.
const result = await client.verifyPhoneNumber({
session: prepareResponse.session,
credential: 'sd-jwt-credential-from-device',
});
console.log(result.verified); // true or false
console.log(result.phone_number); // '+14155551234'
console.log(result.sim_swap?.risk_level); // 'RISK_LEVEL_LOW'getPhoneNumber(request: GetPhoneNumberRequest): Promise<GetPhoneNumberResponse>
Retrieves the phone number associated with the device.
const result = await client.getPhoneNumber({
session: prepareResponse.session,
credential: 'sd-jwt-credential-from-device',
});
console.log(result.phone_number); // '+14157400083'reportInvocation(sessionId: string): Promise<ReportInvocationResponse>
Reports when a user clicks the authenticate button (for analytics).
await client.reportInvocation(sessionId);checkStatus(sessionKey: string): Promise<StatusResponse>
Polls for verification status (authenticated endpoint).
const status = await client.checkStatus(sessionKey);
switch (status.status) {
case 'pending':
// Continue polling
break;
case 'scanned':
// QR code scanned, show device info
console.log(status.device_info);
break;
case 'completed':
// Verification complete
console.log(status.phone_number);
break;
case 'failed':
// Verification failed
console.log(status.error);
break;
}checkStatusPublic(sessionKey: string): Promise<StatusResponse>
Polls for verification status (public endpoint, no auth required).
// Safe to use from browser without exposing credentials
const status = await client.checkStatusPublic(sessionKey);invalidateToken(): void
Forces a token refresh on the next request.
client.invalidateToken();Error Handling
All API errors are thrown as typed exceptions:
import {
MagicalAuthError,
ValidationError,
UnauthorizedError,
SessionNotFoundError,
CarrierNotEligibleError,
RateLimitError,
} from '@glideidentity/glide-be-node-magical-auth';
try {
await client.prepare({ ... });
} catch (error) {
if (error instanceof ValidationError) {
console.log('Validation errors:', error.fieldErrors);
} else if (error instanceof CarrierNotEligibleError) {
console.log('Carrier not supported');
} else if (error instanceof RateLimitError) {
console.log('Rate limited, retry after:', error.retryAfter);
} else if (error instanceof MagicalAuthError) {
console.log('API error:', error.code, error.message);
console.log('Request ID:', error.requestId);
}
}Custom HTTP Client (BYOC)
import type { HttpClient } from '@glideidentity/glide-be-node-magical-auth';
const customHttpClient: HttpClient = {
async request(options) {
// Your custom implementation
const response = await myHttpLibrary.request({
method: options.method,
url: options.url,
headers: options.headers,
body: options.body,
});
return response;
},
};
const client = new MagicalAuthClient({
clientId: '...',
clientSecret: '...',
httpClient: customHttpClient,
});Custom Logger (BYOL)
import type { Logger } from '@glideidentity/glide-be-node-magical-auth';
const customLogger: Logger = {
debug(message, meta) { console.debug(message, meta); },
info(message, meta) { console.info(message, meta); },
warn(message, meta) { console.warn(message, meta); },
error(message, meta) { console.error(message, meta); },
};
const client = new MagicalAuthClient({
clientId: '...',
clientSecret: '...',
logger: customLogger,
});Built-in Console Logger
For local development and quick smoke tests, the SDK ships a default-silent console logger with PII sanitization and a [GlideMagicalAuth] prefix:
import { MagicalAuthClient, createLogger } from '@glideidentity/glide-be-node-magical-auth';
const client = new MagicalAuthClient({
clientId: process.env.GLIDE_CLIENT_ID!,
clientSecret: process.env.GLIDE_CLIENT_SECRET!,
logger: createLogger({ debug: true }),
});Sample output:
[GlideMagicalAuth] Preparing auth { use_case: 'VerifyPhoneNumber' }
[GlideMagicalAuth] Refreshing access token
[GlideMagicalAuth] Auth prepared { strategy: 'ts43', session_key: 'a1b2c3d4e5f6a7b8' }
[GlideMagicalAuth] Phone number retrieved { phone: '+141***1234' }Phone numbers, JWTs, and explicit session_key=… values are auto-masked; fields named phone_number, credential, token, password, secret, or fe_code are redacted to [REDACTED]. The default is silent (createLogger() with no args), so the SDK stays quiet in production unless you explicitly opt in. For Pino, Winston, or other structured loggers, prefer the BYOL pattern above.
Authentication Strategies
The Magical Auth backend selects one of three strategies per session, based on the carrier and the end user's device. Your application code branches on prepareResponse.authentication_strategy.
TS43
Direct credential request via the Digital Credentials API. Available in supported browsers and natively on Android via Credential Manager. Selected when the user's device exposes a compatible credential surface.
Link
OAuth-style redirect flow on mobile. The SDK returns a redirect URL; your application sends the user to it and the carrier completes authentication out-of-band.
Desktop
QR-code-based flow for desktop browsers. The SDK returns a single universal QR; the Glide mobile companion app detects the device at runtime and selects the appropriate strategy when the QR is scanned.
TypeScript Support
This SDK is written in TypeScript and provides full type definitions.
Recommended: Separate Type Imports
// Runtime imports
import { MagicalAuthClient } from '@glideidentity/glide-be-node-magical-auth';
// Type-only imports (clean separation)
import type {
PrepareRequest,
PrepareResponse,
VerifyPhoneNumberRequest,
VerifyPhoneNumberResponse,
StatusResponse,
UseCase,
AuthenticationStrategy,
} from '@glideidentity/glide-be-node-magical-auth/types';Alternative: Import from Main Entry
import { MagicalAuthClient } from '@glideidentity/glide-be-node-magical-auth';
import type { PrepareRequest, PrepareResponse } from '@glideidentity/glide-be-node-magical-auth';License
Distributed under the Glide Identity Pre-Release SDK License — see LICENSE. Evaluation and integration-testing use only. Contact [email protected] for production licensing.
