Skip to Content
SDKsJavaScript

JavaScript SDK (@auris/js)

@auris/js v0.1.0

@auris/js is the foundation SDK for Auris. It is a zero-dependency library that works in browsers, Node.js 18+, and edge runtimes (Cloudflare Workers, Vercel Edge, Deno). Both ESM (import) and CJS (require) builds are included, along with full TypeScript declarations.

The React and Next.js SDKs are thin wrappers around @auris/js. If you are building a framework-agnostic application or need the lowest-level API, use this package directly.


Installation

npm install @auris/js
pnpm add @auris/js
yarn add @auris/js

AurisClient

AurisClient is the main entry point. Create one instance per application.

Constructor

import { AurisClient } from '@auris/js' const auris = new AurisClient({ domain: 'auth.yourdomain.com', // Your Auris tenant domain (required) clientId: 'app_xxxxx', // Application Client ID from the Console (required) redirectUri: 'http://localhost:3000/callback', // Must match a registered Callback URL (required for PKCE flows) tenant: 'my-tenant', // Tenant identifier sent as x-tenant header (optional, defaults to 'default') storage: new BrowserStorage(), // Token storage: StorageAdapter instance (optional) autoRefresh: true, // Automatically refresh access tokens before expiry (default: true) })

Configuration options:

OptionTypeRequiredDefaultDescription
domainstringYesYour Auris tenant domain, without https://
clientIdstringYesApplication Client ID from the Console
redirectUristringConditionalCallback URL for PKCE flows. Must be registered in the Console.
tenantstringNo'default'Tenant identifier sent as x-tenant HTTP header
storageStorageAdapterNoBrowserStorageWhere to persist tokens. Implement { get, set, remove } or use the built-in adapters.
autoRefreshbooleanNotrueRefresh access tokens automatically before they expire

BrowserStorage, MemoryStorage, and CookieStorage adapters are exported from @auris/js. In edge runtimes and Node.js environments without localStorage, use MemoryStorage explicitly. Scopes can be passed per-call in loginWithRedirect() rather than at construction time.


Authentication Methods

loginWithRedirect(options?)

Initiates the OAuth2 Authorization Code flow with PKCE. Generates a code verifier and challenge, stores the verifier, then redirects the browser to the Auris hosted login page.

Signature:

loginWithRedirect(options?: LoginWithRedirectOptions): Promise<void>

Parameters:

OptionTypeDescription
loginHintstringPre-fill the email field
prompt'login' | 'signup'login forces re-auth. signup opens the registration screen.
localestringOverride the hosted page locale (en, it, de, fr, es)
scopestringSpace-separated OAuth2 scopes to request for this login
// Basic login redirect await auris.loginWithRedirect() // Open the signup screen await auris.loginWithRedirect({ prompt: 'signup' }) // Pre-fill the email and force re-authentication await auris.loginWithRedirect({ loginHint: '[email protected]', prompt: 'login' })

handleRedirectCallback()

Completes the OAuth2 PKCE flow after the user returns to your application. Reads the code and state from the current URL, validates the state, exchanges the code for tokens, and stores the result.

Call this method exactly once on your callback page when it first loads.

Signature:

handleRedirectCallback(): Promise<AuthResult>

Returns: AuthResult

const result = await auris.handleRedirectCallback() if (result.accessToken) { window.location.href = '/dashboard' }

login(email, password)

Direct email/password authentication without a redirect. Returns tokens immediately.

Signature:

login(email: string, password: string): Promise<AuthResult>

Direct login sends credentials to your application server. It is not recommended for browser applications because it bypasses the security benefits of the hosted login page (CAPTCHA, rate limiting, suspicious login detection, MFA). Use loginWithRedirect() for end-user authentication in browser apps.

const result = await auris.login('[email protected]', 'hunter2') console.log(result.accessToken)

signup(email, password, options?)

Register a new user and return tokens immediately.

Signature:

signup(email: string, password: string, options?: SignupOptions): Promise<SignupResult>

Returns: SignupResult

interface SignupResult { userId: string email: string name?: string tokens?: AuthResult }

Options:

OptionTypeDescription
firstNamestringUser’s first name
lastNamestringUser’s last name
usernamestringUsername (must be unique in the tenant)
const result = await auris.signup('[email protected]', 'securepassword', { firstName: 'Bob', lastName: 'Smith', })

logout(options?)

Revokes the refresh token, clears stored tokens, and optionally redirects the browser.

Signature:

logout(options?: LogoutOptions): Promise<void>

Options:

OptionTypeDescription
returnTostringURL to redirect to after logout. Must be in the same origin as your redirectUri.
// Clear tokens and redirect to the home page await auris.logout({ returnTo: 'http://localhost:3000' }) // Clear tokens without redirecting (e.g., in a Node.js context) await auris.logout()

getUser()

Returns the currently authenticated user from the stored ID token. Returns null if the user is not authenticated.

Signature:

getUser(): Promise<UserInfo | null>

Returns: UserInfo | null

const user = await auris.getUser() if (user) { console.log(user.userId) // Auris user ID console.log(user.email) console.log(user.roles) // string[] — assigned roles }

isAuthenticated()

Returns true if the user has a valid (non-expired) access token.

Signature:

isAuthenticated(): Promise<boolean>
if (await auris.isAuthenticated()) { // Proceed with authenticated operations }

getAccessToken()

Returns the current access token. If autoRefresh is enabled and the token is expired, it automatically exchanges the refresh token for a new access token before returning.

Signature:

getAccessToken(): string | null
const token = auris.getAccessToken() // Use the token in API requests const response = await fetch('/api/data', { headers: { Authorization: `Bearer ${token}` }, })

refreshToken()

Manually exchanges the stored refresh token for a new access token and refresh token pair.

Signature:

refreshToken(): Promise<AuthResult>
const result = await auris.refreshToken() console.log(result.accessToken)

loginWithMagicLink(email, redirectUrl?)

Triggers a magic link email to the specified address. The user clicks the link in the email to complete authentication via verifyMagicLink().

Signature:

loginWithMagicLink(email: string, redirectUrl?: string): Promise<{ sessionId: string | null }>
const { sessionId } = await auris.loginWithMagicLink('[email protected]', 'https://app.example.com/magic-callback') // Redirect or show a "Check your email" message

verifyMagicLink(token)

Verifies a magic link token (extracted from the URL the user clicked). Returns tokens and user info on success.

Signature:

verifyMagicLink(token: string): Promise<AuthResult>
const urlParams = new URLSearchParams(window.location.search) const token = urlParams.get('token') if (token) { const result = await auris.verifyMagicLink(token) console.log('Access token:', result.accessToken) }

forgotPassword(email)

Sends a password reset email to the specified address.

Signature:

forgotPassword(email: string): Promise<void>
await auris.forgotPassword('[email protected]')

loginWithSocial(provider)

Redirects the user to the specified social identity provider. Auris handles the provider OAuth flow and returns the user to your redirectUri with an authorization code.

Signature:

loginWithSocial(provider: SocialProvider): Promise<void>

Supported providers:

type SocialProvider = | 'google' | 'github' | 'microsoft' | 'apple' | 'facebook' | 'discord' | 'linkedin' | 'twitter' | 'slack'
await auris.loginWithSocial('google') await auris.loginWithSocial('github')

handleCallback()

Handles the redirect back from Auris Hosted Login. Reads the access_token and refresh_token directly from the URL query parameters returned by the Auris Hosted Login redirect.

Signature:

handleCallback(): Promise<AuthResult>

handleCallback() and handleRedirectCallback() serve different flows:

  • handleCallback() — reads access_token/refresh_token from URL query params (Auris Hosted Login redirect). Use this when Auris returns tokens directly in the redirect URL.
  • handleRedirectCallback() — performs PKCE code exchange (standard OAuth2 Authorization Code flow). Use this when Auris returns a code in the redirect URL.

Check which flow your application registration uses in the Auris Console.


getM2MToken(clientSecret, scopes?)

Obtains a machine-to-machine access token using the OAuth2 client_credentials grant. Use this for server-to-server API calls where no user is involved.

Signature:

getM2MToken(clientSecret: string, scopes?: string[]): Promise<M2MTokenResult>
const result = await auris.getM2MToken('cs_live_xxxxx', ['read:users', 'manage:roles']) console.log(result.accessToken) console.log(result.expiresIn) // seconds until expiry

Never use getM2MToken() in browser code. The clientSecret must be kept server-side only. Use this method in Node.js API routes, background workers, and CLI scripts.


onAuthStateChange(callback)

Subscribes to authentication state changes. The callback fires whenever the user logs in, logs out, or their token is refreshed.

Signature:

onAuthStateChange(callback: (user: UserInfo | null) => void): Unsubscribe

Returns: Unsubscribe — call it to remove the listener.

const unsubscribe = auris.onAuthStateChange((user) => { if (user) { console.log('Signed in:', user.email) } else { console.log('Signed out') } }) // Later, when you no longer need the listener: unsubscribe()

Permissions Module

Access via auris.permissions. All methods call the Auris API and require the user to be authenticated.

check(permissions)

Check whether the current user has any or all of the specified permissions.

Signature:

check(permissions: string[]): Promise<PermissionCheckResult>

Returns: PermissionCheckResult

interface PermissionCheckResult { hasPermission: boolean // true if the user has at least one matched permission matchedPermissions: string[] // permissions from the input that the user holds userPermissions: string[] // full list of permissions held by the user userRoles: string[] // roles assigned to the user }
const result = await auris.permissions.check(['manage:users', 'view:reports']) if (result.hasPermission) { // User has at least one of the requested permissions } if (result.matchedPermissions.includes('manage:users')) { // Show admin UI }

checkOne(permission)

Check a single permission. Returns true if allowed, false otherwise.

Signature:

checkOne(permission: string): Promise<boolean>
const canDelete = await auris.permissions.checkOne('delete:documents') if (canDelete) { showDeleteButton() }

listUserPermissions()

Returns the full list of permission strings assigned to the current user across all their roles.

Signature:

listUserPermissions(): Promise<string[]>
const permissions = await auris.permissions.listUserPermissions() console.log(permissions) // ['view:documents', 'create:documents', 'manage:users', ...]

FGA Module

Access via auris.fga. Implements Zanzibar-style Fine-Grained Authorization. Requires the FGA feature to be enabled on your tenant.

check(input)

Check whether a subject has a relation to an object.

Signature:

check(input: FgaCheckInput): Promise<FgaCheckResult>
interface FgaCheckInput { objectType: string // e.g. 'document' objectId: string // e.g. 'doc_abc123' relation: string // e.g. 'viewer' subjectType: string // e.g. 'user' subjectId: string // e.g. the current user's userId } interface FgaCheckResult { allowed: boolean resolution?: ResolutionNode[] // explain mode trace }
const user = await auris.getUser() const result = await auris.fga.check({ objectType: 'document', objectId: 'doc_abc123', relation: 'editor', subjectType: 'user', subjectId: user.userId, }) if (result.allowed) { enableEditMode() }

expand(input)

Expand a relation to list all subjects (users or groups) that have it on a given object.

Signature:

expand(input: FgaExpandInput): Promise<ExpandTree>
const tree = await auris.fga.expand({ objectType: 'project', objectId: 'proj_xyz', relation: 'member', }) console.log(tree)

listObjects(input)

List all object IDs of a given type on which the subject has the specified relation.

Signature:

listObjects(input: FgaListObjectsInput): Promise<string[]>
// List all documents the current user can view const user = await auris.getUser() const documentIds = await auris.fga.listObjects({ objectType: 'document', relation: 'viewer', subjectType: 'user', subjectId: user.userId, })

writeTuples(tuples)

Write one or more relationship tuples to the FGA store.

Signature:

writeTuples(tuples: WriteTupleInput[]): Promise<void>
await auris.fga.writeTuples([ { objectType: 'document', objectId: 'doc_abc123', relation: 'editor', subjectType: 'user', subjectId: 'usr_456', }, ])

deleteTuples(tuples)

Delete one or more relationship tuples from the FGA store.

Signature:

deleteTuples(tuples: WriteTupleInput[]): Promise<void>
await auris.fga.deleteTuples([ { objectType: 'document', objectId: 'doc_abc123', relation: 'editor', subjectType: 'user', subjectId: 'usr_456', }, ])

Management Client

createManagementClient(config) creates a Management API client that authenticates with M2M client_credentials. Use it in backend code (Node.js scripts, API routes, CLI tools) to manage your tenant programmatically.

Signature:

import { createManagementClient } from '@auris/js' const mgmt = createManagementClient({ domain: 'auth.yourdomain.com', clientId: 'app_xxxxx', clientSecret: 'cs_live_xxxxx', tenant: 'my-tenant', })

Users

// List users with pagination const { users, total } = await mgmt.users.list({ page: 1, limit: 50 }) // Get a single user by ID const user = await mgmt.users.get('usr_abc123') // Create a user const newUser = await mgmt.users.create({ email: '[email protected]', password: 'temporary-password', firstName: 'Alice', roles: ['editor'], }) // Update a user await mgmt.users.update('usr_abc123', { firstName: 'Alicia' }) // Disable a user (soft-disable — they cannot log in) await mgmt.users.update('usr_abc123', { enabled: false }) // Delete a user await mgmt.users.delete('usr_abc123')

Organizations

const orgs = await mgmt.organizations.list() const org = await mgmt.organizations.get('org_xyz') const newOrg = await mgmt.organizations.create({ name: 'Acme Corp', slug: 'acme' }) await mgmt.organizations.delete('org_xyz')

Roles

const roles = await mgmt.roles.list() const role = await mgmt.roles.get('role_abc') const newRole = await mgmt.roles.create({ name: 'Editor', description: 'Can edit content' }) await mgmt.roles.update('role_abc', { description: 'Can view and edit content' }) await mgmt.roles.delete('role_abc')

Webhook Verification

verifyWebhookSignature(options) verifies that an incoming webhook was sent by Auris and has not been tampered with. Uses HMAC-SHA256.

Signature:

import { verifyWebhookSignature } from '@auris/js' const isValid = await verifyWebhookSignature({ payload: rawBody, // string or Buffer — the raw request body signature: headerValue, // value of the X-Auris-Signature header secret: 'whsec_xxxxx', // signing secret from the Console timestamp: headerTs, // value of the X-Auris-Timestamp header tolerance: 300, // optional: reject webhooks older than N seconds (default: 300) })

Node.js example (Express):

import express from 'express' import { verifyWebhookSignature } from '@auris/js' const app = express() app.post('/webhooks/auris', express.raw({ type: 'application/json' }), async (req, res) => { const isValid = await verifyWebhookSignature({ payload: req.body.toString('utf-8'), signature: req.headers['x-auris-signature'], secret: process.env.AURIS_WEBHOOK_SECRET, timestamp: req.headers['x-auris-timestamp'], }) if (!isValid) { return res.status(401).json({ error: 'Invalid signature' }) } const event = JSON.parse(req.body) console.log('Received event:', event.type) res.json({ received: true }) })

JWT Verification

createJwtVerifier(options) creates a verifier instance that validates Auris-issued access tokens using the public keys from your JWKS endpoint. Zero-dependency — uses the Web Crypto API in browsers and Node.js 18+.

Signature:

import { createJwtVerifier } from '@auris/js' const verifier = createJwtVerifier({ jwksUrl: 'https://api.altovar.net/api/.well-known/jwks.json', }) const payload = await verifier.verify(token)

The verify method returns the decoded JWT payload if the signature is valid, or throws if the token is expired, malformed, or signed with an unknown key. Key fetching results are cached for 1 hour.

import { createJwtVerifier } from '@auris/js' const verifier = createJwtVerifier({ jwksUrl: `https://${process.env.AURIS_DOMAIN}/.well-known/jwks.json`, }) try { const payload = await verifier.verify(accessToken) console.log('User ID:', payload.sub) console.log('Roles:', payload.roles) } catch (err) { console.error('Token invalid:', err.message) }

TypeScript Types

Key types exported from @auris/js:

// Authentication result (returned by login, handleRedirectCallback, refreshToken, etc.) interface AuthResult { accessToken: string refreshToken: string expiresIn: number } // Signup result interface SignupResult { userId: string email: string name?: string tokens?: AuthResult } // User information interface UserInfo { userId: string email: string username?: string firstName?: string lastName?: string roles: string[] tenant?: string } // M2M token result interface M2MTokenResult { accessToken: string expiresIn: number tokenType: 'Bearer' scope: string } // Permission check result interface PermissionCheckResult { hasPermission: boolean matchedPermissions: string[] userPermissions: string[] userRoles: string[] } // FGA types interface FgaCheckInput { objectType: string objectId: string relation: string subjectType: string subjectId: string subjectRelation?: string } interface FgaCheckResult { allowed: boolean resolution?: ResolutionNode[] } // Storage adapter interface interface StorageAdapter { get(key: string): string | null | Promise<string | null> set(key: string, value: string): void | Promise<void> remove(key: string): void | Promise<void> } // Built-in storage adapters class BrowserStorage implements StorageAdapter { /* uses localStorage */ } class MemoryStorage implements StorageAdapter { /* in-memory, works in Node.js/edge */ } class CookieStorage implements StorageAdapter { /* uses document.cookie */ } // Unsubscribe function from onAuthStateChange type Unsubscribe = () => void

Custom Storage Adapter

Implement the StorageAdapter interface to persist tokens in any storage backend.

import { AurisClient, StorageAdapter } from '@auris/js' class RedisStorageAdapter implements StorageAdapter { constructor(private redis: RedisClient, private prefix = 'auris:') {} async get(key: string) { return this.redis.get(this.prefix + key) } async set(key: string, value: string) { await this.redis.set(this.prefix + key, value, { EX: 60 * 60 * 24 }) } async remove(key: string) { await this.redis.del(this.prefix + key) } } const auris = new AurisClient({ domain: 'auth.yourdomain.com', clientId: 'app_xxxxx', storage: new RedisStorageAdapter(redisClient), })

Error Handling

All async methods throw an AurisError on failure.

import { AurisClient, AurisError } from '@auris/js' try { await auris.login('[email protected]', 'wrongpassword') } catch (err) { if (err instanceof AurisError) { console.error(err.code) // e.g. 'invalid_credentials', 'account_locked', 'mfa_required' console.error(err.message) // Human-readable message console.error(err.status) // HTTP status code (e.g. 401, 403, 429) } }

Common error codes:

CodeStatusDescription
invalid_credentials401Wrong email or password
account_locked403Account temporarily locked after failed attempts
mfa_required403MFA challenge required — use hosted login instead
invalid_token401Token is expired or malformed
permission_denied403User lacks the required permission
rate_limited429Too many requests — respect Retry-After header
not_found404Resource does not exist
validation_error422Request body failed validation