fastapi-svelte-template/frontend/src/lib/auth/session.ts

157 lines
4.2 KiB
TypeScript

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<string> {
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<void> {
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();
}