28
Cognito Authentication for your SvelteKit app
Manage your users in your SvelteKit app with AWS Cognito.
To be honest, in my case, it’s just because we’re using AWS at Koa Health and it was the natural choice. I have many things to say about AWS Cognito, both good and bad, but this won’t be the article covering that 😉.
Well, hopefully, chances are that you may be already using AWS as your main cloud provider and this post can help you to integrate AWS Cognito in you SvelteKit app.
I’m starting with Svelte so if you have any suggestions to improve what I’ll show here, you’re more than welcome to write a comment or contact me.
Alternatively, feel free to take a look at the GitHub repository hosting the final project and open an issue there if you prefer.
We’re just going to use SvelteKit to build a simple app that will authenticate users, so let’s start with with some simple commands to create the project:
npm init svelte@next sveltekit-cognito-auth
# I'm choosing the Skeleton project,
# opting out of TypeScript for this tutorial
# and using ESlint and Prettier support
cd sveltekit-cogniton-auth
npm install
You can find here more details about how to get started with SvelteKit in case you want to learn more about it.
For now, let’s open the src/index.svelte
file, delete its content and build this spartan UI:
<script>
// we'll edit this later
let user;
</script>
<svelte:head>
<title>SvelteKit - Cognito</title>
</svelte:head>
{#if !user}
<button>Log In with Cognito</button>
{:else}
<button>Log Out</button>
{/if}
The idea here is to have a button that will trigger the Cognito authentication flow and another one that will log out the user. As simple as that!
For the moment, we’re going to skip all the logic to show one button or the other but we will come back to it later.
Let’s check that our app is working:
npm run dev -- --open
Hopefully, you should see a page with a button that says Log In with Cognito
.
Let’s create and configure our Cognito User Pool now.
First of all, let’s introduce the pool name: sveltekit-pool
.
For this example, we’re going to use these simple settings.
Remember to tick the email checkbox in the Attributes section
.
We’ll also relax the Password Policy to allow users to choose their own passwords without many hassles, so uncheck all the checkboxes related to the password strength.
Let’ press the Next Step
button until we arrive to the App Clients
section and let’s add one app client.
Leave it everything as it is and set the name that you want. For this example, I’m using sveltekit
. Fairly original, isn’t it? 😉
There are still a couple of things to do in our Cognito Pool. Ideally, we’d like to use the Cognito Hosted UI which will simplify a lot the Sign Up and Sign In flows.
For the moment, bear with me, fill the App Client settings
with the following values and just click Save
:
A couple of things to note here:
- The callback url will be a backend endpoint we’ll be exposing in our app.
- The Sign Out url is the final url to be redirected to once we’ve signed out from Cognito
Finally, for the Cognito Hosted UI to work, we need to set up a domain. Note that the domain you choose is subjected to availability.
Let’s move away from Cognito for the moment and let’s focus again on our Svelte
app.
Luckily for us, RaviAnand Mohabir has created a super cool library called SvelteKit Auth, based on NextAuth.js, which will allow us to use some of the most popular identity providers, such as Google, Twitch, Facebook, Twitter and Reddit.
But not only that, it also provides a generic OAuth2 provider that we will leverage to authenticate our users with Cognito.
The library handles all the OAuth2 flow for you, including the redirects, the token exchange and the user profile retrieval.
Let’s add the dependency to our app before we continue:
npm i sk-auth --save
Let’s create a new file called src/lib/auth.js
and paste the following code:
import { SvelteKitAuth, Providers } from 'sk-auth';
// this is the domain we set up in our Cognito Pool
const DOMAIN = 'sveltekit.auth.eu-west-1.amazoncognito.com';
// these are the configuration seetings for our OAUTH2 provider
const config = {
accessTokenUrl: `https://${DOMAIN}/oauth2/token`,
profileUrl: `https://${DOMAIN}/oauth2/userInfo`,
authorizationUrl: `https://${DOMAIN}/oauth2/authorize`,
redirect: 'https://robertohuertas.com',
clientId: import.meta.env.VITE_CLIENT_ID,
clientSecret: import.meta.env.VITE_CLIENT_SECRET,
scope: ['openid', 'email'],
id: 'cognito', // IMPORTANT: this is the id that we'll use to identify our provider
contentType: 'application/x-www-form-urlencoded'
};
const oauthProvider = new Providers.OAuth2Provider(config);
// exposing our auth object
export const appAuth = new SvelteKitAuth({
providers: [oauthProvider]
});
Probably, if it’s the first time you’ve seen something like import.meta.env.VITE_whatever
, you’ll be wondering what the hell is that.
Basically, it’s the way to get some environment variables from an .env
file in your root.
You can find more info about it in the SvelteKit FAQs.
So make sure to add the following to your .env
file:
VITE_CLIENT_ID=<your_client_id>
VITE_CLIENT_SECRET=<your_client_secret>
Now, let’s create a new file called src/routes/api/auth/[...auth].js
.
If that felt strange to you, you have to know that this kind of naming is a routing convention from SvelteKit.
You can use this piece of code to expose the auth endpoint:
// we're importing our auth object here
import { appAuth } from '$lib/auth';
// and exposing the get and post method handlers
export const { get, post } = appAuth;
The Auth object exposes a getSession method that we can use in our hooks.js
file in order to return a session that it’s accessible on the client.
As we have used the Skeleton project, we don’t have any hooks.js
file yet. Let’s create one in the src
folder and add this code:
import { appAuth } from '$lib/auth';
export const { getSession } = appAuth;
With this, the claims of the access token will be available on the session store.
We’re going to add now some actions to the buttons in our UI, so let’s open our src/index.svelte
file and edit it:
<script>
import { signOut as authSignOut } from 'sk-auth/client';
import { session } from '$app/stores';
// getting the user from the session store
$: user = $session.user;
function signIn() {
location.assign('/api/auth/signin/cognito?redirect=/');
}
function signOut() {
authSignOut().then(session.set);
}
</script>
<svelte:head>
<title>SvelteKit - Cognito</title>
</svelte:head>
{#if !user}
<button on:click="{signIn}">Log In with Cognito</button>
{:else}
<h2>Welcome {user.email}!</h2>
<p>Your username is {user.username} and your email has been verified: {user.email_verified}</p>
<button on:click={signOut}>Log Out</button>
{/if}
Notice that we’ve added some actions to the buttons which are fairly self-explanatory.
The signIn
action will redirect the user to our api/auth
endpoint (the one we set up in the previous step) with signin
and cognito
as parameters (remember that we set the id of our provider to cognito
).
Finally, the redirect
querystring parameter will tell the endpoint to redirect the user to our /
route once the user is authenticated.
Let’s run the app and click over the Log In with Cognito
button. You should see the Cognito Hosted UI:
As we don’t have any account yet, let’s click over the Sign Up
link and create a test user.
Use a real email address as Cognito will send you a verification email with a code you’ll need to enter in the UI.
Once you’ve confirmed your email, you’ll be redirected to the /
route but, this time, you should see something like this:
If you check your Cognito pool, you should be able to see the user you just created.
Note that SvelteKit Auth will set an HttpOnly
cookie in order to manage the session for you.
As you can see, setting up an OAuth2 provider is pretty easy with SvelteKit Auth. If you need further adjustments, check out the repository. If you take a look at the example app, you’ll see plenty of ways to adjust your flow.
As a final note, I would like to mention that our Log Out
button is not really logging out our user from Cognito as we’re only removing the cookie from our browser. So if you try to log in again, you’ll probably be logged in without having to introduce any credentials.
As a way to mitigate this, you can clear the Cognito Hosted UI domain cookies or you just can extend the log out functionality by calling the logout endpoint from AWS Cognito. Give it a try!
--
Originally published at robertohuertas.com on Jul 25, 2021.
28