npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sourceregistry/sveltekit-oidc

v1.6.9

Published

OIDC authentication helpers for SvelteKit applications

Readme

sveltekit-oidc

npm version npm downloads license types Svelte publint

OIDC authentication helpers for SvelteKit with:

  • server-side login, callback, logout, and session refresh flows
  • token endpoint auth support for none, client_secret_basic, client_secret_post, client_secret_jwt, and private_key_jwt
  • back-channel logout support through a revocation store
  • encrypted cookie-backed sessions and PKCE/state protection
  • a handle hook for attaching auth state to event.locals
  • a small OIDCContext provider for client-side auth state and session lifecycle handling

Install

npm install @sourceregistry/sveltekit-oidc

Server Setup

// src/lib/server/auth.ts
import { createOIDC } from '@sourceregistry/sveltekit-oidc/server';

export const oidc = createOIDC({
	issuer: 'https://your-idp.example.com',
	clientId: process.env.OIDC_CLIENT_ID!,
	clientSecret: process.env.OIDC_CLIENT_SECRET!,
	cookieSecret: process.env.OIDC_COOKIE_SECRET!,
	clientAuthMethod: 'client_secret_basic',
	loginPath: '/auth/login',
	redirectPath: '/auth/callback',
	scope: 'openid profile email offline_access',
	backChannelLogoutStore: 'memory',
	cookieOptions: {
		secure: process.env.NODE_ENV === 'production'
	}
});
// src/hooks.server.ts
import { oidc } from '$lib/server/auth';

export const handle = oidc.handle;
// src/routes/auth/login/+server.ts
import { oidc } from '$lib/server/auth';

export const GET = oidc.loginHandler();
// src/routes/auth/callback/+server.ts
import { oidc } from '$lib/server/auth';

export const GET = oidc.callbackHandler({
	redirectTo: '/'
});
// src/routes/auth/logout/+server.ts
import { oidc } from '$lib/server/auth';

export const POST = oidc.logoutHandler();
// src/routes/auth/backchannel-logout/+server.ts
import { oidc } from '$lib/server/auth';

export const POST = oidc.backChannelLogoutHandler();

Actions

If you prefer form actions instead of dedicated routes:

// src/routes/+page.server.ts
import { oidc } from '$lib/server/auth';

export const actions = oidc.createActions();

Load Session

// src/routes/+layout.server.ts
import { oidc } from '$lib/server/auth';

export async function load(event) {
	return {
		session: await oidc.getPublicSession(event),
		sessionManagement: await oidc.getSessionManagementConfig()
	};
}

Client Setup

<script lang="ts">
	import { OIDCContext } from '@sourceregistry/sveltekit-oidc';
	let { data } = $props();
</script>

<OIDCContext
	session={data.session}
	config={data.sessionManagement}
	logoutPath="/auth/logout"
	monitorSession={false}
	redirectIfUnauthenticated={false}
>
	<Account />
</OIDCContext>
<!-- src/lib/Account.svelte -->
<script lang="ts">
	import { useOIDC } from '@sourceregistry/sveltekit-oidc';

	const oidc = useOIDC();
</script>

