Svelte + Auth0 step by step
Tags: svelte, auth0, webdev, auth
Published: 2023-01-07 23:50:54

In this post, I run through, step by step, how I went about getting authentication into a Svelte application using [Auth0](https://a0.to/signup-for-auth0). If you just want to see the full sample code for this can be found on [GitHub](https://github.com/KleeUT/Svelte_auth0_2022).

Svelte is an amazing tool to use when creating interactive web apps. Pretty much any interactive web app needs to have Authentication baked in. Authentication is one of those things that you shouldn't be building for yourself. Auth0 provides a low-effort way to authenticate your users. 

## Get set up

### Create an Auth0 account

For this post, I'm going to assume that you've already got an account on Auth0. If you don't it's as easy as heading to [Auth0](https://a0.to/signup-for-auth0) and signing up for a free account. 

### Start a Svelte app

You could build out a Svelte app from the ground up, picking your bundler and application structure, or you can use a pre-built scaffold. For this post, I'll use a scaffold to keep things short. 

Run `npm create vite@latest svelte-auth --templte svelte-ts`

Navigate into the cloned directory and install the dependencies by running `npm install`. 

Start the application locally by running `npm run dev` this will start a server on port 5173. You can open the app by going to [http://localhost:5173/](http://localhost:5173/).

Now that you've got a sample Svelte app running let's look at how we can add Authentication using Auth0. 

## Adding Authentication

### Configure Auth0

Log into the [Auth0 console](https://manage.auth0.com). If you've just signed up or created a new tenant there will be an app called `Default App` that you can use, or you can create a new application. 

Into this application, you're going to need to set the Allowed Callback URLs to `http://localhost:5173/` and the Allowed Logout URLs to `http://localhost:5173/`. Note that the URL ends in `/`. Then scroll to the bottom and save the changes. 

To configure the library you're going to need some values from Auth0. From the console take note of the Domain and Client ID values. 

![Auth0 console showing the Domain and Client ID fields](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3j4xn5rzflweqdsqsf7v.png)

### Add log in

Auth0 provides a library to make it easier to add Authentication to JavaScript applications. Add the library using `npm install @auth0/auth0-spa-js`. 

Create a new file to be responsible for Authentication called `auth.ts`. To start with export a function called `withAuth` that returns an object with a login method.  

Start by implementing the login function. 
Create an Auth0 client using the values that you copied out of the Auth0 console.
You'll also need to tell the client where to redirect the user back to after login. Given that this is a single-page app we'll direct them back to `window.location.origin`.

Then use the client to redirect the user to Auth0 to log in. 

```ts
async function login() {
  const client = await createAuth0Client({
    domain: "<your domain>", // e.g. "klee-test.au.auth0.com"
    clientId: "<your client id>", // e.g. "GGOFsf1eiSGvYOBkeDHAAJopE5qRpzN7"
    authorizationParams: {
      redirect_uri: window.location.href,
    },
  });
  client.loginWithRedirect();
}

export function withAuth(): {
  login: () => Promise<void>;
} {
 return { login }
}
```

To test this out wire it into the view in `App.svelte`

```tsx
<script lang="ts">
  import { withAuth } from './auth';

  const auth = withAuth();
</script>
<main>
  <button on:click={auth.login}>Login</button>
</main>
```

Now when someone clicks on the button they'll be redirected to Auth0 and asked to log in. When they have they'll be redirected back to your app.

### Add log out

Let's follow that up by adding logout functionality. 
Go back to the `auth.ts` file and add a logout method. You'll need an Auth0 client with the same configuration you used for login and need to call the `logout` method. 

```
async function login() { ... }

async function logout() {
  const client = await createAuth0Client({
    domain: "<your domain>", // e.g. "klee-test.au.auth0.com"
    clientId: "<your client id>", // e.g. "GGOFsf1eiSGvYOBkeDHAAJopE5qRpzN7"
    authorizationParams: {
      redirect_uri: window.location.href,
    },
  });
  client.logout();
}

export function withAuth(): {
  login: () => Promise<void>;
  logout: () => Promise<void>;
} {
 return { login, logout }
}
```
Wire this into the `App.svelte` file.

```tsx
<script lang="ts">
  import { withAuth } from './auth';

  const auth = withAuth();
</script>
<main>
  <button on:click={auth.login}>Login</button>
  <button on:click={auth.logout}>Logout</button>
</main>
```

Now when the user clicks on Logout they'll be taken back to Auth0, their session will be cleared and they'll be redirected back to your app. 

This is great, the user can log in and log out but... really we need to be able to show a different experience for logged-in users compared to the experience for logged-out users. 

### Get user details

Let's go ahead and add a welcome message for logged-in users and display their profile picture. We can also hide the login button for logged-in users and hide the logout button for logged-out users. 

To share the user information with the rest of the app let's have `auth.ts` expose a [Svelte store](https://svelte.dev/tutorial/writable-stores) that provides the user details. This will allow `App.svelte` to respond to `app.ts` getting authentication asynchronously and render the logged-in UI when it's ready. 

In `auth.ts` create a store called `user` by importing `writable` from `svelte` and calling it at the top level of the file. 

```ts
import { writable, type Writable } from "svelte/store";

let user: Writable<User> = writable();
```

Then add a new function `getUser()`. In this function start by setting up an Auth0 client using the same config as `login` and `logout`. 

In this function call `getTokenSilently`. This is a bit unintuitive. Without this call, the Auth0 client will not have a token it can use to call the Auth0 authentication server to get the user information.

After the client has a token call the client again to get the user's information from the authentication server. Once you've got the user's details call the `set` method on the user store to publish the new information. 

Wrap all the calls here in a try-catch block. This is because the `getTokenSilently` call will throw if the user is not authenticated. Adding this catch here is important because you need to call your `getUser` function as part of the `withAuth` function. This ensures that the user will automatically see the authentication information. 

You should now have added some code that looks like this to your `auth.ts` file.

```ts
  async function getUser(): Promise<void> {
    const client = createAuth0Client({
      domain: "klee-test.au.auth0.com",
      clientId: "GGOFsf1eiSGvYOBkeDHAAJopE5qRpzN7",
      authorizationParams: {
        redirect_uri: window.location.href,
      },
    });

    try {
      // ensure the client has a token to cal the Auth0 Authentication server.
      await client.getTokenSilently();
      // get the client to fetch the user information.
      const userDetails = await client.getUser();
      // publish the user information
      user.set(userDetails);
    } catch (e) {
      // if the user is not logged in the getTokenSilently call will fail. 
      console.warn(e);
    }
  }

  getUser();
```
You'll also need to remember to return the `user` store from `withAuth` so that `App.svelte` can subscribe to changes. 

```ts
  return {
    login,
    logout,
    user,
  };
```

Now to update the user interface and display some information from the user returned from Auth0. 

In the `<script>` tag in the `App.svelte` file you need to subscribe to changes made to the `user`. To do this, create a variable to hold the user object and call the `subscribe` method on the user object returned from `withAuth`. `subscribe` takes a callback with the updated value for the user's auth.

```tsx
<script lang="ts">
  import type { User } from '@auth0/auth0-spa-js';
  import { withAuth } from './auth';

  const auth = withAuth();

  // a variable to hold the authentication details
  let user: User;
  // update the local store of auth details when they change
  auth.user.subscribe((auth) =>{
    user = auth;
  } )
</script>
```

Finally to get the display to update add an `{#if}` block to the tsx code to switch between showing the log in button or showing log out button and user profile information. 

```tsx
    {#if !user}
    <button on:click={auth.login}>Login</button>
    {:else}
    <button on:click={auth.logout}>Logout</button>
    <h1>Hello {user.nickname}</h1>
    <div class="profile">
      <img class="profile" src={user.picture} alt="User profile">
    </div>
    <p>User:</p>
    <pre>
      {JSON.stringify(user, null, 2)}
    </pre>
    {/if}
```

With this, you've got an app that you can run to log a user in and see some profile information. 

## Getting an access token

The other thing you may want to do is get an access token to call an API.  

For this, you'll need to go back over to the Auth0 console and add an API. You can find the APIs section under the Applications menu. Add a new API give it a name and pick an identifier. Take note of the identifier, you'll need to add it to your code. 

![A screen shot of adding an API to Auth0](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/za2tvnhlpbewzf1dz990.png)

Back in your code add the API's identifier to the code as part of the `authorizationParams` object when creating the client.

Now when you call `client.getTokenSilently()` it will return a valid JWT access token. Add a new store for the token, set it's value once the `getTokenSiliently` call has completed and return the store as part of the `withAuth` response so the rest of your application can make use of it when calling APIs. 

```
const accessToken = await client.getTokenSilently();
token.set(accessToken);
```

The full sample code for this can be found on [GitHub](https://github.com/KleeUT/Svelte_auth0_2022)