Svelte SDK
The @passkeybase/svelte package provides Svelte stores, actions, and SvelteKit server hooks for integrating passkey authentication into Svelte applications.
Installation
npm install @passkeybase/svelte @passkeybase/jsInitialization
Call initPasskeyBase() in your root layout to initialize the SDK. This sets up the internal stores that all child components can access viagetPasskeyBase().
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { initPasskeyBase } from '@passkeybase/svelte';
initPasskeyBase({
clientId: 'pk_live_abc123',
domain: 'myapp.auth.ing',
redirectUri: window.location.origin + '/callback',
});
</script>
<slot />Auth Stores
getPasskeyBase()
Returns reactive Svelte stores for auth state, the current user, and methods for login, logout, and token access. Use the $ prefix for auto-subscription in templates.
<!-- src/routes/dashboard/+page.svelte -->
<script lang="ts">
import { getPasskeyBase } from '@passkeybase/svelte';
const { isAuthenticated, isLoading, loginWithRedirect, logout, getAccessToken } =
getPasskeyBase();
async function fetchData() {
const token = await getAccessToken();
const res = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
}
</script>
{#if $isLoading}
<div>Loading...</div>
{:else if !$isAuthenticated}
<button on:click={() => loginWithRedirect()}>Log in with Passkey</button>
{:else}
<h1>Welcome back</h1>
<button on:click={fetchData}>Fetch data</button>
<button on:click={() => logout()}>Log out</button>
{/if}User Store
Access the current user's profile as a reactive store.
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
import { getPasskeyBase } from '@passkeybase/svelte';
const { user, isLoading } = getPasskeyBase();
</script>
{#if $isLoading}
<div>Loading profile...</div>
{:else if $user}
<h2>{$user.displayName}</h2>
<p>{$user.email}</p>
<p>User ID: {$user.sub}</p>
<p>Passkeys registered: {$user.credentials.length}</p>
{/if}Protected Routes
Client-Side Guard
Create a layout component that checks auth state and redirects unauthenticated users.
<!-- src/routes/dashboard/+layout.svelte -->
<script lang="ts">
import { getPasskeyBase } from '@passkeybase/svelte';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
const { isAuthenticated, isLoading, loginWithRedirect } = getPasskeyBase();
// Redirect unauthenticated users
$: if (!$isLoading && !$isAuthenticated) {
loginWithRedirect({ state: window.location.pathname });
}
</script>
{#if $isLoading}
<div>Checking authentication...</div>
{:else if $isAuthenticated}
<slot />
{/if}SvelteKit Server Hook
Use the handle hook from@passkeybase/svelte/server to protect routes at the server level. It populates locals.session for use in load functions.
// src/hooks.server.ts
import { handle as passkeyBaseHandle } from '@passkeybase/svelte/server';
export const handle = passkeyBaseHandle({
domain: 'myapp.auth.ing',
clientId: 'pk_live_abc123',
protectedRoutes: ['/dashboard', '/settings'],
loginRoute: '/',
});Chain with other hooks using sequence:
// src/hooks.server.ts
import { sequence } from '@sveltejs/kit/hooks';
import { handle as passkeyBaseHandle } from '@passkeybase/svelte/server';
const auth = passkeyBaseHandle({
domain: 'myapp.auth.ing',
clientId: 'pk_live_abc123',
protectedRoutes: ['/dashboard', '/settings'],
});
const logger: Handle = async ({ event, resolve }) => {
console.log(`${event.request.method} ${event.url.pathname}`);
return resolve(event);
};
export const handle = sequence(auth, logger);Access the session in server load functions:
// src/routes/dashboard/+page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
// locals.session is set by the handle hook
const session = locals.session;
if (!session) {
return { status: 401 };
}
return {
user: session.user,
};
};Login and Logout
Svelte Actions
Use the use:login anduse:logout actions for declarative login/logout buttons.
<!-- src/lib/components/AuthButtons.svelte -->
<script lang="ts">
import { login, logout as logoutAction } from '@passkeybase/svelte';
import { getPasskeyBase } from '@passkeybase/svelte';
const { isAuthenticated } = getPasskeyBase();
</script>
{#if $isAuthenticated}
<button use:logoutAction={{ returnTo: window.location.origin }}>
Sign out
</button>
{:else}
<button use:login>
Sign in with Passkey
</button>
{/if}Programmatic Login
Use loginWithRedirect() andlogout() for full control.
<script lang="ts">
import { getPasskeyBase } from '@passkeybase/svelte';
const { loginWithRedirect, logout } = getPasskeyBase();
function handleLogin() {
loginWithRedirect({
scopes: ['openid', 'profile', 'email'],
state: 'return=/onboarding',
});
}
function handleLogout() {
logout({ returnTo: window.location.origin });
}
</script>
<button on:click={handleLogin}>Log in</button>
<button on:click={handleLogout}>Log out</button>Handling the Callback
Create a callback page to finalize the OAuth redirect flow.
<!-- src/routes/callback/+page.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { getPasskeyBase } from '@passkeybase/svelte';
const { handleRedirectCallback } = getPasskeyBase();
onMount(async () => {
const result = await handleRedirectCallback();
const returnTo = result.state || '/dashboard';
goto(returnTo);
});
</script>
<div>Completing login...</div>Passkey Enrollment
Use enrollPasskey() from the store to open the enrollment popup. Fetch the enrollment token from your server first.
<!-- src/routes/settings/passkeys/+page.svelte -->
<script lang="ts">
import { getPasskeyBase } from '@passkeybase/svelte';
const { getAccessToken, enrollPasskey } = getPasskeyBase();
let isEnrolling = false;
let error: string | null = null;
async function handleEnroll() {
isEnrolling = true;
error = null;
try {
const token = await getAccessToken();
const res = await fetch('/api/enrollment-token', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
});
const { enrollmentToken } = await res.json();
await enrollPasskey(enrollmentToken);
alert('Passkey added successfully!');
} catch (err: any) {
error = err.message;
} finally {
isEnrolling = false;
}
}
</script>
<h2>Passkey Management</h2>
<button on:click={handleEnroll} disabled={isEnrolling}>
{isEnrolling ? 'Enrolling...' : 'Add a new passkey'}
</button>
{#if error}
<p class="error">{error}</p>
{/if}TypeScript Support
All stores, actions, and server utilities are fully typed. The package uses Svelte'sReadable store types and re-exports types from @passkeybase/js.
import type { User, AuthResult } from '@passkeybase/js';
import type { PasskeyBaseStore } from '@passkeybase/svelte';
// getPasskeyBase() returns typed Svelte stores
const { user, isAuthenticated } = getPasskeyBase();
// $user — User | undefined (auto-subscribed)
// $isAuthenticated — boolean (auto-subscribed)
// Init options are typed
import type { PasskeyBaseConfig } from '@passkeybase/svelte';
const config: PasskeyBaseConfig = {
clientId: 'pk_live_abc123',
domain: 'myapp.auth.ing',
redirectUri: '/callback',
};Next steps
- JavaScript SDK -- core API reference
- Node.js SDK -- generate enrollment tokens on the server
- Enrollment Guide -- detailed enrollment flow walkthrough