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/jsOr 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',
});Magic link
// 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 stringVue 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.