Software Licensing
Auris Licensing lets you protect your software with license keys — validate online, fall back offline, gate features, limit seats and devices, and automate key issuance from Stripe or PayPal.
This guide walks you through the full flow: create a policy, issue a key, and validate it in your app.
How It Works
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Auris Console │ │ Auris API │ │ Your App │
│ (Admin UI) │ │ (Backend) │ │ (Desktop/Web) │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ Create Policy │────▶│ Store policy │ │ │
│ Issue Key │────▶│ Generate key │ │ │
│ │ │ + JWT token │ │ │
│ │ │ │◀────│ validateLicense()│
│ │ │ Check validity │────▶│ ✓ or ✗ │
│ │ │ │◀────│ activateLicense()│
│ │ │ Register device │────▶│ Device saved │
└─────────────────┘ └─────────────────┘ └─────────────────┘Three validation modes:
| Mode | How it works | Best for |
|---|---|---|
| Online | Every check hits the API | SaaS, web apps, always-connected |
| Hybrid | Online first, JWT fallback if offline | Desktop apps, intermittent connectivity |
| Offline | JWT only, no network needed | Air-gapped, embedded, field devices |
Quick Start
Create a Policy
In the Auris Console, go to Licensing → Policies → New Policy.
Configure:
- Name:
Vigilante Pro - Slug:
vigilante-pro - Validation Mode:
Hybrid - Dimensions: Enable seats (max 3), devices (max 2), expiry (365 days)
- Features:
threat-intel,vuln-scan,api-access - Key Format: Prefix
VIG, 4 segments × 4 chars, Base32
Issue a Key
Go to Licensing → Keys → Issue Key, select your policy, and enter the licensee info. You’ll get a key like:
VIG-A8BC-D3EF-G4HJ-K5LMInstall the SDK
npm
npm install @auris/jsValidate in Your App
import { validateLicense } from '@auris/js'
const result = await validateLicense('VIG-A8BC-D3EF-G4HJ-K5LM', 'auth.yourdomain.com')
if (result.valid) {
console.log('License is valid!')
console.log('Features:', result.features) // ['threat-intel', 'vuln-scan', 'api-access']
console.log('Seats:', result.seats) // { used: 1, max: 3 }
console.log('Expires:', result.expiresAt) // '2027-03-15T00:00:00Z'
} else {
console.log('Invalid:', result.reason) // 'EXPIRED', 'REVOKED', etc.
}That’s it. One function call.
Desktop App Integration
For desktop apps (Electron, Tauri, etc.), you’ll typically want device activation and offline fallback.
Full Example
import {
validateLicense,
validateLicenseJwt,
activateLicense,
deactivateLicense,
} from '@auris/js'
const DOMAIN = 'auth.yourdomain.com'
// ── Step 1: Activation (first launch) ────────────────────────
// The user enters their license key. You register the device.
async function activate(key: string) {
const fingerprint = getDeviceFingerprint() // e.g. machine UUID
await activateLicense(key, DOMAIN, fingerprint, os.hostname())
// Save key locally for future launches
store.set('licenseKey', key)
}
// ── Step 2: Validation (every launch) ────────────────────────
// Check the license. Online if possible, offline fallback.
async function checkLicense(): Promise<boolean> {
const key = store.get('licenseKey')
if (!key) return false
const result = await validateLicense(key, DOMAIN)
// Online validation succeeded
if (result.valid) {
// Cache the JWT for offline use
if (result.jwtToken) store.set('licenseJwt', result.jwtToken)
return true
}
// Network error — try offline
if (result.reason === 'NETWORK_ERROR') {
const jwt = store.get('licenseJwt')
if (jwt) {
const offline = await validateLicenseJwt(jwt, DOMAIN)
return offline.valid
}
}
return false
}
// ── Step 3: Feature gating ───────────────────────────────────
async function hasFeature(feature: string): Promise<boolean> {
const result = await validateLicense(store.get('licenseKey')!, DOMAIN)
return result.valid && (result.features?.includes(feature) ?? false)
}
// ── Step 4: Deactivation (uninstall / sign out) ──────────────
async function deactivate() {
const key = store.get('licenseKey')
if (key) {
await deactivateLicense(key, DOMAIN, getDeviceFingerprint())
store.delete('licenseKey')
store.delete('licenseJwt')
}
}Offline fallback works because each license key has an associated JWT token that contains the entitlements (features, seats, expiry) signed by Auris. The SDK validates the JWT signature and checks the revocation list locally. The grace period (configurable per policy, default 7 days) determines how long offline validation stays valid.
Web App Integration
For web apps that are always online, you only need validateLicense:
import { validateLicense } from '@auris/js'
// Middleware or API route
export async function checkLicense(licenseKey: string) {
const result = await validateLicense(licenseKey, 'auth.yourdomain.com')
if (!result.valid) throw new Error(`License invalid: ${result.reason}`)
return result
}React Integration
Use @auris/react for hooks-based license checking:
import { useAuris } from '@auris/react'
import { validateLicense } from '@auris/js'
import { useQuery } from '@tanstack/react-query'
function useLicense(key: string) {
const { domain } = useAuris()
return useQuery({
queryKey: ['license', key],
queryFn: () => validateLicense(key, domain),
staleTime: 5 * 60 * 1000, // cache for 5 minutes
})
}
function ProFeature({ children }: { children: React.ReactNode }) {
const { data } = useLicense(userLicenseKey)
if (!data?.valid) return <UpgradePrompt />
if (!data.features?.includes('pro-feature')) return <UpgradePrompt />
return <>{children}</>
}Automate Key Issuance
Instead of issuing keys manually, connect Stripe or PayPal to automatically issue keys on payment.
Create an Automation Rule
In the Console, go to Licensing → Automation → New Rule:
- Provider: Stripe
- Trigger:
checkout.session.completed - Action:
issue_key - Linked Policy: Vigilante Pro
Configure the Webhook
In Stripe Dashboard, add a webhook endpoint:
https://auth.yourdomain.com/api/licensing/webhooks/stripeEvents to listen for: checkout.session.completed
Set STRIPE_LICENSING_WEBHOOK_SECRET in your Auris API environment.
Done
When a customer completes checkout, Auris automatically issues a key under the linked policy. The key is available via the API and can be sent to the customer via email or shown in your app.
Feature Gating Patterns
Boolean Feature Flags
const result = await validateLicense(key, domain)
const canExport = result.features?.includes('export') ?? false
const canUseApi = result.features?.includes('api-access') ?? falseTiered Access
// Define tiers as policies with different feature sets
// Free: ['basic']
// Pro: ['basic', 'analytics', 'export']
// Enterprise: ['basic', 'analytics', 'export', 'api-access', 'sso']
function getTier(features: string[]): 'free' | 'pro' | 'enterprise' {
if (features.includes('sso')) return 'enterprise'
if (features.includes('analytics')) return 'pro'
return 'free'
}Seat Enforcement
const result = await validateLicense(key, domain)
if (result.seats && result.seats.used >= (result.seats.max ?? Infinity)) {
throw new Error('Seat limit reached. Please upgrade your plan.')
}Usage Tracking
Track metered usage (API calls, storage, etc.) against a key:
import { recordLicenseUsage, getLicenseUsage } from '@auris/js'
// Record usage
await recordLicenseUsage(key, domain, 'api_calls', 1)
// Check usage
const usage = await getLicenseUsage(key, domain)
// { api_calls: { used: 142, max: 1000, period: 'month', resetsAt: '2026-04-01' } }Floating Licenses
For apps that need concurrent-user licensing (e.g., 10 seats for 50 employees), use floating licenses with checkout/checkin:
import { checkoutLicense, checkinLicense, heartbeatLicense } from '@auris/js'
const DOMAIN = 'auth.yourdomain.com'
// Check out a seat when the user starts a session
const seat = await checkoutLicense('VIG-A8BC-D3EF-G4HJ-K5LM', DOMAIN, 'user_123')
console.log('Lease expires:', seat.leaseExpiresAt)
// Keep the seat alive with periodic heartbeats
const interval = setInterval(async () => {
await heartbeatLicense('VIG-A8BC-D3EF-G4HJ-K5LM', DOMAIN, 'user_123')
}, 60_000) // every 60 seconds
// Release the seat when the user is done
clearInterval(interval)
await checkinLicense('VIG-A8BC-D3EF-G4HJ-K5LM', DOMAIN, 'user_123')Floating licenses require the policy to have seats enabled. The lease duration is determined by the policy’s floatingLeaseDuration setting (default: 30 minutes). If the client stops sending heartbeats, the lease expires automatically.
Offline Activation
For air-gapped environments where the client device can never reach the Auris API:
import { generateOfflineRequest, importOfflineResponse } from '@auris/js'
// Step 1: On the air-gapped device, generate a request payload
const requestPayload = generateOfflineRequest('VIG-A8BC-D3EF-G4HJ-K5LM', getDeviceFingerprint())
// Returns a base64 string — export it via USB, email, QR code, etc.
// Step 2: An admin submits the payload to the Auris API (from any connected device)
// POST /api/licensing/offline/activate { requestPayload: "base64..." }
// → { success: true, data: { jwt: "signed-jwt..." } }
// Step 3: Transfer the response token back to the air-gapped device and import it
const result = importOfflineResponse(data.jwt)
if (result) {
console.log('Offline activation successful!')
// Store the response token for future offline validation
}The generateOfflineRequest and importOfflineResponse functions in the JS SDK do NOT perform cryptographic signature verification. For production air-gapped deployments, use the native SDKs (C#, Rust, Go, Python, Java) which provide full JWT signature verification against the Auris public key.
Feature Checking Helper
The SDK includes a convenience function to check for a specific feature:
import { checkFeature } from '@auris/js'
const hasThreatIntel = await checkFeature('VIG-A8BC-D3EF-G4HJ-K5LM', 'auth.yourdomain.com', 'threat-intel')
if (hasThreatIntel) {
// Enable threat intel module
}SDK Reference
All licensing functions are exported from @auris/js:
| Function | Description |
|---|---|
validateLicense(key, domain) | Online validation — returns validity, features, seats, devices, expiry |
validateLicenseJwt(jwt, domain) | Offline validation — checks JWT expiry + revocation list |
activateLicense(key, domain, fingerprint, name?) | Register a device |
deactivateLicense(key, domain, fingerprint) | Remove a device |
recordLicenseUsage(key, domain, metric, amount?) | Track metered usage |
getLicenseUsage(key, domain) | Get current usage for a key |
refreshRevocationList(domain) | Manually refresh the offline revocation cache |
checkoutLicense(key, domain, userId) | Check out a floating seat — returns the seat with lease expiry |
checkinLicense(key, domain, userId) | Release a floating seat |
heartbeatLicense(key, domain, userId) | Extend a floating seat lease |
generateOfflineRequest(key, fingerprint) | Generate a base64 offline activation request payload |
importOfflineResponse(token) | Import a signed offline activation response token |
checkFeature(key, domain, feature) | Check if a specific feature is included in the license |
See the full JavaScript SDK reference and Licensing API reference for details.