HelloJohn / docs
Sdks

JavaScript SDK

Use @hellojohn/js to add authentication to vanilla JavaScript apps, Vue, Svelte, Angular, and any non-React framework.

JavaScript SDK

@hellojohn/js is the framework-agnostic client SDK. Use it with vanilla JavaScript, Vue, Svelte, Angular, or any frontend that isn't React.

Installation

npm install @hellojohn/js

Or via CDN:

<script type="module">
  import { createHelloJohnClient } from 'https://cdn.hellojohn.dev/js/latest/hellojohn.js';
</script>

Setup

import { createHelloJohnClient } from '@hellojohn/js';

const hj = createHelloJohnClient({
  publishableKey: 'pk_live_...',
  // Optional
  apiUrl: 'https://auth.yourdomain.com',  // for self-hosted
  tenantId: 'tnt_01H...',
});

Authentication

Sign in

const result = await hj.signIn({
  email: 'alice@example.com',
  password: 'strongpassword',
});

if (result.status === 'complete') {
  console.log('Signed in:', result.session.user.email);
  window.location.href = '/dashboard';
} else if (result.status === 'mfa_required') {
  // Show MFA form
  showMFAForm(result.mfaChallenge);
}

Sign up

const result = await hj.signUp({
  email: 'alice@example.com',
  password: 'strongpassword',
  displayName: 'Alice',
});
// Send magic link
await hj.sendMagicLink({ email: 'alice@example.com' });

// On the callback page (after user clicks link)
const url = new URL(window.location.href);
const token = url.searchParams.get('token');

if (token) {
  const result = await hj.verifyMagicLink({ token });
  if (result.status === 'complete') {
    window.location.href = '/dashboard';
  }
}

OAuth

// Redirect to OAuth provider
await hj.signInWithOAuth({
  provider: 'google',  // google | github | gitlab | discord | microsoft
  redirectUrl: 'https://yourdomain.com/auth/callback',
});

// On the callback page
const result = await hj.handleOAuthCallback();
if (result.status === 'complete') {
  window.location.href = '/dashboard';
}

Sign out

await hj.signOut();
window.location.href = '/';

Session management

// Get current session
const session = hj.getSession();

if (session) {
  console.log('User:', session.user.email);
  console.log('Expires:', new Date(session.expiresAt));
}

// Get a fresh access token (auto-refreshes if expired)
const token = await hj.getToken();

// Use with fetch
const response = await fetch('/api/data', {
  headers: { Authorization: `Bearer ${token}` },
});

// Listen for auth state changes
hj.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_IN') {
    updateUI(session.user);
  } else if (event === 'SIGNED_OUT') {
    showSignInPage();
  } else if (event === 'TOKEN_REFRESHED') {
    // Token was automatically refreshed
  }
});

MFA

// After sign-in returns status: "mfa_required"
const challenge = result.mfaChallenge;

// Verify TOTP code
const mfaResult = await hj.verifyMFA({
  challengeId: challenge.id,
  code: '123456',
  method: 'totp',
});

// Use a backup code
const mfaResult = await hj.verifyMFA({
  challengeId: challenge.id,
  code: 'backup-code-here',
  method: 'backup_code',
});

User object

const session = hj.getSession();
const user = session.user;

// Available fields:
user.id           // "usr_01H..."
user.email        // "alice@example.com"
user.emailVerified // true
user.displayName  // "Alice"
user.avatarUrl    // "https://..."
user.role         // "admin" | "member"
user.tenantId     // "tnt_01H..."
user.organizationId // "org_01H..." | null
user.publicMetadata // { plan: "pro" }
user.createdAt    // ISO 8601 string

Vue 3 example

// composables/useAuth.js
import { ref, onMounted } from 'vue';
import { createHelloJohnClient } from '@hellojohn/js';

const hj = createHelloJohnClient({ publishableKey: import.meta.env.VITE_HJ_KEY });

export function useAuth() {
  const user = ref(null);
  const isLoaded = ref(false);

  onMounted(() => {
    user.value = hj.getSession()?.user ?? null;
    isLoaded.value = true;

    hj.onAuthStateChange((event, session) => {
      user.value = session?.user ?? null;
    });
  });

  return {
    user,
    isLoaded,
    isSignedIn: () => user.value !== null,
    signIn: (params) => hj.signIn(params),
    signOut: () => hj.signOut(),
    getToken: () => hj.getToken(),
  };
}

Svelte example

<!-- Auth.svelte -->
<script>
  import { onMount } from 'svelte';
  import { createHelloJohnClient } from '@hellojohn/js';

  const hj = createHelloJohnClient({ publishableKey: import.meta.env.VITE_HJ_KEY });

  let user = null;
  let isLoaded = false;

  onMount(() => {
    user = hj.getSession()?.user ?? null;
    isLoaded = true;

    return hj.onAuthStateChange((event, session) => {
      user = session?.user ?? null;
    });
  });

  async function signIn() {
    const result = await hj.signIn({ email, password });
    if (result.status === 'complete') {
      user = result.session.user;
    }
  }
</script>

{#if !isLoaded}
  <p>Loading...</p>
{:else if user}
  <p>Hello, {user.displayName}</p>
  <button on:click={() => hj.signOut()}>Sign out</button>
{:else}
  <button on:click={signIn}>Sign in</button>
{/if}

Token storage options

const hj = createHelloJohnClient({
  publishableKey: 'pk_live_...',
  tokenStorage: 'cookie',        // default — httpOnly cookie (most secure)
  // tokenStorage: 'localStorage', // persists across tabs (less secure)
  // tokenStorage: 'memory',       // lost on page refresh (most secure, SPA only)
});

Recommendation: Use cookie storage (the default) for best security. The cookie is set with HttpOnly, Secure, and SameSite=Lax flags.

On this page