# Authon — Complete AI Reference > Self-hosted authentication platform — a modern Clerk alternative with 15 SDKs. > This file contains everything needed to implement Authon authentication in any supported framework. Website: https://authon.dev Docs: https://docs.authon.dev GitHub: https://github.com/mikusnuz/authon-sdk License: MIT --- ## Table of Contents 1. [Prerequisites — Create a Project and Get API Keys](#prerequisites--create-a-project-and-get-api-keys) 2. [Core Concepts](#core-concepts) 3. [Environment Variables](#environment-variables) 4. [SDK Reference — JavaScript / @authon/js](#sdk-javascript) 5. [SDK Reference — React / @authon/react](#sdk-react) 6. [SDK Reference — Next.js / @authon/nextjs](#sdk-nextjs) 7. [SDK Reference — Vue / @authon/vue](#sdk-vue) 8. [SDK Reference — Nuxt / @authon/nuxt](#sdk-nuxt) 9. [SDK Reference — Svelte / @authon/svelte](#sdk-svelte) 10. [SDK Reference — Angular / @authon/angular](#sdk-angular) 11. [SDK Reference — React Native / @authon/react-native](#sdk-react-native) 12. [SDK Reference — Node.js / @authon/node](#sdk-node) 13. [SDK Reference — Python](#sdk-python) 14. [SDK Reference — Go](#sdk-go) 15. [SDK Reference — Dart/Flutter](#sdk-dart) 16. [SDK Reference — Swift](#sdk-swift) 17. [SDK Reference — Kotlin/Android](#sdk-kotlin) 18. [REST API Endpoints](#rest-api-endpoints) 19. [Webhook Events](#webhook-events) 20. [TypeScript Types](#typescript-types) 21. [Error Codes](#error-codes) 22. [Common Patterns](#common-patterns) 23. [Migration from Clerk](#migration-from-clerk) 24. [Migration from Auth.js (NextAuth)](#migration-from-authjs) --- ## Prerequisites — Create a Project and Get API Keys Before using any Authon SDK, create a project and get your API keys: 1. **Create a project** at [Authon Dashboard](https://authon.dev/dashboard/overview) - Click "Create Project" and enter your app name - Select the authentication methods you want (Email/Password, OAuth providers, etc.) 2. **Get your API keys** from Project Settings → API Keys - **Publishable Key** (`pk_live_...` or `pk_test_...`) — safe to use in client-side code - **Secret Key** (`sk_live_...` or `sk_test_...`) — server-side only, never expose to clients 3. **Configure OAuth providers** (optional) in Project Settings → OAuth - Add Google, Apple, GitHub, etc. with their respective Client ID and Secret - Set the redirect URL to `https://api.authon.dev/v1/auth/oauth/redirect` **Test vs Live keys:** Use `pk_test_...` during development. Switch to `pk_live_...` before deploying to production. Test keys use a sandbox environment with no rate limits. --- ## Core Concepts ### Authentication Methods - **Email/Password**: Traditional sign-up and sign-in with email verification - **OAuth**: 10 providers — Google, Apple, GitHub, Discord, Facebook, Microsoft, Kakao, Naver, LINE, X - **Passwordless**: Magic link (email) and email OTP (6-digit code) - **Passkeys (WebAuthn)**: Hardware key and biometric authentication - **Web3**: EVM wallets (MetaMask, WalletConnect, Coinbase, Pexus, Trust) and Solana (Phantom) - **MFA (TOTP)**: Google Authenticator / Authy compatible with backup codes ### Architecture - **Publishable Key** (`pk_live_...` / `pk_test_...`): Used by client-side SDKs. Safe to expose in browser code. - **Secret Key** (`sk_live_...` / `sk_test_...`): Used by server-side SDKs. Never expose to clients. - **ShadowDOM Modal**: Built-in sign-in/sign-up UI rendered inside a ShadowRoot to prevent CSS conflicts. Branding is fetched from your Authon project dashboard. - **Session Management**: Access token (default 15 min TTL) + refresh token (default 7 day TTL). Client SDKs auto-refresh. Default max 5 concurrent sessions per user. - **Organizations**: Multi-tenant with roles (owner, admin, member), invitations, and per-org metadata. ### API Base URL Default: `https://api.authon.dev` Self-hosted: Configure `apiUrl` in SDK options or `AUTHON_API_URL` env var. --- ## Environment Variables ```env # Client-side (safe to expose) NEXT_PUBLIC_AUTHON_KEY=pk_live_... # Next.js NUXT_PUBLIC_AUTHON_KEY=pk_live_... # Nuxt VITE_AUTHON_KEY=pk_live_... # Vite apps (React, Vue, Svelte) # Server-side (keep secret) AUTHON_SECRET_KEY=sk_live_... # Required for backend SDKs AUTHON_API_URL=https://api.authon.dev # Optional, this is the default AUTHON_WEBHOOK_SECRET=whsec_... # For verifying incoming webhooks ``` --- ## SDK Reference — JavaScript / @authon/js {#sdk-javascript} The core browser SDK. All framework SDKs wrap this internally. ### Install ```bash npm install @authon/js ``` ### Initialize ```ts import { Authon } from '@authon/js'; const authon = new Authon('pk_live_...'); ``` With options: ```ts const authon = new Authon('pk_live_...', { apiUrl: 'https://api.authon.dev', // default mode: 'popup', // 'popup' | 'embedded' theme: 'auto', // 'light' | 'dark' | 'auto' locale: 'en', containerId: 'auth-container', // element ID for embedded mode appearance: { brandName: 'My App', primaryColorStart: '#7c3aed', primaryColorEnd: '#4f46e5', borderRadius: 12, }, }); ``` ### Configuration Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `apiUrl` | `string` | `'https://api.authon.dev'` | Authon API base URL | | `mode` | `'popup' \| 'embedded'` | `'popup'` | Modal display mode | | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | UI theme | | `locale` | `string` | `'en'` | UI locale | | `containerId` | `string` | — | Element ID for embedded mode | | `appearance` | `Partial` | — | Override branding from dashboard | ### Modal ```ts await authon.openSignIn(); await authon.openSignUp(); ``` ### Email / Password ```ts // Sign up const user = await authon.signUpWithEmail('user@example.com', 'password', { displayName: 'Alice', }); // Sign in const user = await authon.signInWithEmail('user@example.com', 'password'); // Sign out await authon.signOut(); ``` ### OAuth ```ts // Supported providers: 'google' | 'apple' | 'github' | 'discord' | 'facebook' | 'microsoft' | 'kakao' | 'naver' | 'line' | 'x' await authon.signInWithOAuth('google'); await authon.signInWithOAuth('github', { flowMode: 'popup' }); await authon.signInWithOAuth('google', { flowMode: 'redirect' }); await authon.signInWithOAuth('google', { flowMode: 'auto' }); // default — popup with redirect fallback ``` ### Passwordless ```ts // Magic link await authon.sendMagicLink('user@example.com'); const user = await authon.verifyPasswordless({ token: 'token-from-url' }); // Email OTP await authon.sendEmailOtp('user@example.com'); const user = await authon.verifyPasswordless({ email: 'user@example.com', code: '123456' }); ``` ### Passkeys (WebAuthn) ```ts // Register (user must be signed in) const credential = await authon.registerPasskey('My MacBook'); // Authenticate const user = await authon.authenticateWithPasskey(); const user = await authon.authenticateWithPasskey('user@example.com'); // restrict by email // List const passkeys = await authon.listPasskeys(); // Rename await authon.renamePasskey(passkeys[0].id, 'Work Laptop'); // Revoke await authon.revokePasskey(passkeys[0].id); ``` ### Web3 ```ts // EVM (MetaMask example) const { message, nonce } = await authon.web3GetNonce('0xAbc...', 'evm', 'metamask', 1); const signature = await window.ethereum.request({ method: 'personal_sign', params: [message, '0xAbc...'], }); const user = await authon.web3Verify(message, signature, '0xAbc...', 'evm', 'metamask'); // Solana (Phantom example) const { message } = await authon.web3GetNonce(publicKey.toString(), 'solana', 'phantom'); const encodedMessage = new TextEncoder().encode(message); const { signature } = await window.solana.signMessage(encodedMessage, 'utf8'); const user = await authon.web3Verify(message, bs58.encode(signature), publicKey.toString(), 'solana', 'phantom'); // Link additional wallet (user must be signed in) const wallet = await authon.linkWallet({ address, chain: 'evm', walletType: 'walletconnect', chainId: 1, message, signature }); // List / unlink wallets const wallets = await authon.listWallets(); await authon.unlinkWallet(wallets[0].id); ``` Supported wallet types: `'metamask' | 'pexus' | 'walletconnect' | 'coinbase' | 'phantom' | 'trust' | 'other'` Supported chains: `'evm' | 'solana'` ### MFA (TOTP) ```ts // Setup const setup = await authon.setupMfa(); // setup.qrCodeSvg — inline SVG string // setup.qrCodeUri — otpauth:// URI // setup.secret — raw TOTP secret // setup.backupCodes — one-time recovery codes // Confirm setup await authon.verifyMfaSetup('123456'); // Sign-in with MFA import { AuthonMfaRequiredError } from '@authon/js'; try { await authon.signInWithEmail('user@example.com', 'password'); } catch (err) { if (err instanceof AuthonMfaRequiredError) { const user = await authon.verifyMfa(err.mfaToken, '123456'); } } // Status const status = await authon.getMfaStatus(); // { enabled: boolean, backupCodesRemaining: number } // Disable await authon.disableMfa('123456'); // Regenerate backup codes const newCodes = await authon.regenerateBackupCodes('123456'); ``` ### User Profile ```ts const user = authon.getUser(); // synchronous const token = authon.getToken(); // synchronous const updated = await authon.updateProfile({ displayName: 'Alice Smith', avatarUrl: 'https://example.com/avatar.png', phone: '+12025551234', publicMetadata: { plan: 'pro' }, }); ``` ### Session Management ```ts const sessions = await authon.listSessions(); // [{ id, ipAddress, userAgent, createdAt, lastActiveAt }] await authon.revokeSession(sessions[0].id); ``` ### Organizations ```ts // List const { data: orgs } = await authon.organizations.list(); // Create const org = await authon.organizations.create({ name: 'My Team', slug: 'my-team' }); // Get / update / delete const org = await authon.organizations.get('org_...'); await authon.organizations.update('org_...', { name: 'New Name' }); await authon.organizations.delete('org_...'); // Members const members = await authon.organizations.getMembers('org_...'); await authon.organizations.removeMember('org_...', 'member_...'); await authon.organizations.updateMemberRole('org_...', 'member_...', 'admin'); await authon.organizations.leave('org_...'); // Invitations const invite = await authon.organizations.invite('org_...', { email: 'user@example.com', role: 'member' }); const invitations = await authon.organizations.getInvitations('org_...'); await authon.organizations.acceptInvitation('invitation_token'); await authon.organizations.rejectInvitation('invitation_token'); ``` ### Events ```ts const off = authon.on('signedIn', (user) => console.log('Signed in:', user.email)); authon.on('signedOut', () => console.log('Signed out')); authon.on('tokenRefreshed', (token) => { /* update API client */ }); authon.on('mfaRequired', (mfaToken) => { /* show TOTP input */ }); authon.on('passkeyRegistered', (credential) => { /* passkey added */ }); authon.on('web3Connected', (wallet) => { /* wallet linked */ }); authon.on('error', (error) => console.error(error)); off(); // unsubscribe ``` ### Cleanup ```ts authon.destroy(); ``` ### Complete Method Reference | Method | Returns | Description | |--------|---------|-------------| | `openSignIn()` | `Promise` | Open sign-in modal | | `openSignUp()` | `Promise` | Open sign-up modal | | `signInWithEmail(email, password)` | `Promise` | Email sign-in | | `signUpWithEmail(email, password, meta?)` | `Promise` | Email sign-up | | `signInWithOAuth(provider, options?)` | `Promise` | OAuth flow | | `signOut()` | `Promise` | Sign out | | `sendMagicLink(email)` | `Promise` | Send magic link | | `sendEmailOtp(email)` | `Promise` | Send OTP code | | `verifyPasswordless(opts)` | `Promise` | Verify magic link or OTP | | `registerPasskey(name?)` | `Promise` | Register passkey | | `authenticateWithPasskey(email?)` | `Promise` | Sign in with passkey | | `listPasskeys()` | `Promise` | List passkeys | | `renamePasskey(id, name)` | `Promise` | Rename passkey | | `revokePasskey(id)` | `Promise` | Delete passkey | | `web3GetNonce(address, chain, walletType, chainId?)` | `Promise` | Get sign-in nonce | | `web3Verify(message, signature, address, chain, walletType)` | `Promise` | Verify wallet | | `listWallets()` | `Promise` | List wallets | | `linkWallet(params)` | `Promise` | Link wallet | | `unlinkWallet(walletId)` | `Promise` | Unlink wallet | | `setupMfa()` | `Promise` | Begin MFA setup | | `verifyMfaSetup(code)` | `Promise` | Confirm MFA setup | | `verifyMfa(mfaToken, code)` | `Promise` | Complete MFA sign-in | | `getMfaStatus()` | `Promise` | Get MFA status | | `disableMfa(code)` | `Promise` | Disable MFA | | `regenerateBackupCodes(code)` | `Promise` | New backup codes | | `getUser()` | `AuthonUser \| null` | Current user (sync) | | `getToken()` | `string \| null` | Current token (sync) | | `updateProfile(data)` | `Promise` | Update profile | | `listSessions()` | `Promise` | List sessions | | `revokeSession(sessionId)` | `Promise` | Revoke session | | `getProviders()` | `Promise` | List enabled providers | | `on(event, listener)` | `() => void` | Subscribe to event | | `destroy()` | `void` | Cleanup | --- ## SDK Reference — React / @authon/react {#sdk-react} ### Install ```bash npm install @authon/react @authon/js ``` Requires React >= 18. ### Setup ```tsx import { AuthonProvider } from '@authon/react'; function App() { return ( ); } ``` ### Components | Component | Description | |-----------|-------------| | `` | Provides auth context — wrap at root | | `` | Sign-in modal or embedded form | | `` | Sign-up modal or embedded form | | `` | Avatar dropdown with user info + sign-out | | `` | Renders children only when signed in | | `` | Renders children only when signed out | | `` | Role-based access guard | | `` | Single OAuth button | | `` | Auto-renders all enabled OAuth buttons | ### Hooks | Hook | Returns | |------|---------| | `useAuthon()` | `{ isSignedIn, isLoading, user, signOut, openSignIn, openSignUp, getToken, client }` | | `useUser()` | `{ user, isLoading }` | | `useAuthonMfa()` | `{ setupMfa, verifyMfaSetup, verifyMfa, disableMfa, getMfaStatus, regenerateBackupCodes, isLoading, error }` | | `useAuthonPasskeys()` | `{ registerPasskey, authenticateWithPasskey, listPasskeys, renamePasskey, revokePasskey, isLoading, error }` | | `useAuthonPasswordless()` | `{ sendMagicLink, sendEmailOtp, verifyPasswordless, isLoading, error }` | | `useAuthonWeb3()` | `{ getNonce, verify, listWallets, linkWallet, unlinkWallet, isLoading, error }` | | `useAuthonSessions()` | `{ listSessions, revokeSession, isLoading, error }` | ### Example: Full auth-aware app ```tsx import { AuthonProvider, SignedIn, SignedOut, UserButton, useAuthon, useUser } from '@authon/react'; function App() { return ( ); } function Dashboard() { const { user } = useUser(); return