{#if oidc.isAuthenticated}
	<p>Signed in as {oidc.user?.email ?? oidc.user?.name ?? oidc.session?.sub}</p>
	<form method="POST" action="/auth/logout">
		<button type="submit">Sign out</button>
	</form>
{:else}
	<a href="/auth/login?returnTo=%2Faccount">Sign in</a>
{/if}

OIDCContext handles:

  • local expiry redirects
  • check_session_iframe polling when the provider advertises it
  • periodic invalidateAll() revalidation so revoked server sessions are detected
  • a client context for nested auth-aware components through useOIDC() / getOIDCContext()

Set monitorSession={false} when you want to keep the client context but leave remote session revocation checks to your server-side guard or another mechanism. This disables check_session_iframe polling without changing the provider metadata exposed through the context.

Typed Custom Claims

createOIDC infers a TClaims type from whatever transformClaims / transformUser / transformSession return, and threads it through the session, event.locals.oidc, OIDCPublicSession, and the client context — no casts needed.

// src/lib/server/auth.ts
export const oidc = createOIDC({
	issuer: 'https://your-idp.example.com',
	clientId: process.env.OIDC_CLIENT_ID!,
	cookieSecret: process.env.OIDC_COOKIE_SECRET!,
	transformClaims: (claims) => ({
		...claims,
		roles: (claims.roles as string[]) ?? [],
		tenant: claims.tenant as string | undefined
	})
});

Wire App.Locals so event.locals.oidc is fully typed everywhere — OIDCLocals infers everything directly from the instance:

// src/app.d.ts
import type { OIDCLocals } from '@sourceregistry/sveltekit-oidc/server';
import { oidc } from '$lib/server/auth';

declare global {
	namespace App {
		interface Locals {
			oidc?: OIDCLocals<typeof oidc>;
		}
	}
}

export {};

Now event.locals.oidc?.claims?.roles is string[] and ?.tenant is string | undefined in every hook, load function, and action — no separate type aliases needed.

OIDCContext is a generic component, but Svelte doesn't support passing explicit type arguments in markup — TClaims is inferred from the session prop instead. Since data.session comes from oidc.getPublicSession(event), the type flows through automatically:

<!-- src/routes/+layout.svelte -->
<script lang="ts">
	import { OIDCContext } from '@sourceregistry/sveltekit-oidc';
	let { data, children } = $props();
</script>

<OIDCContext session={data.session} config={data.sessionManagement}>
	{@render children()}
</OIDCContext>
<!-- src/lib/Account.svelte -->
<script lang="ts">
	import { useOIDC } from '@sourceregistry/sveltekit-oidc';

	const auth = useOIDC(); // TClaims inferred from App.Locals.oidc
</script>

{#if auth.isAuthenticated}
	<p>Roles: {auth.claims?.roles.join(', ')}</p>
{/if}

If transformClaims (and friends) are omitted, TClaims defaults to OIDCUserClaims — existing setups keep working unchanged.

Typed Custom Session

Backend apps commonly stash application-specific data on the session itself (e.g. tenantId, permissions) — not just inside claims/user. A second generic, TSession, is inferred from transformSession's return type and threads through the session store, cookies, event.locals.oidc, and handleCallback's result — again with no casts:

// src/lib/server/auth.ts
import { createOIDC, type OIDCSession, type OIDCUserClaims } from '@sourceregistry/sveltekit-oidc/server';

type AppClaims = OIDCUserClaims & { tenant?: string };
type AppSession = OIDCSession<AppClaims> & {
	tenantId: string;
	permissions: string[];
};

export const oidc = createOIDC({
	issuer: 'https://your-idp.example.com',
	clientId: process.env.OIDC_CLIENT_ID!,
	cookieSecret: process.env.OIDC_COOKIE_SECRET!,
	transformClaims: (claims) => ({ ...claims, tenant: claims.tenant as string | undefined }),
	transformSession: (session): AppSession => ({
		...session,
		tenantId: session.claims?.tenant ?? 'default',
		permissions: session.user?.roles ?? []
	})
});

app.d.ts stays a one-liner — OIDCLocals picks up both TClaims and TSession from the instance:

// src/app.d.ts
import type { OIDCLocals } from '@sourceregistry/sveltekit-oidc/server';
import { oidc } from '$lib/server/auth';

declare global {
	namespace App {
		interface Locals {
			oidc?: OIDCLocals<typeof oidc>;
		}
	}
}

export {};

Now event.locals.oidc?.session?.tenantId is string and ?.permissions is string[] everywhere — and oidc.requireAuth(event) / handleCallback's onsuccess resolve to AppSession directly.

Use OIDCInferClaims / OIDCInferSession when you need the types explicitly in non-Svelte code (utility functions, API helpers, etc.):

import type { OIDCInferClaims, OIDCInferSession } from '@sourceregistry/sveltekit-oidc/server';
import type { oidc } from '$lib/server/auth';

type AppClaims = OIDCInferClaims<typeof oidc>;
type AppSession = OIDCInferSession<typeof oidc>;

If transformSession is omitted, TSession defaults to OIDCSession<TClaims> — existing setups keep working unchanged.

Example App

This repository now includes a runnable example under src/routes and src/hooks.server.ts.

Set these environment variables to enable it:

  • OIDC_ISSUER
  • OIDC_CLIENT_ID
  • OIDC_COOKIE_SECRET
  • optional: OIDC_CLIENT_SECRET
  • optional: OIDC_SCOPE
  • optional: OIDC_POST_LOGOUT_REDIRECT_URI

Notes

  • cookieSecret should be a strong random secret and must stay stable across instances.
  • Built-in returnTo and logout redirect values are restricted to same-origin paths. Use your own callback onsuccess logic if you need custom redirect policy.
  • clockSkewSeconds defaults to 30 and tolerates small clock drift between your app and the identity provider.
  • createInMemoryBackChannelLogoutStore() is suitable for local development or single-instance deployments. Use Redis, SQL, or another shared store for production.
  • The library validates id_token and logout_token values through @sourceregistry/node-jwt and provider JWKS metadata.
  • groups are normalized onto the session from groups and roles claims when present.
  • Use transformClaims, transformUser, and transformSession to project provider-specific claims into your own session shape.
  • check_session_iframe monitoring only runs when monitorSession is enabled, the provider advertises that endpoint, and the session includes session_state.
  • Refresh token handling is automatic when a valid refresh token is present.
  • event.locals.oidc is attached by the hook; wire it in app.d.ts with OIDCLocals<typeof oidc> — see Typed Custom Claims.