import { getResponseBodyOrError } from '$lib/api/utils'; import { readUser } from '$lib/api/endpoints'; import jwt_decode from 'jwt-decode'; import { writable } from 'svelte/store'; /** * Svelte store to hold the current StoredUser object. */ export const storedUser = writable(); // Name of the key holding the auth JWT in localstorage const jwtKey = 'jwt'; // Name of the key holding the authenticated user in localstorage const userKey = 'user'; export type StoredUser = { id: number, email: string, isAdmin: boolean, sessionExpires: Date } /** * Saves the specified token in localstorage. * * @param {string} token - The token to save in localstorage. */ function saveTokenToLocalstorage(token: string): void { localStorage.setItem(jwtKey, token); } /** * Retrieves and returns the token, if present, from localstorage. */ export function getTokenFromLocalstorage(): string | null { return localStorage.getItem(jwtKey); } /** * Removes the saved token from localstorage. */ function clearTokenInLocalstorage(): void { localStorage.removeItem(jwtKey); } /** * Saves the specified StoredUser object in localstorage. * * @param {StoredUser} user - The user to write to localstorage. */ function saveUserToLocalstorage(user: StoredUser): void { localStorage.setItem(userKey, JSON.stringify(user)); } /** * Retrieves and returns the user, if present, from localstorage. */ function getUserFromLocalstorage(): StoredUser | null { let item: string | null = localStorage.getItem(userKey); if (typeof item !== 'string') { return null; } return JSON.parse(item) as StoredUser; } /** * Removes the saved user from localstorage. */ function clearUserInLocalstorage(): void { localStorage.removeItem(userKey); } /** * Sends an API request containing the specified login credentials to the backend, * and returns the JWT retrieved from the response on sucess. * * @param {string} email - The email address of the user whose JWT to retrieve. * @param {string} password - The password used to authenticate the user whose JWT is being retrieved. * @throws {Error} - If the API request failed or the supplied credentials were invalid. */ async function requestJwt(email: string, password: string): Promise { type Token = { token: string } const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ email: email, password: password, }) }); const responseBody = await getResponseBodyOrError(response) as Token; return responseBody.token; } /** * Loads the user currently saved in localstorage into the storedUser * svelte store. */ export function loadUserIntoStore(): void { storedUser.set(getUserFromLocalstorage()); } /** * Clears the storedUser svelte store. */ function clearUserFromStore(): void { storedUser.set(null); } /** * Sends an API request to retrieve the specified user's JWT from the backend, * and stores the retrieved token in localstorage. Then, retrieves the authenticated * user's basic account information (StoredUser), saves it in localsotrage and in the * currentUser svelte store. * * @param {string} email - The email address of the user whose token and information to retrieve. * @param {string} password - The password used to authenticate the user whose token is being retrieved. * @throws {Error} - If any API request fails or the supplied credentials were invalid. */ export async function login(email: string, password: string): Promise { const token = await requestJwt(email, password); interface Token { sub: number, exp: number } const parsedToken = jwt_decode(token) as Token; const userId = parsedToken.sub; const user = await readUser(userId, token); saveTokenToLocalstorage(token); saveUserToLocalstorage({ id: user.id, email: user.email, isAdmin: user.is_admin, sessionExpires: new Date(parsedToken.exp), }); loadUserIntoStore(); } /** * Purges the currently logged in user's information and token from localstorage * and the svelte store. */ export function logout(): void { clearUserFromStore(); clearUserInLocalstorage(); clearTokenInLocalstorage(); }