Adding Authentication to a Qwik App

Gil Fink
4 min readFeb 28, 2023

--

Authentication is a basic feature that is implemented in most of the apps and websites. Today there are many ways to authenticate users and the most popular front-end library for authentication is probably auth.js.
But how can you use the library in your Qwik app?

This post will explain how to integrate qwik-auth package into your Qwik app.

Note: The qwik-auth package is still experimental and some of the shown code might change in the future.

Setting the qwik-auth Package

The first thing to do when you want to use qwik-auth is to install it. Since auth.js is a peer dependency, you will have to install it as well. Run the following command to install the libraries:

pnpm i "@builder.io/qwik-auth" "@auth/core"

Once the installation finishes, you will need to update the vite.config.js and include the @auth/core library to the optimizeDeps config:

import { defineConfig } from 'vite';
import { qwikVite } from '@builder.io/qwik/optimizer';
import { qwikCity } from '@builder.io/qwik-city/vite';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig(() => {
return {
optimizeDeps: {
include: ['@auth/core'],
},
plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
preview: {
headers: {
'Cache-Control': 'public, max-age=600',
},
},
};
});

Note: This is a workaround to a bug that currently exists in qwik-auth which is cuasing this error: SyntaxError: The requested module '/node_modules/.pnpm/cookie@0.5.0/node_modules/cookie/index.js?v=849ea94b' does not provide an export named 'parse'

Once you updated the vite.config.js you will have to add a secret string for your app. The place to put it is in the app .env file. In my demo app I called the variable VITE_AUTH_SECRET, but you can put any name that you like as long as you will use that name in the relevant place.

Generating the qwik-auth Actions and Loaders

Everything is set up and now you can go and use qwik-auth in your app. qwik-auth exposes a serverAuth$ generator function that will generate all the actions and loaders you need for login, logout or getting the current session. Once you give it a callback that returns a configuration object with all the authentication providers, it will generate the following:

  • useAuthSignin — action that can be used to run the login process
  • useAuthSignout — action that can be used to run the logout process
  • useAuthSession — loader to get the current session

You can use any of the authentication providers that auth.js exposes. The following code shows you how I used the credentials provider:

import type { Provider } from '@auth/core/providers';
import CredentialsProvider from '@auth/core/providers/credentials';
import { serverAuth$ } from '@builder.io/qwik-auth';
import { users } from '~/data/users';

export const { useAuthSignin, useAuthSignout, useAuthSession, onRequest } = serverAuth$(
() => {
return ({
secret: import.meta.env.VITE_AUTH_SECRET,
trustHost: true,
providers: [
CredentialsProvider({
name: 'Login',
authorize: (credentials) => {
if (credentials) {
const user = users.get(credentials.username as string);
if (user) {
if (user.password === credentials.password) {
return { id: user.username, name: user.username, email: `${@gmail.com">user.username}@gmail.com`};
}
}
}
return null;
},
credentials: {
username: { label: 'Username', type: 'text' },
password: { label: 'Password', type: 'password' },
},
}),
] as Provider[],
})});

In the credentials provider, I plug into the authorize function and check the existence of a user inside an in-memory map object. In real-world, you will check that against a database but I wanted a simplified example.

Pay attention that the place to put the code I showed is inside the app routes folder.

Using The Generated qwik-auth Actions and Loaders

Once you generated the qwik-auth actions and loaders instances, you can use them in your app. To run the login process, you will first have to run the useAuthSignin action:

const loginAction = useAuthSignin();

Then you can wire it to a trigger button inside a Form element:

<Form action={loginAction}>
<div class="container">
<input
type="hidden"
name="provider"
value="credentials"
/>
<div class="login">
<button>Login</button>
</div>
</div>
</Form>

Pay attention to the hidden input with the provider name. This input holds the requested provider and you need to put it inside the form.

To run the logout process you will do the same as with the login but this time with the useAuthSignout action. If you want to use the current session you will use the useAuthSession loader and in it’s value property you will find the current user.
In my example, I used the session in the main layout and passed a loggedIn Boolean to the header component, which is later used to show/hide the logout button:

export default component$(() => {
const serverTime = useServerTimeLoader();
const userSignal = useAuthSession();

return (
<>
<main>
<Header loggedIn={userSignal.value?.user !== undefined} />
<section>
<Slot />
</section>
</main>
<footer>
<a href="https://www.builder.io/" target="_blank">
Made with ♡ by Builder.io
<div>{serverTime.value.date}</div>
</a>
</footer>
</>
);
});

If you want to look at the full example, you can find it online.

Summary

In the post I showed how you can install and then use the qwik-auth package. This package is part of Qwik and is currently experimenatal. There is no documentation for the package yet, so I hope that this post will help you get started with it if you want to use it today.

--

--

Gil Fink

Hardcore web developer, @sparXys CEO, Google Web Technologies GDE, Pro SPA Development co-author, husband, dad and a geek.