Skip to content

@xid-kit/svelte

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

Status

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'

  const client = new XidClient({ apiUrl: 'https://acme.xid.dev' })
  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: 'org: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_JWT_KEY!),
  issuer: 'https://xid.dev',
  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
Navigation

Type to search...

Use arrow keys to navigateEnter to selectEscape to close