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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@jimallanson/sveltekit-oidc

v0.0.9

Published

This project aims to integrate OpenID confidential flow using Sveltekit. Once login is complete, Navigation to protected pages of app don't require a request to Authorization Server (Keycloak in our instance). Sveltekit hooks take care of :

Downloads

3

Readme

sveltekit + keycloak OpenID connect

This project aims to integrate OpenID confidential flow using Sveltekit. Once login is complete, Navigation to protected pages of app don't require a request to Authorization Server (Keycloak in our instance). Sveltekit hooks take care of :

* Silent Refresh Workflow
* Validating the client access_token validity
* Renewing the token in case of token expiry 
* Offline Auth server error handling
* Setting valid user information ( access_token, refresh_token, userid etc. ) in form of cookies
* Populating session variable with user information
* If Auth server has redirected to application, creates a backchannel machine to machine request for exchanging auth_code with access_token.

When the client side kicks in, it:

* Checks for user and Auth server information in session variable
* In case, no user is found or some error has occured on server-side, populate AuthStore with proper messages
* Provides Login, Logout functionality
* Initiates a front channel Authorization flow, in case of protected component via Sveletkit Load method.
* Logout in one browser tab initiates automatic logout from all tabs.
* Prompt on all browser tabs and Page reloading on User Login.

Complete JWT Implementation based on Hasura Blog on BEST Practices for JWT AUTH

Npm Package link

https://www.npmjs.com/package/sveltekit-oidc

Usage

Configuration

Create an .env file in project root with following content

VITE_OIDC_ISSUER="http://localhost:28080/auth/realms/hasura"
VITE_OIDC_CLIENT_ID="hasura-app"
VITE_OIDC_CLIENT_SECRET="1439e34f-343e-4f71-bbc7-cc602dced84a"
VITE_OIDC_REDIRECT_URI="http://localhost:3000"
VITE_OIDC_POST_LOGOUT_REDIRECT_URI="http://localhost:3000"
VITE_OIDC_CLIENT_SCOPE="openid profile email hasura-claims"
VITE_OIDC_TOKEN_REFRESH_MAX_RETRIES="5"
VITE_REFRESH_TOKEN_ENDPOINT="/auth/refresh_token"
VITE_REFRESH_PAGE_ON_SESSION_TIMEOUT=true

Inside your src/global.d.ts

interface ImportMetaEnv {
    VITE_OIDC_ISSUER: string;
    VITE_OIDC_CLIENT_ID: string;
    VITE_OIDC_CLIENT_SECRET: string;
    VITE_OIDC_REDIRECT_URI: string;
    VITE_OIDC_POST_LOGOUT_REDIRECT_URI: string;
    VITE_OIDC_CLIENT_SCOPE: string;
    VITE_OIDC_TOKEN_REFRESH_MAX_RETRIES: number;
    VITE_REFRESH_TOKEN_ENDPOINT: string;
    VITE_REFRESH_PAGE_ON_SESSION_TIMEOUT: boolean;
}

REFESH_TOKEN_ENDPOINT

Create a refresh_token endpoint as set in .env file (VITE_REFRESH_TOKEN_ENDPOINT) we have set /auth/refresh_token. As such, create file src/routes/auth/refresh_token.ts

import { renewOIDCToken } from 'sveltekit-oidc';

import type { Locals } from 'sveltekit-oidc/types';
import type { RequestHandler } from '@sveltejs/kit';

const oidcBaseUrl = `${import.meta.env.VITE_OIDC_ISSUER}/protocol/openid-connect`;
const clientId = `${import.meta.env.VITE_OIDC_CLIENT_ID}`;
const clientSecret = process.env.VITE_OIDC_CLIENT_SECRET || import.meta.env.VITE_OIDC_CLIENT_SECRET;
/**
 * @type {import('@sveltejs/kit').RequestHandler}
 */
 export const post: RequestHandler<Locals, FormData> = async (request) => {

	const data = await renewOIDCToken(request.body.get('refresh_token'), oidcBaseUrl, clientId, clientSecret);

    const response = {
		body: {
            ...data
        }
	};

    return response;

}

