@authrim/sveltekit
v0.1.7
Published
SvelteKit SDK for Authrim authentication
Readme
@authrim/sveltekit
Official SvelteKit SDK for Authrim authentication.
Features
- Passkey Authentication - WebAuthn-based passwordless login
- Email Code (OTP) - One-time password via email
- Social Login - Google, GitHub, Apple, Microsoft, Facebook
- Svelte Stores - Reactive authentication state
- Server-Side Integration - SvelteKit hooks and load functions
- SSR Support - Full server-side rendering compatibility
- TypeScript - Complete type definitions
- UI Components - Ready-to-use authentication components
Requirements
- Node.js >= 18
- SvelteKit >= 2.0
- Svelte >= 4.0 or >= 5.0
- @authrim/core >= 0.1.10
Installation
# pnpm (recommended)
pnpm add @authrim/sveltekit @authrim/core
# npm
npm install @authrim/sveltekit @authrim/core
# yarn
yarn add @authrim/sveltekit @authrim/coreNote:
@authrim/coreis a peer dependency that provides the underlying authentication logic. You need to install it alongside@authrim/sveltekit, but you don't need to import from it directly—all necessary types and functions are re-exported from@authrim/sveltekit.
Quick Start
1. Create Auth Client
// src/lib/auth.ts
import { createAuthrim } from '@authrim/sveltekit';
export const auth = await createAuthrim({
issuer: 'https://auth.example.com',
clientId: 'your-client-id',
});By default, @authrim/sveltekit uses server-mediated auth: Direct Auth artifacts
are redeemed by your SvelteKit server and OAuth/OIDC tokens are not returned to
browser JavaScript.
Server session cookies are HttpOnly and AES-GCM encrypted with
AUTHRIM_SESSION_SECRET; use at least 32 bytes of high-entropy secret material.
2. Set Up Server-Mediated Auth Endpoints
// src/routes/authrim/session/exchange/+server.ts
import { createDirectAuthSessionHandlers } from '@authrim/sveltekit/server';
import { AUTHRIM_ISSUER, AUTHRIM_CLIENT_ID, AUTHRIM_SESSION_SECRET } from '$env/static/private';
const handlers = createDirectAuthSessionHandlers({
issuer: AUTHRIM_ISSUER,
clientId: AUTHRIM_CLIENT_ID,
sessionSecret: AUTHRIM_SESSION_SECRET,
});
export const POST = handlers.exchange;// src/routes/authrim/session/+server.ts
import { createDirectAuthSessionHandlers } from '@authrim/sveltekit/server';
import { AUTHRIM_ISSUER, AUTHRIM_CLIENT_ID, AUTHRIM_SESSION_SECRET } from '$env/static/private';
const handlers = createDirectAuthSessionHandlers({
issuer: AUTHRIM_ISSUER,
clientId: AUTHRIM_CLIENT_ID,
sessionSecret: AUTHRIM_SESSION_SECRET,
});
export const GET = handlers.session;// src/routes/authrim/session/logout/+server.ts
import { createDirectAuthSessionHandlers } from '@authrim/sveltekit/server';
import { AUTHRIM_ISSUER, AUTHRIM_CLIENT_ID, AUTHRIM_SESSION_SECRET } from '$env/static/private';
const handlers = createDirectAuthSessionHandlers({
issuer: AUTHRIM_ISSUER,
clientId: AUTHRIM_CLIENT_ID,
sessionSecret: AUTHRIM_SESSION_SECRET,
});
export const POST = handlers.logout;3. Set Up Server Hooks
// src/hooks.server.ts
import { createAuthHandle } from '@authrim/sveltekit/server';
import { AUTHRIM_SESSION_SECRET } from '$env/static/private';
export const handle = createAuthHandle({
sessionSecret: AUTHRIM_SESSION_SECRET,
});4. Configure Type Safety
// src/app.d.ts
import type { ServerAuthContext } from '@authrim/sveltekit/server';
declare global {
namespace App {
interface Locals {
auth?: ServerAuthContext;
}
}
}
export {};5. Set Up AuthProvider
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { AuthProvider } from '@authrim/sveltekit/components';
import { auth } from '$lib/auth';
export let data;
</script>
<AuthProvider
{auth}
initialSession={data.auth?.session}
initialUser={data.auth?.user}
>
<slot />
</AuthProvider>// src/routes/+layout.server.ts
import { createAuthLoad } from '@authrim/sveltekit/server';
export const load = createAuthLoad();6. Use Authentication
<!-- src/routes/+page.svelte -->
<script lang="ts">
import { getAuthContext } from '@authrim/sveltekit';
const auth = getAuthContext();
const { session, user, isAuthenticated, loadingState } = auth.stores;
async function handleLogin() {
const result = await auth.passkey.login();
if (result.error) {
console.error(result.error.message);
}
}
</script>
{#if $loadingState !== 'idle'}
<p>Loading...</p>
{:else if $isAuthenticated}
<p>Welcome, {$user?.name}</p>
<button on:click={() => auth.signOut()}>Sign Out</button>
{:else}
<button on:click={handleLogin}>Sign In with Passkey</button>
{/if}API Reference
createAuthrim(config)
Creates an Authrim client instance.
const auth = await createAuthrim({
issuer: 'https://auth.example.com',
clientId: 'your-client-id',
storage: {
storage: 'sessionStorage', // 'memory' | 'sessionStorage' | 'localStorage'
prefix: 'authrim',
},
});The default authMode is 'server'. Set authMode: 'browser' only when this
SvelteKit app intentionally uses browser-held OAuth/OIDC tokens.
const auth = await createAuthrim({
issuer: 'https://auth.example.com',
clientId: 'your-client-id',
authMode: 'browser',
browserPublicClientMode: 'strict',
});Authentication Namespaces
auth.passkey
// Login with Passkey
const result = await auth.passkey.login();
// Login with Conditional UI (autofill)
const result = await auth.passkey.login({ conditional: true });
// Sign up with Passkey
const result = await auth.passkey.signUp({ email: '[email protected]' });
// Register new Passkey (requires authentication)
const credential = await auth.passkey.register();
// Check support
auth.passkey.isSupported(); // boolean
await auth.passkey.isConditionalUIAvailable(); // Promise<boolean>
// Cancel Conditional UI
auth.passkey.cancelConditionalUI();auth.emailCode
// Send verification code
const result = await auth.emailCode.send('[email protected]');
// Verify code
const result = await auth.emailCode.verify('[email protected]', '123456');
// Check pending verification
auth.emailCode.hasPendingVerification('[email protected]'); // boolean
auth.emailCode.getRemainingTime('[email protected]'); // seconds
auth.emailCode.clearPendingVerification('[email protected]');auth.social
// Login with popup
const result = await auth.social.loginWithPopup('google');
// Login with redirect
await auth.social.loginWithRedirect('github');
// Handle callback (after redirect)
if (auth.social.hasCallbackParams()) {
const result = await auth.social.handleCallback();
}
// Get supported providers
auth.social.getSupportedProviders(); // ['google', 'github', 'apple', 'microsoft', 'facebook']auth.session
// Get current session
const result = await auth.session.get();
// Validate session
const isValid = await auth.session.validate();
// Get user
const user = await auth.session.getUser();
// Refresh session cache
const session = await auth.session.refresh();
// Check authentication
const isAuth = await auth.session.isAuthenticated();
// Clear cache
auth.session.clearCache();Shortcuts
// Sign in
await auth.signIn.passkey();
await auth.signIn.social('google');
// Sign up
await auth.signUp.passkey({ email: '[email protected]' });
// Sign out
await auth.signOut();
await auth.signOut({ redirectUri: '/login' });Svelte Stores
All stores are Readable (not Writable) to ensure events are the source of truth.
const { session, user, isAuthenticated, loadingState, error } = auth.stores;| Store | Type | Description |
|-------|------|-------------|
| session | Readable<Session \| null> | Current session |
| user | Readable<User \| null> | Current user |
| isAuthenticated | Readable<boolean> | Authentication status (derived from session) |
| loadingState | Readable<AuthLoadingState> | Current loading state |
| error | Readable<AuthError \| null> | Last error |
AuthLoadingState
| State | Description |
|-------|-------------|
| 'idle' | Completely stable (also after errors) |
| 'initializing' | Initial session check |
| 'authenticating' | Login/signup in progress |
| 'refreshing' | Session refresh in progress |
| 'signing_out' | Sign out in progress |
Important: After any operation completes (success or error),
loadingStatereturns to'idle'. Useerror !== nullto detect error conditions.
Events
// Subscribe to events
const unsubscribe = auth.on('auth:login', ({ session, user, method }) => {
console.log('Logged in:', user.email, 'via', method);
});
auth.on('auth:logout', ({ redirectUri }) => {
console.log('Logged out');
});
auth.on('auth:error', ({ error }) => {
console.error('Auth error:', error.message);
});
auth.on('session:changed', ({ session, user }) => {
console.log('Session changed');
});
auth.on('session:expired', ({ reason }) => {
console.log('Session expired:', reason);
});
// Unsubscribe
unsubscribe();Cleanup
// When not using AuthProvider, manually cleanup resources
auth.destroy();Components
AuthProvider
Provides auth context to child components.
<script lang="ts">
import { AuthProvider } from '@authrim/sveltekit/components';
import { auth } from '$lib/auth';
export let data;
</script>
<AuthProvider
{auth}
initialSession={data.auth?.session}
initialUser={data.auth?.user}
>
<slot />
</AuthProvider>Props:
auth(required): Authrim client instanceinitialSession: Session from SSRinitialUser: User from SSR
SignInButton
<script lang="ts">
import { SignInButton } from '@authrim/sveltekit/components';
</script>
<!-- Passkey login -->
<SignInButton
method="passkey"
on:success={({ detail }) => console.log(detail.user)}
on:error={({ detail }) => console.error(detail.message)}
>
Sign In with Passkey
</SignInButton>
<!-- Social login -->
<SignInButton method="social" provider="google">
Sign In with Google
</SignInButton>SignOutButton
<script lang="ts">
import { SignOutButton } from '@authrim/sveltekit/components';
</script>
<SignOutButton
redirectUri="/login"
on:success={() => console.log('Signed out')}
on:error={({ detail }) => console.error(detail)}
>
Sign Out
</SignOutButton>UserProfile
<script lang="ts">
import { UserProfile } from '@authrim/sveltekit/components';
</script>
<UserProfile showAvatar showEmail>
<svelte:fragment slot="avatar" let:user>
<img src={user.picture} alt={user.name} />
</svelte:fragment>
</UserProfile>ProtectedRoute
<script lang="ts">
import { ProtectedRoute } from '@authrim/sveltekit/components';
</script>
<ProtectedRoute redirectTo="/login" includeReturnPath>
<Dashboard />
<svelte:fragment slot="loading">
<Spinner />
</svelte:fragment>
<svelte:fragment slot="unauthenticated">
<p>Please sign in to continue.</p>
</svelte:fragment>
</ProtectedRoute>Server-Side Integration
Handle Hook
// src/hooks.server.ts
import { createAuthHandle } from '@authrim/sveltekit/server';
import { AUTHRIM_SESSION_SECRET } from '$env/static/private';
export const handle = createAuthHandle({
sessionSecret: AUTHRIM_SESSION_SECRET,
cookieName: 'authrim_session',
secure: true,
sameSite: 'lax',
});Protected Routes (Server)
// src/routes/dashboard/+page.server.ts
import { requireAuth } from '@authrim/sveltekit/server';
export const load = requireAuth({
loginUrl: '/login',
redirectParam: 'redirectTo',
});Layout Data
// src/routes/+layout.server.ts
import { createAuthLoad } from '@authrim/sveltekit/server';
export const load = createAuthLoad();Helper Functions
import {
isAuthenticated,
getUser,
getSession,
getAuthFromEvent
} from '@authrim/sveltekit/server';
// In +page.server.ts or hooks
export const load = async ({ locals }) => {
if (isAuthenticated(locals)) {
const user = getUser(locals);
const session = getSession(locals);
// ...
}
};
// In handle hook
const authContext = getAuthFromEvent(event);Package Exports
// Main client
import { createAuthrim, getAuthContext, setAuthContext } from '@authrim/sveltekit';
// Server utilities
import {
createAuthHandle,
requireAuth,
createAuthLoad,
isAuthenticated,
getUser,
getSession
} from '@authrim/sveltekit/server';
// Components
import {
AuthProvider,
SignInButton,
SignOutButton,
UserProfile,
ProtectedRoute
} from '@authrim/sveltekit/components';
// Stores (for advanced use)
import { createAuthStores } from '@authrim/sveltekit/stores';Design Principles
Stores are observation-only: All stores are
Readable, notWritable. This ensures that the event system is the single source of truth.Events are source of truth: Store updates are projections of events. This makes the data flow predictable and debuggable.
Components are thin wrappers: UI components delegate behavior to props/events. They don't implement business logic.
SSR-first: Full server-side rendering support with proper hydration handling.
Type safety: Complete TypeScript support with strict typing.
Security Considerations
- PKCE: All OAuth flows use PKCE (Proof Key for Code Exchange)
- Secure Storage: Session tokens are stored securely with configurable storage options
- CSRF Protection: State parameter validation for OAuth flows
- HttpOnly Cookies: Server-side session cookies are HttpOnly and encrypted by default