Welcome, {user?.displayName}

; } function LandingPage() { const { openSignIn } = useAuthon(); return ; } ``` --- ## SDK Reference — Next.js / @authon/nextjs {#sdk-nextjs} ### Install ```bash npm install @authon/nextjs @authon/js ``` Requires Next.js >= 14. ### 1. Middleware (route protection) ```ts // middleware.ts import { authonMiddleware } from '@authon/nextjs'; export default authonMiddleware({ publicRoutes: ['/', '/about', '/pricing', '/sign-in', '/sign-up'], signInUrl: '/sign-in', }); export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\..*).*)'], }; ``` Middleware options: | Option | Type | Default | Description | |--------|------|---------|-------------| | `publicRoutes` | `string[]` | `['/']` | Routes accessible without auth. Supports trailing `*` wildcard | | `signInUrl` | `string` | `'/sign-in'` | Redirect for unauthenticated users | | `secretKey` | `string` | `process.env.AUTHON_SECRET_KEY` | Secret key for token verification | | `apiUrl` | `string` | `'https://api.authon.dev'` | API base URL | ### 2. Layout provider ```tsx // app/layout.tsx import { AuthonProvider } from '@authon/nextjs'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### 3. Server-side helpers ```ts // In Server Components or Route Handlers import { currentUser, auth } from '@authon/nextjs/server'; // Get current user const user = await currentUser(); // Get full auth state const { userId, user, getToken } = await auth(); const token = getToken(); // forward to downstream services ``` ### 4. Client components All hooks and components from @authon/react are re-exported. Use `'use client'` directive. ```tsx 'use client'; import { useAuthon, useUser, SignedIn, SignedOut, UserButton } from '@authon/nextjs'; ``` ### Environment variables ```env NEXT_PUBLIC_AUTHON_KEY=pk_live_... AUTHON_SECRET_KEY=sk_live_... AUTHON_API_URL=https://api.authon.dev # optional ``` --- ## SDK Reference — Vue / @authon/vue {#sdk-vue} ### Install ```bash npm install @authon/vue @authon/js ``` Requires Vue >= 3.3. ### Setup ```ts // main.ts import { createApp } from 'vue' import { createAuthon } from '@authon/vue' import App from './App.vue' const app = createApp(App) app.use(createAuthon({ publishableKey: 'pk_live_...' })) app.mount('#app') ``` ### Composables ```ts import { useAuthon, useUser } from '@authon/vue' const { isSignedIn, isLoading, user, client, signOut, openSignIn, openSignUp, getToken } = useAuthon() const { user, isLoading } = useUser() ``` ### Components | Component | Description | |-----------|-------------| | `` | Sign-in modal or embedded form | | `` | Sign-up modal or embedded form | | `` | Avatar dropdown | | `` | Slot only when signed in | | `` | Slot only when signed out | | `` | Single OAuth button | | `` | All enabled OAuth buttons | ### Example ```vue ``` --- ## SDK Reference — Nuxt / @authon/nuxt {#sdk-nuxt} ### Install ```bash npm install @authon/nuxt @authon/js ``` Requires Nuxt >= 3. ### Setup ```ts // plugins/authon.client.ts import { createAuthonPlugin } from '@authon/nuxt' export default defineNuxtPlugin(() => { const config = useRuntimeConfig() const authon = createAuthonPlugin(config.public.authonKey) return { provide: { authon } } }) ``` ```ts // nuxt.config.ts export default defineNuxtConfig({ runtimeConfig: { public: { authonKey: process.env.NUXT_PUBLIC_AUTHON_KEY }, }, }) ``` ### Usage ```vue ``` ### Route middleware ```ts // middleware/auth.ts import { createAuthMiddleware } from '@authon/nuxt' export default defineNuxtRouteMiddleware((to, from) => { const { $authon } = useNuxtApp() return createAuthMiddleware($authon, '/login')(to, from) }) ``` ```vue ``` --- ## SDK Reference — Svelte / @authon/svelte {#sdk-svelte} ### Install ```bash npm install @authon/svelte @authon/js ``` Requires Svelte >= 4. ### Setup ```svelte ``` ### Usage ```svelte {#if $isLoading}

