In this post, I run through, step by step, how I went about getting authentication into a Svelte application using Auth0. If you just want to see the full sample code for this can be found on GitHub.
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 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/.
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. 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.
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.
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
<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.
<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 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.
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.
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.
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.
<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.
{#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.
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
Top comments (1)
Nice guide! One thing I noticed: You will not be able to get the token silently without updating your auth0 configuration to also include the localhost:5173/ domain in the allowed web origins.