Securing SvelteKit Apps with Keycloak
Tags: authentication, sso, svelte, keycloak
Published: 2024-05-07 03:02:59

---
canonical_url: https://phasetwo.io/blog/instant-user-management-and-sso-for-sveltekit/
---

[Svelte](https://svelte.dev/) and specifically, [SvelteKit](https://kit.svelte.dev/) is an open source web framework that makes developing web applications easier.

In this article we'll be using [Keycloak](https://www.keycloak.org/) to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

{% cta https://phasetwo.io/ %} Phase Two {% endcta %} is a [Keycloak as a Service](https://phasetwo.io/) provider enabling SaaS builders to accelerate time-to-market with powerful enterprise features like SSO, identity, and user management features. Phase Two enhances Keycloak through a variety of open-source extentions for modern SaaS use cases. Phase Two supports both hosted and on-premise deployment options. 

_What is Keycloak?_

[Keycloak](https://www.keycloak.org/) has been a leader in the Identity and Access Management world since its launch almost 8 years ago. It is an open-source offering under the stewardship of [Red Hat](https://www.redhat.com/)

> **INFO**
If you just want to skip to the code, visit the Phase Two [SvelteKit example](https://github.com/p2-inc/examples/tree/main/frameworks/sveltekit). 

## TOC

* [Keycloak Setup](#keycloak-setup)
* [SvelteKit](#sveltekit)

---

## Setting up a Keycloak Instance <a name="keycloak-setup"></a>

> **TIP**
If you already have a functioning Keycloak instance, you can skip to the next section.

{% details **Keycloak Setup Details** %} 
Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a Phase Two [free Keycloak](https://phasetwo.io/) starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

- Visit the sign-up [page](https://phasetwo.io/dashboard/).
- Enter an email, use a Github account, or use an existing Google account to register.

![Register](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1jovhdjmmnys3kjp1wb.png)

- Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

![Email Link](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s73nhw4jl43li164nm5i.png)

- After creating an account, a [realm](https://www.youtube.com/watch?v=ZTFlc-3pG1M) is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.
- Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

  ![Create shared deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7oknshhzra67byi0uxoz.png)

- After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

![Deployments](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arwn5n54hbzwocobkmzs.png)

At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.
{% enddetails %}


## Setting up an OIDC Client

We need to create a [OpenID Connect](https://www.keycloak.org/docs/latest/server_admin/#con-oidc_server_administration_guide) Client in Keycloak for the app to communicate with. 

{% details **Details** %}

Keycloak's [docs](https://www.keycloak.org/docs/latest/server_admin/#_oidc_clients) provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

1.  Open the Admin UI by clicking **Open Console** in the Phase Two Dashboard.
1.  Click **Clients** in the menu.
1.  Click **Create client**.
1.  Leave **Client type** set to **OpenID Connect**.
1.  Enter a **Client ID**. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.
1.  Supply a **Name** for the client.
1.  Click **Next**.
![General settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vmlbu0gbf3wdmmacy23.png)
1.  Under the Capability Config section, leave the defaults as selected. This can be configured further later.
- Client authentication to Off.
- Authorization to Off.
- Standard flow checked. Direct access grants checked. All other items unchecked.
1.  Click **Next**.
![Capbility config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rv9fpa1jgny2300c6btd.png)
1.  Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example application:
**URI and Origin Details**
The choice of `localhost` is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    _Valid redirect URI_ (allows redirect back to application)
    ```
    http://localhost:3000/*
    ```
    _Web origins_ (allows for Token auth call)
    ```
    http://localhost:3000
    ```

1.  Click **Save**
![Login settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lk6j7sjbv2b5ns7jc85a.png)
{% enddetails %}

### OIDC Config

{% details **Details** %}

We will need values to configure our application. To get these values follow the instructions below.

1. Click **Clients** in the menu.
1. Find the Client you just created and click on it. In the top right click the **Action** dropdown and select **Download adapter config**.
1. Select **Keycloak OIDC JSON** in the format option. The details section will populate with the details we will need.
   - Note the `realm`, `auth-server-url`, and `resource` values.
![Adapter config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hjxmpf25hhyogdno2yht.png)

{% enddetails %}

## Adding a Non-Admin User

> **INFO**
It is bad practice to use your Admin user to sign in to an Application.

Since we do not want to use our Admin user for signing into the app we will build, we need to add another non-admin user.

{% details **Details** %}
1.  Open the Admin UI by clicking **Open Console** in the Phase Two Dashboard.
1.  Click **Users** in the menu.
1.  Click **Add user**.
1.  Fill out the information for Email, First name, and Last name. Click **Create**.
1.  We will now set the password for this user manually. Click **Credentials** (tab) and click **Set Password**. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
1.  Click **Save** and confirm the password by clicking **Save password**
{% enddetails %}

---

## Setting up a SvelteKit Project <a name="sveltekit"></a>

> **INFO**
We will use the Phase Two SvelteKit example code here, but the logic could easily be applied to any existing application.

1. Clone the Phase Two [example repo](https://github.com/p2-inc/examples/).
2. Open the SvelteKit [folder](https://github.com/p2-inc/examples/tree/main/frameworks/sveltekit) within `/frameworks/sveltekit`.
3. Run `npm install` and then `npm run dev`. This example leverages [@auth/sveltekit](https://github.com/nextauthjs/next-auth) to provide HOC support.
4. The project makes use of the following SvelteKit items: hooks, components, layout, and `@auth/sveltekit` module. We'll review each in kind.
5. We'll review where we configure out Keycloak instance. Open the `src/auth.ts` file. This is a server only file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code. While it is recommended to use Environment variables for the secret, for the purpose of this tutorial, paste in the **Client secret** from the OIDC client creation section for the value of `clientSecret`.
```tsx
// Use Environment Variables AUTH_SECRET in prod
const authjsSecret =
  "f18d48ce9bea32e44b5591b2c89185729d4559435f77ca76872a83a0850563a4";

const realm = "shared-deployment-001";

const kcConfig = {
  // Use Environment Variables AUTH_KEYCLOAK_ISSUER in prod
  issuer: `https://usw2.auth.ac/auth/realms/${realm}`,
  // Paste "Client id" here. Use Environment Variables AUTH_KEYCLOAK_ID in prod
  clientId: "reg-example-1",
  // Paste "Client secret" here. Use Environment Variables AUTH_KEYCLOAK_ISSUER in prod
  clientSecret: "CLIENT_SECRET",
};
```
Those are used to popluate the config for the provider `Keycloak`:
```tsx
export const { handle, signIn, signOut } = SvelteKitAuth({
  trustHost: true,
  secret: authjsSecret,
  providers: [Keycloak(kcConfig)],
});
```
6. Next, let's review the hooks (`src/hooks.server.ts`) file. This imports the `handle` method by the `src/auth.ts` and re-exports it. This allows the hooks to act as a middleware and include the authentication status of the user in each request.
```tsx
export { handle } from "./auth.js";
```
7. To retrieve the authentication status of the user in server-side rendering on the index route, it's using the `+layout.server.ts` file. It retrieves the authentication status via the `getSession` function from [SvelteKit Locals](https://kit.svelte.dev/docs/types#app-locals) set by `src/hook.server.ts`.
```tsx
import type { LayoutServerLoad } from "./$types.js";

export const load: LayoutServerLoad = async (event) => {
  const session = await event.locals.getSession();
  return {
    session,
  };
};
```
8. The server-side authentication data is then accessed inside the `+page.svelte`, and then passed onto the user (`src/components/user.svelte`) component as follows:
```html
<script lang="ts">
  import User from "$components/user.svelte";
  import type { LayoutServerData } from "./$types.js";

  export let data: LayoutServerData;
</script>

// ... Rest of the index route ... // <User data={{ user: data?.session?.user,
status: Boolean(data?.session), }} />
```
The props, **user** and **status** represent the user information object and whether the user is signed in, respectively.
9. At this point our entire application will be able to access all information and methods needed to perform authentication. View `src/components/user.svelte` for exactly how the code is authenticating your user. The sections rendering the **Log in** and **Log out** buttons are conditional areas based on the authenticated context. The buttons invoke server-side APIs provided by `@auth/sveltekit`.

The logic using the authenticator to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

10. Open [localhost:3000](http://localhost:3000). You will see the Phase Two example landing page. You current state should be **Not authenticated**. Click **Log In**. This will redirect you to your login page.

> **INFO**
Use the non-admin user created in the previous section to sign in.

11. Enter the credentials of the non-admin user you created. Click **Submit**. You will then be redirected to the application. The Phase Two example landing page now loads your **Authenticated** state, displaying your user's email and name.
12. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.


## Learning more

Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our [blog](https://phasetwo.io/blog) has many use cases from [customizing login pages](https://phasetwo.io/blog/customizing-login-pages), setting up [magic links](https://phasetwo.io/blog/set-up-magic-links) (password-less sign in), and [Organization](https://phasetwo.io/product/organizations) workflows.