Back to arky.io

Authentication

Implement user authentication with magic links and guest access

Arky uses passwordless authentication via magic links (email verification codes). Users enter their email, receive a 6-digit code, and verify it to log in. No passwords needed.

How It Works

  1. User enters their email
  2. A 6-digit verification code is sent to their inbox
  3. User enters the code
  4. Tokens are issued and stored automatically

If the email doesn’t match an existing account, one is created automatically on verification.

Step 1: Request a Code

POST /v1/auth/code
SDK: sdk.auth.code(params)
await sdk.auth.code({
  email: 'user@example.com',
});

// User receives email with 6-digit verification code

Step 2: Verify the Code

POST /v1/auth/verify
SDK: sdk.auth.verify(params)
const result = await sdk.auth.verify({
  email: 'user@example.com',
  code: '123456',
});

// Tokens are automatically stored via the setToken callback
console.log('Logged in:', result.accessToken);

That’s it. No registration step, no password, no email confirmation flow. The verify step handles everything: creates the account if needed, issues tokens, and stores them.

Business-Scoped Authentication

For multi-tenant apps where users belong to a specific business, use business-scoped auth. The verification email is branded to the business.

// Step 1: Request code (scoped to business)
await sdk.auth.businessCode('biz_abc123', {
  email: 'customer@example.com',
});

// Step 2: Verify code
const result = await sdk.auth.businessVerify('biz_abc123', {
  email: 'customer@example.com',
  code: '123456',
});

Guest Access

Allow users to browse and interact without logging in:

// Create a guest session (idempotent - safe to call multiple times)
await sdk.auth.signInAnonymously();

// Check if current user is a guest
const isGuest = await sdk.auth.isGuest();

if (isGuest) {
  // Show login prompt
}
Tip

Guest accounts are automatically converted to full accounts when the user verifies their email. Any data associated with the guest session (cart, subscriptions) is preserved.

Token Management

Tokens are managed via the getToken and setToken callbacks you provide during SDK initialization:

import { createSdk } from '@arky/sdk';

const sdk = createSdk({
  businessId: 'biz_abc123',
  getToken: async () => {
    const stored = localStorage.getItem('arky_tokens');
    return stored ? JSON.parse(stored) : null;
  },
  setToken: async (tokens) => {
    if (tokens) {
      localStorage.setItem('arky_tokens', JSON.stringify(tokens));
    } else {
      localStorage.removeItem('arky_tokens');
    }
  },
});

Refreshing Tokens

Access tokens expire after 1 hour. Use the refresh token (valid for 7 days) to get a new one:

const result = await sdk.auth.refresh({
  refreshToken: 'eyJhbGciOiJIUzI1NiIs...',
});

Logging Out

Clear the stored tokens:

function logout() {
  localStorage.removeItem('arky_tokens');
}

Complete Auth Flow Example

import { createSdk } from '@arky/sdk';

const sdk = createSdk({
  businessId: 'biz_abc123',
  getToken: async () => {
    const stored = localStorage.getItem('arky_tokens');
    return stored ? JSON.parse(stored) : null;
  },
  setToken: async (tokens) => {
    if (tokens) {
      localStorage.setItem('arky_tokens', JSON.stringify(tokens));
    } else {
      localStorage.removeItem('arky_tokens');
    }
  },
});

// Check auth state
async function checkAuth() {
  const isGuest = await sdk.auth.isGuest();
  if (isGuest) return null;
  return await sdk.account.getMe({});
}

// Login flow
async function login(email: string) {
  await sdk.auth.code({ email });
  // Show code input UI...
}

async function verifyLogin(email: string, code: string) {
  await sdk.auth.verify({ email, code });
  // Tokens stored automatically, user is logged in
  return await sdk.account.getMe({});
}

// Logout
function logout() {
  localStorage.removeItem('arky_tokens');
}

Error Handling

try {
  await sdk.auth.verify({
    email: 'user@example.com',
    code: '000000',
  });
} catch (error) {
  // Invalid or expired code
  console.error('Verification failed:', error.message);
}

Next Steps