Inside your src/hooks.ts

import type { Handle, GetSession } from '@sveltejs/kit';
import { 
    userDetailsGenerator,
    getUserSession
} from 'sveltekit-oidc';
import type { Locals } from 'sveltekit-oidc/types';

import type { ServerRequest } from '@sveltejs/kit/types/hooks';

const clientSecret = process.env.VITE_OIDC_CLIENT_SECRET || import.meta.env.VITE_OIDC_CLIENT_SECRET;

export const handle: Handle<Locals>  = async ({ request, resolve }) => {
	// Initialization part
	const userGen = userDetailsGenerator(request, clientSecret);
	const { value, done } = await userGen.next();
	if ( done ) {
		const response = value;
		return response;
	}
	
	// Set Cookie attributes
	request.locals.cookieAttributes = 'Path=/; HttpOnly; SameSite=Lax;';

	// Your code here -----------
	if (request.query.has('_method')) {
		request.method = request.query.get('_method').toUpperCase();
	}
	// Handle resolve
	const response = await resolve(request);


	// After your code ends, Populate response headers with Auth Info
	// wrap up response by over-riding headers and status
    if ( response?.status !== 404 ) {
		const extraResponse = (await userGen.next(request)).value;
		const { Location, ...restHeaders } = extraResponse.headers;
		// SSR Redirection
		if ( extraResponse.status === 302 && Location ) {
			response.status = extraResponse.status
			response.headers['Location'] = Location;
		}
		response.headers = {...response.headers, ...restHeaders};

	}
	// Return response back
	return response;
};


/** @type {import('@sveltejs/kit').GetSession} */
export const getSession: GetSession = async (request: ServerRequest<Locals>) => {
	const userSession = await getUserSession(request, clientSecret);	
	return userSession;
}

Inside your src/routes/__layout.svelte component

<script lang="ts">
    import { Keycloak } from 'sveltekit-oidc';
</script>


<Keycloak
    issuer={import.meta.env.VITE_OIDC_ISSUER}
    client_id={import.meta.env.VITE_OIDC_CLIENT_ID}
    scope={import.meta.env.VITE_OIDC_CLIENT_SCOPE}
    redirect_uri={import.meta.env.VITE_OIDC_REDIRECT_URI}
    post_logout_redirect_uri={import.meta.env.VITE_OIDC_POST_LOGOUT_REDIRECT_URI}
    refresh_token_endpoint={import.meta.env.VITE_REFRESH_TOKEN_ENDPOINT}
    refresh_page_on_session_timeout={import.meta.env.VITE_REFRESH_PAGE_ON_SESSION_TIMEOUT}
    >
    <slot></slot>
</Keycloak>

Use these stores for auth information

<script lang="ts">
    import { isAuthenticated, isLoading, authError, accessToken, idToken, userInfo, refreshToken, LoginButton } from 'sveltekit-oidc';
</script>

{#if $isAuthenticated}
    <div>User is authenticated</div>
{:else}
    <LoginButton class="btn btn-primary">Login</LoginButton>
{/if}
<div>

For protected routes

<script lang="ts">
    import { KeycloakProtectedRoute, LogoutButton } from 'sveltekit-oidc';
</script>

<KeycloakProtectedRoute>
    <div class="h-screen-minus-navbar bg-gray-800 text-white flex flex-col justify-center items-center w-full">

        This is a protected page

        <LogoutButton class="btn btn-primary">Logout</LogoutButton>
    </div>
</KeycloakProtectedRoute>

Application Screenshots

Login / Index page

Login Page

Once user clicks login, Redirection to Auth server

Keycloak Auth

Auth Complete with backchannel token exchange and client hydrated with access_token

Index page with JWT

Developing

Once you've created a project and installed dependencies with npm install (or pnpm install or yarn), start a development server:

npm run dev

# or start the server and open the app in a new browser tab
npm run dev -- --open

Building

npm run build