Official TypeScript client for SDKey license authentication.
Implements the sealed session protocol: Ed25519-verified handshake, HKDF session keys, and AES-256-GCM validate envelopes, plus plaintext client auth (register / login / upgrade). See PROTOCOL.md.
npm install @sdkey/sdk
# or
pnpm add @sdkey/sdkRequires Node.js 20+ (Web Crypto / Ed25519) or a modern browser / Cloudflare Worker runtime.
Embed these values from the SDKey dashboard when you ship your app. appVersion must exactly match the application's configured version (APP_OUTDATED otherwise).
import { SdkeyClient, SdkeyError } from '@sdkey/sdk'
const client = new SdkeyClient({
apiBaseUrl: 'https://api.sdkey.dev',
appId: 'YOUR_APP_ID',
appVersion: '1.0.0',
appPublicKeyB64: 'YOUR_APP_PUBLIC_KEY_BASE64',
})
try {
// hwid is optional (omit for web clients)
const result = await client.validate('SDKY-XXXX-XXXX-XXXX-XXXX', 'machine-hwid')
if (result.success) {
console.log('licensed', result.status, result.expiresAt, result.subscriptionTier)
console.log('message', result.message) // sealed success text
} else {
console.error('denied', result.code, result.message) // sealed failure text
}
} catch (err) {
if (err instanceof SdkeyError) {
// init / transport failures: server text is err.message; server code is err.serverCode
console.error(err.code, err.message, err.serverCode)
}
throw err
}validate calls init() automatically when no session exists. Sessions last ~15 minutes server-side; on SESSION_EXPIRED the client clears local state so the next call re-handshakes.
const registered = await client.register({
username: 'player1',
password: '••••••••',
licenseKey: 'SDKY-XXXX-XXXX-XXXX-XXXX', // may be required by app settings
hwid: 'machine-hwid', // optional
})
const loggedIn = await client.login({
username: 'player1',
password: '••••••••',
})
// Upgrade = username + license key only (no password)
const upgraded = await client.upgrade({
username: 'player1',
licenseKey: 'SDKY-HIGHER-TIER-KEY',
})Auth success returns sessionToken, expiresAt, user, license, and session. Failures throw SdkeyError with code: 'AUTH_FAILED', message = server error, and serverCode = server code.
Per-app responseMessages override defaults. Clients receive those strings; they do not load settings themselves.
| Surface | Success text field | Failure text field |
|---|---|---|
| Session init | (none) | error |
| Sealed validate | message (OK) |
message |
| Client register / login / upgrade | (none) | error |
{
"success": true,
"code": "OK",
"message": "validated",
"status": "active",
"expiresAt": "2026-01-01T00:00:00.000Z",
"subscriptionTier": 0,
"sessionId": "...",
"timestamp": 1720000001,
"v": 1
}{
"success": false,
"code": "HWID_MISMATCH",
"message": "Hardware ID mismatch",
"status": null,
"expiresAt": null,
"sessionId": "...",
"timestamp": 1720000001,
"v": 1
}{
"success": false,
"error": "Client version outdated",
"code": "APP_OUTDATED"
}| Option | Type | Description |
|---|---|---|
apiBaseUrl |
string |
API origin (no trailing slash) |
appId |
string |
Application UUID |
appVersion |
string |
Exact app version → sent as clientVersion |
appPublicKeyB64 |
string |
Raw Ed25519 public key (32 bytes), base64 |
fetch |
typeof fetch |
Optional fetch override (tests / custom agents) |
init()— challenge handshake; verifies the signed hello; derives the AES session key; sendsclientVersionvalidate(licenseKey, hwid?)— sealed validate; omitshwidJSON key when not provided; always decrypts then verifies the Ed25519 signature before trustingsuccessregister({ username, password, email?, licenseKey?, hwid? })— plaintext client registerlogin({ username, password, hwid? })— plaintext client loginupgrade({ username, licenseKey, hwid? })— plaintext tier upgrade (no password)getSession()/clearSession()— inspect or drop the local crypto session
Protocol / transport failures throw SdkeyError with a code:
INIT_FAILED · HELLO_SIGNATURE_INVALID · VALIDATE_RESPONSE_INVALID · RESPONSE_SIGNATURE_INVALID · SESSION_MISMATCH · CLOCK_SKEW · AUTH_FAILED · NETWORK
When the server returns a plaintext failure body, err.message is the server error string and err.serverCode is the server code (for example APP_OUTDATED, TIER_NOT_HIGHER).
License denials (banned, HWID mismatch, etc.) return a normal ValidateResult with success: false — they are not thrown. Sealed text is always in message.
- Never ship app private keys in a client.
- Do not skip signature verification — that is the anti-spoof binding.
- This package is open source; the SDKey server remains a separate product.
- This client SDK does not include developer tooling / Bearer management APIs.
pnpm install
pnpm test
pnpm buildMIT