Skip to Content
GuidesSoftware Licensing

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:

ModeHow it worksBest for
OnlineEvery check hits the APISaaS, web apps, always-connected
HybridOnline first, JWT fallback if offlineDesktop apps, intermittent connectivity
OfflineJWT only, no network neededAir-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-K5LM

Install the SDK

npm install @auris/js

Validate 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/stripe

Events 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') ?? false

Tiered 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:

FunctionDescription
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.