Loading...

{:else if $isSignedIn}

Welcome, {$user?.displayName}

{:else} {/if} ``` ### Social Buttons ```svelte
``` --- ## SDK Reference — Angular / @authon/angular {#sdk-angular} ### Install ```bash npm install @authon/angular @authon/js ``` Requires Angular >= 16. ### Setup (standalone) ```ts // app.config.ts import { provideAuthon } from '@authon/angular' export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), ...provideAuthon({ publishableKey: 'pk_live_...' }), ], } ``` ### Service ```ts import { AuthonService } from '@authon/angular' constructor(@Inject('AuthonService') private authon: AuthonService) {} // Properties: user, isSignedIn, isLoading // Methods: openSignIn(), openSignUp(), signOut(), getToken(), getClient(), onStateChange(cb), destroy() ``` ### Route guard ```ts import { authGuard, AuthonService } from '@authon/angular' import { inject } from '@angular/core' { path: 'dashboard', component: DashboardComponent, canActivate: [() => authGuard(inject(AuthonService) as AuthonService, '/login')], } ``` ### Change detection ```ts ngOnInit() { this.unsubscribe = this.authon.onStateChange(() => this.cdr.markForCheck()) } ngOnDestroy() { this.unsubscribe?.() } ``` --- ## SDK Reference — React Native / @authon/react-native {#sdk-react-native} ### Install ```bash npm install @authon/react-native react-native-svg npx expo install expo-secure-store expo-web-browser ``` ### Setup ```tsx import { AuthonProvider } from '@authon/react-native'; import * as SecureStore from 'expo-secure-store'; const storage = { getItem: (key: string) => SecureStore.getItemAsync(key), setItem: (key: string, value: string) => SecureStore.setItemAsync(key, value), removeItem: (key: string) => SecureStore.deleteItemAsync(key), }; export default function App() { return ( ); } ``` ### Hooks ```ts const { isLoaded, isSignedIn, userId, user, signIn, signUp, signOut, getToken, startOAuth, completeOAuth, client } = useAuthon(); const { isLoaded, isSignedIn, user } = useUser(); ``` ### Email sign-in ```tsx const { signIn } = useAuthon(); await signIn({ strategy: 'email_password', email, password }); ``` ### OAuth (Expo recommended flow) ```tsx import * as WebBrowser from 'expo-web-browser'; const { completeOAuth, getToken } = useAuthon(); async function handleGoogleSignIn() { // Request OAuth URL from API const params = new URLSearchParams({ redirectUri: `${API_URL}/v1/auth/oauth/redirect`, flow: 'redirect', returnTo: 'https://auth.example.com/authon/mobile-callback', }); const response = await fetch(`${API_URL}/v1/auth/oauth/google/url?${params}`, { headers: { 'x-api-key': PUBLISHABLE_KEY }, }); const { url, state } = await response.json(); const pollPromise = completeOAuth(state); await WebBrowser.openAuthSessionAsync(url, 'myapp://oauth-callback'); await pollPromise; const token = getToken(); } ``` ### Social Buttons ```tsx import { SocialButtons } from '@authon/react-native'; console.log('Signed in')} onError={(error) => console.error(error)} /> ``` --- ## SDK Reference — Node.js / @authon/node {#sdk-node} Server-side SDK for token verification, user management, session management, and webhooks. ### Install ```bash npm install @authon/node ``` ### Initialize ```ts import { AuthonBackend } from '@authon/node'; const authon = new AuthonBackend('sk_live_...'); // Optional: new AuthonBackend('sk_live_...', { apiUrl: 'https://custom.api' }) ``` ### Token Verification ```ts const user = await authon.verifyToken(accessToken); // Returns AuthonUser ``` ### User Management ```ts // List const result = await authon.users.list({ page: 1, limit: 20, search: 'alice' }); // Get const user = await authon.users.get('usr_...'); const user = await authon.users.getByExternalId('your-db-id'); // Create const user = await authon.users.create({ email: 'alice@example.com', password: 'secret1234', displayName: 'Alice', externalId: 'your-db-id', publicMetadata: { role: 'admin' }, privateMetadata: { plan: 'pro' }, }); // Update const user = await authon.users.update('usr_...', { displayName: 'Alice Smith' }); // Delete await authon.users.delete('usr_...'); // Ban / unban await authon.users.ban('usr_...', 'Spam'); await authon.users.unban('usr_...'); ``` ### Session Management ```ts const sessions = await authon.sessions.list('usr_...'); await authon.sessions.revoke('usr_...', 'sess_...'); ``` ### Webhook Verification ```ts const event = authon.webhooks.verify( rawBody, // string | Buffer signature, // X-Authon-Signature header ("v1=") timestamp, // X-Authon-Timestamp header (ISO 8601) webhookSecret, // from Authon dashboard ); // Returns parsed payload object ``` ### Organizations (Backend) ```ts const { data: orgs } = await authon.organizations.list({ page: 1, limit: 20 }); const org = await authon.organizations.get('org_...'); const org = await authon.organizations.create({ name: 'Team', createdBy: 'usr_...' }); await authon.organizations.delete('org_...'); const members = await authon.organizations.getMembers('org_...'); await authon.organizations.addMember('org_...', { userId: 'usr_...', role: 'admin' }); await authon.organizations.removeMember('org_...', 'usr_...'); ``` ### Audit Logs ```ts const { data: logs } = await authon.auditLogs.list({ event: 'auth.signin', dateFrom: '2025-01-01', page: 1, limit: 50, }); ``` ### JWT Templates ```ts const templates = await authon.jwtTemplates.list(); const template = await authon.jwtTemplates.create({ name: 'custom', claims: [{ key: 'role', source: 'publicMetadata.role' }], }); await authon.jwtTemplates.update(template.id, { name: 'updated' }); await authon.jwtTemplates.delete(template.id); ``` ### Express Middleware ```ts import { expressMiddleware } from '@authon/node'; app.use('/api', expressMiddleware({ secretKey: process.env.AUTHON_SECRET_KEY!, onError: (err) => console.error(err), })); app.get('/api/profile', (req, res) => { res.json({ user: req.auth }); }); ``` ### Express Webhook Handler ```ts app.post('/webhooks/authon', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-authon-signature'] as string; const timestamp = req.headers['x-authon-timestamp'] as string; try { const event = authon.webhooks.verify(req.body, signature, timestamp, process.env.AUTHON_WEBHOOK_SECRET!); switch (event.type) { case 'user.created': console.log('New user:', event.data); break; case 'user.updated': console.log('User updated:', event.data); break; case 'user.deleted': console.log('User deleted:', event.data); break; case 'session.created': console.log('Session created:', event.data); break; } res.json({ received: true }); } catch { res.status(400).json({ error: 'Invalid signature' }); } }); ``` ### Fastify Plugin ```ts import { fastifyPlugin } from '@authon/node'; const authHook = fastifyPlugin({ secretKey: process.env.AUTHON_SECRET_KEY! }); app.addHook('onRequest', authHook); app.get('/api/profile', async (request) => ({ user: request.auth })); ``` --- ## SDK Reference — Python {#sdk-python} ### Install ```bash pip install authon ``` Requires Python >= 3.8. Uses httpx. ### Basic Usage ```python from authon import AuthonBackend authon = AuthonBackend("sk_live_...") user = authon.verify_token("eyJ...") result = authon.users.list(page=1, limit=10) new_user = authon.users.create(email="user@example.com", password="secret") authon.users.update("user_abc123", display_name="Updated Name") authon.users.ban("user_abc123", reason="Spam") authon.users.delete("user_abc123") ``` ### Async Client ```python from authon import AsyncAuthonBackend authon = AsyncAuthonBackend("sk_live_...") user = await authon.verify_token("eyJ...") ``` ### FastAPI ```python from fastapi import FastAPI, Depends, Header from authon.middleware.fastapi import AuthonDependency from authon.types import AuthonUser app = FastAPI() authon_dep = AuthonDependency("sk_live_...") @app.get("/api/profile") async def profile(user: AuthonUser = Depends(authon_dep)): return {"id": user.id, "email": user.email} ``` ### Django ```python from authon import AuthonBackend from authon.middleware.django import authon_login_required authon = AuthonBackend("sk_live_...") @authon_login_required(authon) def profile(request): user = request.authon_user return JsonResponse({"id": user.id, "email": user.email}) ``` ### Flask ```python from authon import AuthonBackend from authon.middleware.flask import flask_authon_required authon = AuthonBackend("sk_live_...") @app.route("/api/profile") @flask_authon_required(authon) def profile(): user = g.authon_user return jsonify({"id": user.id, "email": user.email}) ``` ### Webhook Verification ```python from authon import verify_webhook event = verify_webhook( payload=request_body, signature=request.headers["x-authon-signature"], secret="whsec_...", ) ``` --- ## SDK Reference — Go {#sdk-go} ### Install ```bash go get github.com/mikusnuz/authon-sdk/go ``` Requires Go >= 1.21. ### Usage ```go import authon "github.com/mikusnuz/authon-sdk/go" client := authon.NewBackend("sk_live_...") // Verify token user, err := client.VerifyToken("eyJ...") // HTTP middleware mux.Handle("/api/profile", client.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := authon.UserFromContext(r.Context()) w.Write([]byte("Hello, " + user.DisplayName)) }))) // User management result, _ := client.Users.List(authon.ListOptions{Page: 1, PerPage: 20}) user, _ := client.Users.Get("user_abc123") user, _ := client.Users.Create(authon.CreateUserParams{Email: "user@example.com", Password: "secret"}) client.Users.Delete("user_abc123") // Webhooks data, err := client.Webhooks.Verify([]byte(body), signature, "whsec_...") // Custom API URL client := authon.NewBackend("sk_live_...", authon.WithAPIURL("https://custom.api")) ``` --- ## SDK Reference — Dart/Flutter {#sdk-dart} ### Install ```yaml dependencies: authon: ^0.1.0 ``` ### Setup ```dart import 'package:authon/authon.dart'; void main() { runApp(AuthonProvider(publishableKey: 'pk_live_...', child: const MyApp())); } ``` ### Usage ```dart final authon = Authon.of(context); // OAuth await authon.signInWithOAuth(OAuthProvider.google); // Email final user = await authon.signInWithEmail('user@example.com', 'password'); // Auth state AuthonBuilder(builder: (context, state) { if (state.isLoading) return CircularProgressIndicator(); if (!state.isSignedIn) return SignInScreen(); return Text('Welcome, ${state.user!.displayName}'); }); // Token / sign out final token = await authon.getToken(); await authon.signOut(); ``` --- ## SDK Reference — Swift {#sdk-swift} ### Install (SPM) ```swift dependencies: [ .package(url: "https://github.com/mikusnuz/authon-sdk.git", from: "0.1.0") ] ``` ### Usage ```swift import Authon let authon = AuthonClient(publishableKey: "pk_live_...") // OAuth let user = try await authon.signIn(with: .google) // Email let user = try await authon.signIn(email: "user@example.com", password: "password") // Auth state if let user = authon.currentUser { print(user.displayName) } let token = try await authon.getToken() try await authon.signOut() // SwiftUI @StateObject private var auth = AuthonObservable(publishableKey: "pk_live_...") // auth.user, auth.isSignedIn, auth.isLoading ``` Tokens stored in Keychain. OAuth uses ASWebAuthenticationSession. --- ## SDK Reference — Kotlin/Android {#sdk-kotlin} ### Install (Gradle) ```kotlin implementation("dev.authon:sdk:0.1.0") ``` ### Usage ```kotlin import dev.authon.sdk.AuthonClient val authon = AuthonClient(publishableKey = "pk_live_...", context = applicationContext) // OAuth val user = authon.signInWithOAuth(OAuthProvider.GOOGLE, activity) // Email val user = authon.signInWithEmail("user@example.com", "password") // Google One Tap val user = authon.signInWithGoogleOneTap(activity) // Auth state (Kotlin Flow) authon.authState.collect { state -> when { state.isLoading -> showLoading() state.user != null -> showProfile(state.user) else -> showSignIn() } } // Jetpack Compose val auth = rememberAuthonState(publishableKey = "pk_live_...") // auth.isLoading, auth.isSignedIn, auth.user // Token / sign out val token = authon.getToken() authon.signOut() ``` Tokens stored in EncryptedSharedPreferences (AES256-GCM). OAuth uses Chrome Custom Tabs. --- ## REST API Endpoints {#rest-api-endpoints} Base URL: `https://api.authon.dev` All requests require `x-api-key` header with either a publishable key (client endpoints) or secret key (backend endpoints). Authenticated endpoints also require `Authorization: Bearer `. ### Client Auth Endpoints (publishable key) | Method | Path | Description | |--------|------|-------------| | POST | `/v1/auth/signup` | Sign up with email/password | | POST | `/v1/auth/signin` | Sign in with email/password | | GET | `/v1/auth/branding` | Get project branding config | | GET | `/v1/auth/providers` | List enabled OAuth providers | | GET | `/v1/auth/oauth/{provider}/url` | Get OAuth authorization URL | | GET | `/v1/auth/oauth/poll?state=...` | Poll OAuth completion status | | GET | `/v1/auth/oauth/redirect` | OAuth redirect handler | | POST | `/v1/auth/passwordless/magic-link` | Send magic link | | POST | `/v1/auth/passwordless/email-otp` | Send email OTP | | POST | `/v1/auth/passwordless/verify` | Verify magic link or OTP | | POST | `/v1/auth/passkeys/register/options` | Get passkey registration options (auth required) | | POST | `/v1/auth/passkeys/register/verify` | Verify passkey registration (auth required) | | POST | `/v1/auth/passkeys/authenticate/options` | Get passkey auth options | | POST | `/v1/auth/passkeys/authenticate/verify` | Verify passkey authentication | | GET | `/v1/auth/passkeys` | List registered passkeys (auth required) | | PATCH | `/v1/auth/passkeys/{id}` | Rename passkey (auth required) | | DELETE | `/v1/auth/passkeys/{id}` | Delete passkey (auth required) | | POST | `/v1/auth/web3/nonce` | Get Web3 sign-in nonce | | POST | `/v1/auth/web3/verify` | Verify Web3 signature | | GET | `/v1/auth/web3/wallets` | List linked wallets (auth required) | | POST | `/v1/auth/web3/wallets/link` | Link wallet (auth required) | | DELETE | `/v1/auth/web3/wallets/{id}` | Unlink wallet (auth required) | | POST | `/v1/auth/mfa/totp/setup` | Begin TOTP MFA setup (auth required) | | POST | `/v1/auth/mfa/totp/verify-setup` | Confirm MFA setup (auth required) | | POST | `/v1/auth/mfa/verify` | Verify MFA code during sign-in | | POST | `/v1/auth/mfa/disable` | Disable MFA (auth required) | | GET | `/v1/auth/mfa/status` | Get MFA status (auth required) | | POST | `/v1/auth/mfa/backup-codes/regenerate` | Regenerate backup codes (auth required) | | GET | `/v1/auth/token/verify` | Verify access token | | PATCH | `/v1/auth/me` | Update user profile (auth required) | | GET | `/v1/auth/me/sessions` | List user sessions (auth required) | | DELETE | `/v1/auth/me/sessions/{id}` | Revoke session (auth required) | | GET | `/v1/auth/organizations` | List user's organizations (auth required) | | POST | `/v1/auth/organizations` | Create organization (auth required) | | GET | `/v1/auth/organizations/{id}` | Get organization (auth required) | | PATCH | `/v1/auth/organizations/{id}` | Update organization (auth required) | | DELETE | `/v1/auth/organizations/{id}` | Delete organization (auth required) | | GET | `/v1/auth/organizations/{id}/members` | List organization members (auth required) | | DELETE | `/v1/auth/organizations/{id}/members/{memberId}` | Remove member (auth required) | | PATCH | `/v1/auth/organizations/{id}/members/{memberId}` | Update member role (auth required) | | POST | `/v1/auth/organizations/{id}/invitations` | Send invitation (auth required) | | GET | `/v1/auth/organizations/{id}/invitations` | List invitations (auth required) | | POST | `/v1/auth/organizations/invitations/{token}/accept` | Accept invitation (auth required) | | POST | `/v1/auth/organizations/invitations/{token}/reject` | Reject invitation (auth required) | | POST | `/v1/auth/organizations/{id}/leave` | Leave organization (auth required) | ### Backend Endpoints (secret key) | Method | Path | Description | |--------|------|-------------| | GET | `/v1/backend/users` | List all users | | GET | `/v1/backend/users/{id}` | Get user by ID | | GET | `/v1/backend/users/by-external-id/{externalId}` | Get user by external ID | | POST | `/v1/backend/users` | Create user | | PATCH | `/v1/backend/users/{id}` | Update user | | DELETE | `/v1/backend/users/{id}` | Delete user | | POST | `/v1/backend/users/{id}/ban` | Ban user | | POST | `/v1/backend/users/{id}/unban` | Unban user | | GET | `/v1/backend/users/{id}/sessions` | List user sessions | | DELETE | `/v1/backend/users/{id}/sessions/{sessionId}` | Revoke session | | GET | `/v1/backend/users/{id}/passkeys` | List user passkeys | | DELETE | `/v1/backend/users/{id}/passkeys/{credentialId}` | Delete passkey | | GET | `/v1/backend/users/{id}/web3-wallets` | List user wallets | | POST | `/v1/backend/web3/verify-signature` | Verify Web3 signature | | POST | `/v1/backend/passwordless/send` | Send passwordless code | | POST | `/v1/backend/passwordless/verify` | Verify passwordless code | | GET | `/v1/backend/organizations` | List organizations | | GET | `/v1/backend/organizations/{id}` | Get organization | | POST | `/v1/backend/organizations` | Create organization | | DELETE | `/v1/backend/organizations/{id}` | Delete organization | | GET | `/v1/backend/organizations/{id}/members` | List members | | POST | `/v1/backend/organizations/{id}/members` | Add member | | DELETE | `/v1/backend/organizations/{id}/members/{userId}` | Remove member | | GET | `/v1/backend/audit-logs` | List audit logs | | GET | `/v1/backend/jwt-templates` | List JWT templates | | GET | `/v1/backend/jwt-templates/{id}` | Get JWT template | | POST | `/v1/backend/jwt-templates` | Create JWT template | | PUT | `/v1/backend/jwt-templates/{id}` | Update JWT template | | DELETE | `/v1/backend/jwt-templates/{id}` | Delete JWT template | --- ## Webhook Events {#webhook-events} Events are sent as POST requests with these headers: - `X-Authon-Signature`: `v1=` - `X-Authon-Timestamp`: ISO 8601 timestamp - `Content-Type`: `application/json` Signature is computed as: `HMAC-SHA256(webhookSecret, timestamp + "." + rawBody)` ### Event Types | Event | Description | |-------|-------------| | `user.created` | New user registered | | `user.updated` | User profile updated | | `user.deleted` | User deleted | | `user.banned` | User banned | | `user.unbanned` | User unbanned | | `session.created` | New session created (sign-in) | | `session.ended` | Session ended (sign-out) | | `session.revoked` | Session revoked (admin or user action) | | `provider.linked` | OAuth/Web3 provider linked | | `provider.unlinked` | OAuth/Web3 provider unlinked | ### Audit Log Event Types | Event | Description | |-------|-------------| | `auth.signup` | User signed up | | `auth.signin` | User signed in | | `auth.signin.failed` | Sign-in attempt failed | | `auth.signout` | User signed out | | `auth.token.refresh` | Token refreshed | | `auth.mfa.setup` | MFA setup initiated | | `auth.mfa.verify` | MFA verified | | `auth.passkey.register` | Passkey registered | | `auth.web3.verify` | Web3 signature verified | | `admin.user.banned` | Admin banned a user | | `admin.user.unbanned` | Admin unbanned a user | | `admin.user.deleted` | Admin deleted a user | | `org.created` | Organization created | | `org.deleted` | Organization deleted | | `org.member.added` | Member added to organization | | `org.member.removed` | Member removed from organization | | `org.member.role_changed` | Member role changed | | `org.invitation.sent` | Organization invitation sent | | `org.invitation.accepted` | Organization invitation accepted | --- ## TypeScript Types {#typescript-types} All types are exported from `@authon/shared`. ```ts interface AuthonUser { id: string; projectId: string; email: string | null; displayName: string | null; avatarUrl: string | null; phone: string | null; emailVerified: boolean; phoneVerified: boolean; isBanned: boolean; publicMetadata: Record | null; lastSignInAt: string | null; signInCount: number; createdAt: string; updatedAt: string; } interface AuthTokens { accessToken: string; refreshToken: string; expiresIn: number; // seconds user: AuthonUser; } interface SessionInfo { id: string; ipAddress: string | null; userAgent: string | null; createdAt: string; lastActiveAt: string | null; } interface MfaSetupResponse { secret: string; qrCodeUri: string; backupCodes: string[]; } interface MfaStatus { enabled: boolean; backupCodesRemaining: number; } interface PasskeyCredential { id: string; name: string | null; createdAt: string; lastUsedAt: string | null; } type Web3Chain = 'evm' | 'solana'; type Web3WalletType = 'metamask' | 'pexus' | 'walletconnect' | 'coinbase' | 'phantom' | 'trust' | 'other'; interface Web3Wallet { id: string; address: string; chain: Web3Chain; walletType: Web3WalletType; chainId: number | null; createdAt: string; } interface Web3NonceResponse { message: string; nonce: string; } interface BrandingConfig { logoDataUrl?: string; brandName?: string; primaryColorStart?: string; primaryColorEnd?: string; lightBg?: string; lightText?: string; darkBg?: string; darkText?: string; borderRadius?: number; providerOrder?: string[]; hiddenProviders?: string[]; showEmailPassword?: boolean; showDivider?: boolean; termsUrl?: string; privacyUrl?: string; customCss?: string; locale?: string; showSecuredBy?: boolean; } interface SessionConfig { accessTokenTtl?: number; // default 900 (15 min) refreshTokenTtl?: number; // default 604800 (7 days) maxSessions?: number; // default 5 singleSession?: boolean; // default false } interface AuthonOrganization { id: string; projectId: string; name: string; slug: string; logoUrl: string | null; metadata: Record | null; maxMembers: number; createdBy: string; createdAt: string; updatedAt: string; } interface OrganizationMember { id: string; organizationId: string; userId: string; role: 'owner' | 'admin' | 'member'; joinedAt: string; createdAt: string; } interface OrganizationInvitation { id: string; organizationId: string; email: string; role: string; status: 'pending' | 'accepted' | 'rejected' | 'expired'; invitedBy: string; expiresAt: string; createdAt: string; } type OAuthProviderType = 'google' | 'apple' | 'kakao' | 'naver' | 'facebook' | 'github' | 'discord' | 'x' | 'line' | 'microsoft'; type WebhookEventType = 'user.created' | 'user.updated' | 'user.deleted' | 'user.banned' | 'user.unbanned' | 'session.created' | 'session.ended' | 'session.revoked' | 'provider.linked' | 'provider.unlinked'; ``` --- ## Error Codes {#error-codes} | HTTP Status | Error | Description | |-------------|-------|-------------| | 400 | Bad Request | Missing or invalid parameters | | 401 | Unauthorized | Invalid or expired token / API key | | 403 | Forbidden | Insufficient permissions | | 404 | Not Found | Resource does not exist | | 409 | Conflict | Email already registered | | 429 | Too Many Requests | Rate limit exceeded | ### SDK-Specific Errors | Error Class | Thrown When | |-------------|------------| | `AuthonMfaRequiredError` | `signInWithEmail()` when MFA is enabled. Contains `mfaToken` property. | --- ## Common Patterns {#common-patterns} ### Protect a Next.js API route (App Router) ```ts // app/api/data/route.ts import { currentUser } from '@authon/nextjs/server'; export async function GET() { const user = await currentUser(); if (!user) return new Response('Unauthorized', { status: 401 }); return Response.json({ data: 'secret', userId: user.id }); } ``` ### Protect an Express API route ```ts import { AuthonBackend } from '@authon/node'; const authon = new AuthonBackend(process.env.AUTHON_SECRET_KEY!); async function requireAuth(req, res, next) { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) return res.status(401).json({ error: 'Missing token' }); try { req.auth = await authon.verifyToken(token); next(); } catch { res.status(401).json({ error: 'Invalid token' }); } } app.get('/api/profile', requireAuth, (req, res) => { res.json({ user: req.auth }); }); ``` ### Add Google login to React ```tsx import { AuthonProvider, useAuthon, SocialButtons } from '@authon/react'; function LoginPage() { return ( window.location.href = '/dashboard'} onError={(err) => console.error(err)} /> ); } ``` ### Add MFA to an existing app ```tsx import { useAuthonMfa } from '@authon/react'; function MfaSetup() { const { setupMfa, verifyMfaSetup, isLoading, error } = useAuthonMfa(); const [qr, setQr] = useState(''); const [code, setCode] = useState(''); const handleSetup = async () => { const result = await setupMfa(); if (result) setQr(result.qrCodeSvg); }; const handleVerify = async () => { const success = await verifyMfaSetup(code); if (success) alert('MFA enabled!'); }; return (
{qr &&
} setCode(e.target.value)} placeholder="6-digit code" />
); } ``` ### Role-based access control ```tsx import { Protect } from '@authon/react'; Admin access required

} condition={(user) => user.publicMetadata?.role === 'admin'} >
``` ### Send authenticated requests from client ```ts const { getToken } = useAuthon(); async function fetchProtectedData() { const token = getToken(); const response = await fetch('/api/data', { headers: { Authorization: `Bearer ${token}` }, }); return response.json(); } ``` --- ## Migration from Clerk {#migration-from-clerk} ### Package replacements | Clerk | Authon | |-------|--------| | `@clerk/nextjs` | `@authon/nextjs` | | `@clerk/clerk-react` | `@authon/react` | | `@clerk/clerk-js` | `@authon/js` | | `@clerk/backend` | `@authon/node` | | `@clerk/themes` | Built-in `appearance` config | ### Import replacements | Clerk | Authon | |-------|--------| | `ClerkProvider` | `AuthonProvider` | | `useAuth()` | `useAuthon()` | | `useUser()` | `useUser()` (same name) | | `useClerk()` | `useAuthon()` (returns `client`) | | `` | `` (same name) | | `` | `` (same name) | | `` | `` (same name) | | `` | `` (same name) | | `` | `` (same name) | | `clerkMiddleware()` | `authonMiddleware()` | | `currentUser()` | `currentUser()` (same name, from `@authon/nextjs/server`) | | `auth()` | `auth()` (same name, from `@authon/nextjs/server`) | ### Environment variables | Clerk | Authon | |-------|--------| | `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | `NEXT_PUBLIC_AUTHON_KEY` | | `CLERK_SECRET_KEY` | `AUTHON_SECRET_KEY` | | `CLERK_WEBHOOK_SECRET` | `AUTHON_WEBHOOK_SECRET` | ### Middleware ```ts // Before (Clerk) import { clerkMiddleware } from '@clerk/nextjs/server'; export default clerkMiddleware(); // After (Authon) import { authonMiddleware } from '@authon/nextjs'; export default authonMiddleware({ publicRoutes: ['/', '/sign-in', '/sign-up'], }); ``` ### Provider ```tsx // Before (Clerk) // After (Authon) ``` --- ## Migration from Auth.js (NextAuth) {#migration-from-authjs} ### Key differences - Auth.js uses server-side sessions by default; Authon uses JWT access/refresh tokens. - Auth.js requires configuring each provider in code; Authon configures providers in the dashboard. - Authon provides a built-in UI modal; Auth.js requires building your own or using third-party components. ### Provider setup ```ts // Before (Auth.js) — in auth.ts import GoogleProvider from "next-auth/providers/google"; export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [GoogleProvider({ clientId: "...", clientSecret: "..." })], }) // After (Authon) — configure Google in dashboard, then: // middleware.ts import { authonMiddleware } from '@authon/nextjs'; export default authonMiddleware({ publicRoutes: ['/', '/sign-in'], }); ``` ### Session access ```ts // Before (Auth.js) import { auth } from "@/auth"; const session = await auth(); const userId = session?.user?.id; // After (Authon) import { currentUser, auth } from '@authon/nextjs/server'; const user = await currentUser(); const { userId, getToken } = await auth(); ``` ### Protecting API routes ```ts // Before (Auth.js) import { auth } from "@/auth"; export async function GET() { const session = await auth(); if (!session) return new Response("Unauthorized", { status: 401 }); } // After (Authon) import { currentUser } from '@authon/nextjs/server'; export async function GET() { const user = await currentUser(); if (!user) return new Response("Unauthorized", { status: 401 }); } ``` ### Client-side auth state ```tsx // Before (Auth.js) import { useSession } from "next-auth/react"; const { data: session, status } = useSession(); // After (Authon) import { useAuthon } from '@authon/nextjs'; const { isSignedIn, isLoading, user } = useAuthon(); ```