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

sveltekit-fireauth

v0.1.0

Published

A SvelteKit library for seamless server-side authentication using Firebase Authentication.

Downloads

3

Readme

sveltekit-fireauth

A SvelteKit library for seamless server-side authentication using Firebase Authentication.

Partially inspired by next-firebase-auth, check out its discussion of when (not) to use this package.

Quick start

This guide will assume your app uses typescript

  1. Install this package with peer dependencies
 npm i sveltekit-fireauth firebase firebase-admin
  1. Set up the required types in app.d.ts
import type { FirebaseAuth } from 'sveltekit-fireauth/server'

declare global {
  namespace App {
    interface Locals {
      auth: FirebaseAuth
    }
  }
}

export {}
  1. Set up a handle hook inside hooks.server.ts
import type { Handle } from '@sveltejs/kit'
import { createAuthHandle } from 'sveltekit-fireauth/server'
import { FIREBASE_SERVICE_ACCOUNT_KEY } from '$env/static/private'

export const handle: Handle = createAuthHandle({
  // Your web app's Firebase configuration
  firebaseConfig: {
    apiKey: '',
    authDomain: '',
    projectId: '',
    storageBucket: '',
    messagingSenderId: '',
    appId: '',
  },
  // Optional. Just set the FIREBASE_SERVICE_ACCOUNT_KEY environment variable and the library will pick it up
  serviceAccountKey: FIREBASE_SERVICE_ACCOUNT_KEY,
  // Optional. Refresh token cookie expire time, default 30 days
  refreshExpireTime: 60 * 60 * 24 * 30,
})

You may want to use the sequence helper function to set up multiple handle hooks, especially if you are going to use this library's other handle hooks.

  1. Set up some form actions to log in, sign up and log out a user.
import { loginWithCredentials, signupWithCredentials, signOut } from 'sveltekit-fireauth/server'

export const actions = {
  login: async (event) => {
    // Get the email and password from the form
    try {
      await loginWithCredentials({ event, email, password })
    } catch (e) {
      throw error(401, { message: 'Unauthorized' })
    }
    throw redirect(303, '/protected')
  },
  signup: async (event) => {
    // Get the email and password from the form
    try {
      await signupWithCredentials({ event, email, password })
    } catch (e) {
      throw error(401, { message: 'Account cannot be created' })
    }
    throw redirect(303, '/protected')
  },
  logout: async ({ cookies }) => {
    return signOut({ cookies, redirectRoute: '/login' })
  },
}

If you prefer so, this could be done using request handlers as well.

  1. Pass the user's session to the client-side
// +layout.server.ts
import { verifySession } from 'sveltekit-fireauth/server'

export const load = (event) => ({
  session: verifySession(event),
})
<!-- +layout.svelte -->
<script lang="ts">
  export let data

  $: ({ session } = data)
</script>

<p>Logged in as user: {session.uid}</p>

Recipes

Protecting Pages

You can protect a page using the onlyAuthenticatedLoad function inside any page's +page.server.ts file. For example:

// /protected/+page.server.ts
import { onlyAuthenticatedLoad } from 'sveltekit-fireauth/server'

export const load = onlyAuthenticatedLoad({
  redirectRoute: '/login',
  load: () => {
    // regular load function here
  },
})

Passing a load function is optional, in case a particular page does not load anything from the server.

// /protected/+page.server.ts
import { onlyAuthenticatedLoad } from 'sveltekit-fireauth/server'

export const load = onlyAuthenticatedLoad({
  redirectRoute: '/login',
})

This approach gives you the flexibility to handle authentication on a per page basis and without introducing waterfalls. This also seems to be the recommended approach to handle authentication in SvelteKit, at least as a TLDR from this discussion.

However, I'm aware this approach also introduces some overhead, having to call onlyAuthenticatedLoad on every page you want to protect. So this library also includes a createProtectedRoutesHandle for you to use in your hooks.server.ts.

import { createAuthHandle } from 'sveltekit-fireauth/server'

const protectedRoutesHandle: Handle = createProtectedRoutesHandle({
  baseRoute: '/protected', // the group of routes you want to protect
  redirectRoute: '/login',
})

export const handle = sequence(/* ... */)

There also exists onlyPublicLoad and createPublicRoutesHandle functions for you to keep your authenticated users out of your login page (or any page you want).

Syncing authentication state with the client

If you're using the Firebase SDK on the client and have security rules for Firestore or Storage you will need to sync the server-side session with the client.

  1. In a +layout.server.ts file load the session and auth config object. This object is the Firebase SDK config object. If you're creating your own Firebase SDK client you don't need to load auth config here
// +layout.server.ts
import { verifySession } from 'sveltekit-fireauth/server'

export const load = (event) => ({
  authConfig: event.locals.auth.config,
  session: verifySession(event),
})
  1. In the +layout.ts file get the session that was loaded from the server and create the Firebase Auth client using the auth config. If you're creating your own Firebase SDK client you don't need to create the Firebase Auth client here.
// +layout.ts
import { createFirebaseAuth } from 'sveltekit-fireauth/client'

export const load = ({ data }) => ({
  auth: createFirebaseAuth(data.authConfig),
  session: data.session,
})
  1. In the +layout.svelte file use the syncAuthState function inside an onMount callback.
<!-- +layout.svelte -->
<script lang="ts">
  import { onMount } from 'svelte'
  import { syncAuthState } from 'sveltekit-fireauth/client'

  export let data

  $: ({ session, auth } = data)

  onMount(() => {
    const unsubscribe = syncAuthState(auth, session)
    return () => {
      unsubscribe()
    }
  })
</script>

OAuth

It's possible to use OAuth with this library, however it requires a bit more setup, specially if you're using form actions. You may want to use an API route instead.

Assuming you're using an API route, here's what you need:

  1. Setup the API route. This is going to receive an ID token and use the loginWithIdToken helper to log in the user.
// /login/oauth/+server.ts
import { error, json } from '@sveltejs/kit'
import { loginWithIdToken } from 'sveltekit-fireauth/server'

export const POST = async (event) => {
  const { token } = await event.request.json()
  if (!token) {
    throw error(400, { message: 'Missing token' })
  }
  try {
    await loginWithIdToken({ event, token })
  } catch (e) {
    throw error(401, { message: 'Unauthorized' })
  }
  return json({ success: true })
}
  1. Next setup your login button with an on click handler. We'll use Google sign in for this example. Notice how in this case we sign in on the client first to get the ID token and then on the server with that token.
<script lang="ts">
  import { invalidateAll } from '$app/navigation'
  import { GoogleAuthProvider, signInWithPopup, signOut } from 'firebase/auth'

  export let data

  const googleLogin = async () => {
    const provider = new GoogleAuthProvider()
    const { user } = await signInWithPopup(data.auth, provider)
    const token = user.getIdToken()
    const response = await fetch('/login/oauth', {
      method: 'POST',
      body: JSON.stringify({ token }),
    })
    if (!response.ok) {
      // handle login failure
      await signOut(data.auth)
    }
    await invalidateAll()
  }
</script>

<button on:click={googleLogin}>Sign in with Google</button>