Secure Svelte with Keycloak
Tags: svelte, sapper, keycloak, sso
Published: 2021-01-06 12:49:00

**This project is deprecated and no longer maintained.**

Hello, in this post I would like to show you my implementation of Keycloak JavaScript Adapter in Svelte App.

Feel free to use **Table Of Content**.

You can find the code on Github.
{% github matejbucek/Secure_Svelte_With_Keycloak no-readme %}

Let's start from scratch. First thing we need is the Keycloak server running. You can download the latest version [here] (https://www.keycloak.org/downloads), or you can use for example docker.

#####Table Of Contents
1. [Setting up Keycloak Realm and Client](#ch-1)
2. [Preparing new Sapper project](#ch-2)
3. [Creating the Auth and the User classes](#ch-3)
4. [Creating AuthGuard and RoleGuard components](#ch-4)
5. [Using our classes and components](#ch-5)
6. [Conclusion](#ch-6)

### 1. Setting up Keycloak Realm and Client <a name="ch-1"></a>

Now let's login to our _Administration console_ and create new _Realm_, _Client_ and some user and role for testing purposes.

Let's start with _Realm_.
![Creating new Realm](https://dev-to-uploads.s3.amazonaws.com/i/mdp2wzascubj3r0v7gfx.png)

![Setting Realm's name](https://dev-to-uploads.s3.amazonaws.com/i/ymzpirkdg9n1d2ya4iuo.png)

After we set up a _Realm_, we also have to create new _Client_, so we can then use this authorization server.
![Creating new Client](https://dev-to-uploads.s3.amazonaws.com/i/994q68kyuyha63gcp26y.png)

![Setting Client's name and Root URL](https://dev-to-uploads.s3.amazonaws.com/i/57pbgo3241w8eeeybhmw.png)


Now we will create new default role and new user.
![Creating new Role](https://dev-to-uploads.s3.amazonaws.com/i/03c2rmkrwitiu4509kcv.png)

![Making the Role default](https://dev-to-uploads.s3.amazonaws.com/i/pytn5y8lcl4q0jjtnf04.png)

![Creating new User](https://dev-to-uploads.s3.amazonaws.com/i/pj7mjgt6fkhmcul1151g.png)

Now we should be good to go to the next step.

### 2. Preparing new Sapper project <a name="ch-2"></a>

At first we have to create new project. I am going to use Sapper and TypeScript, but you can do the same thing with Svelte and JavaScript.

```bash
npx degit "sveltejs/sapper-template#rollup" my-app
```

Now let's convert our project to TypeScript.

```bash
node scripts/setupTypeScript.js
```

The last thing that is left to do is adding Keycloak JS script tag to our _template.html_ and then we can continue to the fun part.

To make our life easier our Keycloak server provides the right version of JS file for us so our tag should look like this.
```html
<script src="https://{your_server_url}/auth/js/keycloak.js"></script>
```

So our _template.html_ should look something like this.
```html
...
	<link rel="stylesheet" href="global.css">
	<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
	<link rel="icon" type="image/png" href="favicon.png">
	<script src="https://{your_server_url}/auth/js/keycloak.js"></script>

	<!-- Sapper creates a <script> tag containing `src/client.js`
	     and anything else it needs to hydrate the app and
	     initialise the router -->
	%sapper.scripts%
...
```

### 3. Creating the Auth and the User classes <a name="ch-3"></a>

The Auth class will handle most of the stuff. The User class is just so we can easily keep track of actual user.

I am using the default flow, which is the Authorization Flow. But you can obviously change that to e. g. Implicit Flow.

You can find more [here in the official documentation](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter).


_Auth.class.ts - whole code on Github_ 
```typescript
import { writable } from 'svelte/store';
import { User } from "./User.class";

//Template for loacl storage mapping
export type localStorageMapping = {"access_token": string, "refresh_token": string, "exp": string};

export class Auth {
    //The actual keycloak connector
    private keycloak: any;
    //Used mapping
    private localStorageMapping: localStorageMapping;
    //This keeps track whether Auth and Role guards can call buildUser method 
    private initialized: any;

    //This class builds the actual User from access token
    public buildUser(): User {
        let parsed = this.keycloak.tokenParsed;
        if(!parsed){
            return null;
        }
        //If you also want the resource roles, just concat them here
        return new User(parsed["sub"], parsed["preferred_username"], parsed["given_name"], parsed["family_name"], parsed["realm_access"]["roles"]);
    };

    public constructor(config: {}, localStorageMapping?: localStorageMapping) {
        //Keycloak class is not defined, because we add that library into the template.html
        //@ts-ignore
        this.keycloak = new Keycloak(config);

        this.initialized = writable(false);

        if(localStorageMapping){
            this.localStorageMapping = localStorageMapping;
        }else{
            this.localStorageMapping = {
                "access_token": "access_token",
                "refresh_token": "refresh_token",
                "exp": "exp"
            };
        }

        //Check, if user is authenticated
        if (localStorage.getItem(this.localStorageMapping.access_token) !== null) {
                this.refresh();
        }
    }

    public isInitialized(): any{
        return this.initialized;
    }

    //Makes the initialization process with given parameters
    private init(initParams: {}) {
        this.keycloak
        .init(initParams)
        .then((authenticated) => {
            if (authenticated) {
                localStorage.setItem(
                    this.localStorageMapping.access_token,
                    this.keycloak.token
                );
                localStorage.setItem(
                    this.localStorageMapping.refresh_token,
                    this.keycloak.refreshToken
                );
                localStorage.setItem(this.localStorageMapping.exp, this.keycloak.tokenParsed["exp"]);
                //Setting the update (refresh) of our token
                this.keycloak.updateToken(5).then((refreshed) => {
                    if (refreshed) {
                        localStorage.setItem(
                            this.localStorageMapping.access_token,
                            this.keycloak.token
                        );
                        localStorage.setItem(
                            this.localStorageMapping.refresh_token,
                            this.keycloak.refreshToken
                        );
                        localStorage.setItem(this.localStorageMapping.exp, this.keycloak.tokenParsed["exp"]);
                    }
                });
            }
            this.initialized.set(true);
        })
        .catch(function (e) {
            console.error(e);
        });
        this.keycloak.onTokenExpired = () => {
            //Setting the update (refresh) of our token
            this.keycloak.updateToken(5).then((refreshed) => {
                if (refreshed) {
                    localStorage.setItem(
                        this.localStorageMapping.access_token,
                        this.keycloak.token
                    );
                    localStorage.setItem(
                        this.localStorageMapping.refresh_token,
                        this.keycloak.refreshToken
                    );
                    localStorage.setItem(this.localStorageMapping.exp, this.keycloak.tokenParsed["exp"]);
                }
            });
        };
    }

    //This builds initial parameters and add the access token and refresh token. 
    //You can also use the check-sso. More in official docs.
    private buildInitParams(onLoad: string = "login-required", silentCheckSsoRedirectUri?: string): any {
        return {
            onLoad,
            token: localStorage.getItem(this.localStorageMapping.access_token),
            refreshToken: localStorage.getItem(this.localStorageMapping.refresh_token),
            silentCheckSsoRedirectUri
        };
    }

    public login() {
        this.init(this.buildInitParams());
    }

    public refresh() {
        this.init(this.buildInitParams());
    }

    public logout() {
        localStorage.removeItem(this.localStorageMapping.access_token);
        localStorage.removeItem(this.localStorageMapping.refresh_token);
        localStorage.removeItem(this.localStorageMapping.exp);
        this.keycloak.logout();
    }

    //Checks whether there is the back redirect from auth server 
    public checkParams(){
        let params = (new URL(document.location.href.replace("#", "?"))).searchParams;
        if(params.get("state") && params.get("session_state") && params.get("code")){
            this.init(this.buildInitParams());
        }
    }
}
```

_User.class.ts - whole code on Github_
```typescript
export class User{
    //The sub parameter of access token
    private userId: string;
    private username: string;
    private firstname: string;
    private lastname: string;
    private roles: Array<string>;

    public constructor(userId: string, username: string, firstname: string, lastname: string, roles: Array<string>){
        this.userId = userId;
        this.username = username;
        this.firstname = firstname;
        this.lastname = lastname;
        this.roles = roles;
    }
    //Checks whether user has all of the roles
    public hasRole(role: string | Array<string>): boolean{
        if(role instanceof Array){
            let contains = true;
            role.forEach((r) => {
                contains = contains && this.roles.includes(r);
            });
            return contains;
        }
        return this.roles.includes(role);
    }

//getters...
}
```

### 4. Creating AuthGuard and RoleGuard components <a name="ch-4"></a>

Now we will take a look at how we would actually check, whether the user is authenticated or not.

Let's define Svelte component for that purpose.
```html
<script>
    import { onMount } from 'svelte';
    import { goto } from '@sapper/app';
    import { authStore } from './stores';
    let auth;
    let unsub;
    let initialized;
    $: if(auth) {
        auth.initialized.subscribe(i => {
            initialized = i;
        });
    };
    $: user = (initialized) ? auth.buildUser() : null;
    let forceLogin = false;
    let manual = false;
    export {
        forceLogin,
        manual
    }
    onMount(() => {
        unsub = authStore.subscribe(value => {
            auth = value;
        });
        if(forceLogin && user === null){
            goto("/login");
        }
    });
</script>

{#if user && manual}
    <slot name="authed"></slot>
{:else if !user && manual}
    <slot name="not_authed"></slot>
{:else if user && !manual}
    <slot></slot>
{/if}
```

Don't worry about that **stores.js**, we will define it later.

So as you can see it is really simple. You can specify whether you want user to be redirected to login page, if user is not logged in. It also allows you to manually specify which of your tags should be displayed when user is authenticated and when he isn't.

Let me show you little example:
```html
<AuthGuard>
    <h1>This will be showed to authenticated user.</h1>
</AuthGuard>

<AuthGuard forceLogin=true>
    <h1>This will force user to login.</h1>
</AuthGuard>

<AuthGuard manual=true>
    <h1 slot="authed">This will be showed to authenticated user.</h1>
    <h1 slot="not_authed">This will be showed to not authenticated user.</h1>
</AuthGuard>
```

Now let's take look on RoleGuard. This component will help you check whether user has right roles.
```html
<script>
    import { onMount } from "svelte";
    import { authStore } from './stores';
    let auth;
    let unsub;

    let initialized;
    $: if(auth) {
        auth.initialized.subscribe(i => {
            initialized = i;
        });
    };
    $: user = (initialized) ? auth.buildUser() : null;
    let roles;
    let actualRoles = roles.split(",");
    let manual = false;
    export {
        roles,
        manual
    }

    onMount(() => {
        unsub = authStore.subscribe((value) => {
            auth = value;
        });
    });
</script>
{#if user}
    {#if user.hasRole(actualRoles) && manual}
        <slot name="role"></slot>
    {:else if !user.hasRole(actualRoles) && manual}
        <slot name="no_role"></slot>
    {:else if user.hasRole(actualRoles) && !manual}
        <slot></slot>
    {/if}
{/if}
```

Let's take look on another example:

```html

<RoleGuard roles=user>
    <h2>You have user role!</h2>
</RoleGuard>

<RoleGuard roles=user,admin>
    <h2>You have user and admin roles!</h2>
</RoleGuard>

<RoleGuard manual=true roles=user>
    <h2 slot=role>You have user role!</h2>
    <h2 slot=no_role>You don't have user role!</h2>
</RoleGuard>
```

Now we have our two main components. In next section we will take look on **stores.js** and setting up our Keycloak JS adapter.

### 5. Using our classes and components <a name="ch-5"></a>

At this point we have all components, Auth class and User class. What is left thou is **stores.js** and _layout.svelte. So let's take look at them now.

_stores.js_
```javascript
import { writable } from 'svelte/store';

export const authStore = writable(null);
```
This store allow us to share one Auth instance between all of our components.

_&#x5f;layout.svelte_
```html
<script lang="ts">
	import { onMount } from "svelte";
	import { Auth } from "../components/Auth.class";
	import { authStore } from "../components/stores";
	import Nav from "../components/Nav.svelte";
	onMount(() => {
		authStore.set(
			new Auth({
				realm: "{realm_name}",
				"auth-server-url": "{your_server_url/auth}",
				"ssl-required": "external",
				resource: "{resource}",
				clientId: "{client_id}",
				"public-client": true,
				"confidential-port": 0,
			})
		);
	});
	export let segment: string;
</script>

<style>
	main {
		position: relative;
		max-width: 56em;
		background-color: white;
		padding: 2em;
		margin: 0 auto;
		box-sizing: border-box;
	}
</style>

<Nav {segment} />

<main>
	<slot />
</main>
```

Here's what it all connects together. Layout allows us to define one Auth instance and distribute it through our other components. We can now specify our realm and client we'v created at beginning. 

If you want to know other attributes you can specify take look at official documentation.

Now let me show you example of a login page.

```html
<script>
    import { onMount } from 'svelte';
    import { authStore } from '../components/stores';
    import AuthGuard from '../components/AuthGuard.svelte';
    let unsub;
    let auth;
    onMount(() => {
        unsub = authStore.subscribe(
            (a) => {
                auth = a;
            }
        );
    });
    $: if(auth){ 
        auth.checkParams();
    };
    function login(){
        if(auth){
            auth.login();
        }
    }

    function logout(){
        if(auth){
            auth.logout();
        }
    }
</script>

{#if auth}
    <AuthGuard manual="true">
        <button slot="not_authed" on:click={login}>Login</button>
        <button slot="authed" on:click={logout}>Logout</button>
    </AuthGuard>
{/if}
```

It's pretty simple, just calling methods we'v defined in Auth class.

After successful login you can try to go to developer tools, take your access token and paste it to [JWT.IO](https://jwt.io), where you can then see your token parsed.

If you want to see whole implementation [take look at my Github](https://github.com/matejbucek/Secure_Svelte_With_Keycloak).

### 6. Conclusion <a name="ch-6"></a>

In this post we'v taken a look at simple way of securing Svelte/Sapper applications. This is my first post here so I hope it was at least helpful.

Thank you very much for reading. Feel free to ask me any question or give me some suggestion.

#### Resources
* [Keycloak Docs](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter)