Introduction
Recently I implemented HTTP Basic Auth using a session cookie in SvelteKit on
Cloudflare Pages using Cloudflare D1 as a database backend. Here I'll show some
code snippets so you can do the same.
Project Setup
First you would want to set up your project according to
this.
So you can have a basic project set up.
Database Setup
Next you'll want to add the database to your project. It's easy to create using
wrangler. Just remember to link it to your project in your cloudflare dash
under settings > functions.
wrangler d1 create your-database-nameThen you can copy it into your
so your local dev works with it. To run with support for D1 you can use this
oneliner.
npm run build && wrangler pages dev .svelte-kit/cloudflareMy final schema looks like this. Yours might be a bit different, but mine
allows for adding songs to a playlist, which was the purpose of
CREATE TABLE IF NOT EXISTS Users (id TEXT PRIMARY KEY, password TEXT);
CREATE TABLE IF NOT EXISTS Playlist (id TEXT PRIMARY KEY, email TEXT, url TEXT, date TEXT);You'll want to create these tables using wrangler both locally (on your dev
server) and remotely.
wrangler d1 execute svelte-auth --local --file=./schema.sql
wrangler d1 execute svelte-auth --file=./schema.sqlNow you can add it to your
like this.
declare global {
namespace App {
interface Platform {
env?: {
DB: D1Database;
};
context: {
waitUntil(promise: Promise<any>): void;
};
caches: CacheStorage & { default: Cache };
}
}
}
export {};Authentication Implementation
Now you're ready to set up authentication. It's probably easier to just show
you how I did it. I imported sha256 which is just a simple function I wrote
to call web crypto to get the sha256sum of a string.
is my code.
I added form actions. These
allow you to quickly write code that will work with HTML <form>'s.
import { redirect } from '@sveltejs/kit';
import sha256 from '$lib/sha256';
export const actions = {
register: async (event) => {
const data = await event.request.formData();
const email = data.get('email');
const password = await sha256(data.get('password')?.toString() ?? '');
const { success } = await event.platform?.env?.DB.prepare('INSERT INTO Users VALUES (?, ?)')
.bind(email, password)
.all();
if (success) {
event.cookies.set('session', JSON.stringify({ email, password }));
throw redirect(303, '/');
}
},
login: async (event) => {
const data = await event.request.formData();
const email = data.get('email');
const password = await sha256(data.get('password')?.toString() ?? '');
const results = await event.platform?.env?.DB.prepare('SELECT password FROM Users WHERE id=?')
.bind(email)
.all();
if (results.results[0].password === password) {
event.cookies.set('session', JSON.stringify({ email, password }));
throw redirect(303, '/');
}
}
};Checking Authentication Status
Now this is enough to save a cookie with the user auth, but to re-auth on a
page we need to check if their cookie is valid. So how I do this is I wrote
another function called
import type { D1Database } from '@cloudflare/workers-types';
const tryLogin = async (session_cookie: string | undefined, DB: D1Database) => {
if (session_cookie) {
const { email, password } = JSON.parse(session_cookie);
const results = await DB.prepare('SELECT password FROM Users WHERE id=?').bind(email).all();
if (results.results[0].password === password) {
return true;
}
}
return false;
};
export default tryLogin;Now I can call this inside
import tryLogin from '$lib/tryLogin';
import getCurrentSong from '$lib/getCurrentSong';
export const load = async (event) => ({
auth: await tryLogin(event.cookies.get('session'), event.platform?.env?.DB),
song: await getCurrentSong(event.platform?.env?.DB)
});Using it inside the
is as simple as this one line.
export let data: {auth: boolean, song: {id: string, url: string}};Playlist Management
Now I wrote more form actions for adding and removing songs from the playlist
here.
import { redirect } from '@sveltejs/kit';
import tryLogin from '$lib/tryLogin';
export const actions = {
add: async (event) => {
if (await tryLogin(event.cookies.get('session'), event.platform?.env?.DB)) {
const session = JSON.parse(event.cookies.get('session'));
const email = session.email;
const formdata = await event.request.formData();
const v = new URL(formdata.get('url')).searchParams.get('v');
await event.platform?.env?.DB.prepare('INSERT INTO Playlist VALUES (?, ?, ?, ?)')
.bind(crypto.randomUUID(), email, v, Math.floor(new Date().getTime()))
.all();
}
throw redirect(303, '/');
},
remove: async (event) => {
if (await tryLogin(event.cookies.get('session'), event.platform?.env?.DB)) {
const formdata = await event.request.formData();
const id = formdata.get('id');
await event.platform?.env?.DB.prepare('DELETE FROM Playlist WHERE id=?').bind(id).all();
}
throw redirect(303, '/');
}
};User Interface
Now this allows us to add songs, we just need the interface for it. So I wrote
the main page
to allow logged in users to submit songs.
<script lang="ts">
import { enhance } from '$app/forms';
import YouTube from '$lib/svelte-youtube.svelte';
export let data: { auth: boolean; song: { id: string; url: string } };
</script>
{#if !data.auth}
<p>Visit <a href="/login">login</a></p>
{/if}
<YouTube
on:end={() => document.getElementById('remove_song')?.click()}
videoId={data.song.url}
options={{ playerVars: { autoplay: 1 } }}
/>
<form style="display: none;" method="POST" action="/playlist?/remove" use:enhance>
<input name="id" value={data.song.id} />
<button id="remove_song">remove</button>
</form>
{#if data.auth}
<form method="POST" action="/playlist?/add" use:enhance>
<label
>Submit a song
<input name="url" type="url" placeholder="paste a youtube link" pattern=".*v=.*" />
</label>
<button>Submit</button>
</form>
<p><a href="/logout">Log out</a></p>
{/if}Conclusion
If you have any problems or you want to see the entire repo, the code is
here.