I'm trying to implement JWT authentication in my SvelteKit SPA. I'm new to TS (and thus Svelte[Kit]) and there is a lot to learn, so my problem might not strictly be with Svelte and its store system but also with TS in general.
My root +layout.svelte
for all routes that require authentication looks like this:
<script lang="ts">
import { goto } from '$app/navigation';
import auth from '$lib/stores/auth';
</script>
{#await auth.init() then user}
<slot />
{:catch}
{goto('/login')}
{/await}
lib/stores/auth.ts
:
import { writable, get } from 'svelte/store';
import { jwtDecode, type JwtPayload } from 'jwt-decode';
type User = {
email: string;
name: string;
};
interface Token extends JwtPayload {
user: User;
}
function createStore() {
const user = {};
const { subscribe, update, set } = writable(user as User);
return {
subscribe,
async init() {
const tokenStr = localStorage.getItem('token')!;
const token = jwtDecode<Token>(tokenStr);
set(token.user);
},
setToken(tokenStr: string) {
const token = jwtDecode<Token>(tokenStr);
let storeValue = get(this);
storeValue = token.user;
update(() => storeValue);
localStorage.setItem('token', tokenStr);
}
};
}
export default createStore();
My problem is that I can't use the user
result object in my layout (e.g. to display the username in the navigation, etc.) It is undefined. Why is that?
So I ended up using svelte-persisted-store as also suggested by @José.
lib/stores/auth.ts
:
import { derived, writable } from 'svelte/store';
import { browser } from '$app/environment';
import { jwtDecode, type JwtPayload } from 'jwt-decode';
type User = {
email: string;
name: string;
};
interface Token extends JwtPayload {
user: User;
}
export const token = (() => {
const { set, subscribe, update } = writable(browser ? localStorage.getItem('token') : null);
return {
set: (value: string | null) => {
value ? localStorage.setItem('token', value) : localStorage.removeItem('token');
set(value);
},
subscribe,
update
};
})();
export const user = derived(token, (token) => (token ? jwtDecode<Token>(token).user : null));
layout.svelte
:
<script lang="ts">
import { goto } from '$app/navigation';
import { user } from '$lib/stores/auth';
</script>
{#if $user}
<slot />
{:else}
{goto('/login')}
{/if}