Skip to content
XID

@xid-kit/svelte

Svelte 5 reactive stores and SvelteKit server hook for client and SSR authentication.

Status

XID publishes no packages to any registry. This SDK is distributed as source in the XID repository; use it from a checkout of the repository.

Package status is Current package. Svelte 5 stores, SvelteKit server hook, and helper utilities are implemented. A real IdP round-trip on production infrastructure is still pending manual verification.

Client setup

Create stores in the root layout using createXidStores and setXidContext. Stores use XidClient.subscribe, which satisfies the Svelte store subscriber contract.

<!-- +layout.svelte -->
<script lang="ts">
  import { setContext, onMount } from 'svelte'
  import { XidClient, createXidStores, setXidContext } from '@xid-kit/svelte'

  // Core auth endpoints must be served on this application's exact origin.
  const client = new XidClient({ mode: 'same-origin' })
  const stores = createXidStores(client)
  setXidContext(setContext, stores)

  onMount(() => {
    const ac = new AbortController()
    void client.load({ signal: ac.signal })
    return () => ac.abort()
  })
</script>
<slot />

Reading auth state

<!-- Any child component -->
<script lang="ts">
  import { getContext } from 'svelte'
  import { getXidContext } from '@xid-kit/svelte'

  const { auth, user } = getXidContext(getContext)
  // $auth: { isLoaded, isSignedIn, userId, sessionId, session }
  // $user: discriminated union on isLoaded / isSignedIn / user
</script>

{#if !$auth.isLoaded}
  <p>Loading...</p>
{:else if $auth.isSignedIn}
  <p>Hello, {$user.isSignedIn ? $user.user.fullName : ''}</p>
{:else}
  <p>Not signed in</p>
{/if}

Sign-in and sign-out

<script lang="ts">
  import { getContext } from 'svelte'
  import { getXidContext, buildSignInUrl, executeSignOut } from '@xid-kit/svelte'

  const { client } = getXidContext(getContext)

  function handleSignIn() {
    window.location.assign(buildSignInUrl('/sign-in', window.location.href))
  }

  async function handleSignOut() {
    await executeSignOut(client, { redirectUrl: '/' })
  }
</script>

Role and permission guard

<script lang="ts">
  import { getContext } from 'svelte'
  import { getXidContext, isAllowed } from '@xid-kit/svelte'

  const { state } = getXidContext(getContext)
</script>

{#if isAllowed($state, { role: 'admin' })}
  <AdminPanel />
{/if}

{#if isAllowed($state, { permission: 'org:member:write' })}
  <EditButton />
{/if}

SvelteKit server hook

// src/hooks.server.ts
import { handleXid } from '@xid-kit/svelte/server'

export const handle = handleXid({
  jwtKey: JSON.parse(process.env.XID_JWKS_PUBLIC_KEY!),
  issuer: 'https://xid.dev',
  sessionTokenExchange: { endpoint: '/v1/sessions/token' },
  protectedRoutes: ['/dashboard', '/account'],
  signInUrl: '/sign-in',
})

Reading auth in load functions

// +page.server.ts
import { redirect } from '@sveltejs/kit'
import { getXidAuth } from '@xid-kit/svelte/server'
import type { PageServerLoad } from './$types'

export const load: PageServerLoad = async ({ locals }) => {
  const auth = getXidAuth(locals)
  if (!auth.userId) throw redirect(303, '/sign-in')
  return { userId: auth.userId, orgId: auth.orgId }
}

App.Locals typing

// src/app.d.ts
import type { AuthResult } from '@xid-kit/svelte/server'

declare global {
  namespace App {
    interface Locals {
      xidAuth: AuthResult
    }
  }
}
export {}

Exported API

Export Kind Module
XidClient, createXidStores, setXidContext, getXidContext client setup @xid-kit/svelte
buildSignInUrl, executeSignOut, isAllowed utilities @xid-kit/svelte
handleXid, getXidAuth server hook @xid-kit/svelte/server

Type to search

Use arrow keys to navigateEnter to selectEscape to close