import { CookieRef } from "nuxt/app"; import { PublicApi } from "~~/lib/api/public"; import { UserOut } from "~~/lib/api/types/data-contracts"; import { UserClient } from "~~/lib/api/user"; export interface IAuthContext { get token(): string | null; get expiresAt(): string | null; get attachmentToken(): string | null; /** * The current user object for the session. This is undefined if the session is not authorized. */ user?: UserOut; /** * Returns true if the session is expired. */ isExpired(): boolean; /** * Returns true if the session is authorized. */ isAuthorized(): boolean; /** * Invalidates the session by removing the token and the expiresAt. */ invalidateSession(): void; /** * Logs out the user and calls the invalidateSession method. */ logout(api: UserClient): ReturnType; /** * Logs in the user and sets the authorization context via cookies */ login(api: PublicApi, email: string, password: string, stayLoggedIn: boolean): ReturnType; } class AuthContext implements IAuthContext { // eslint-disable-next-line no-use-before-define private static _instance?: AuthContext; private static readonly cookieTokenKey = "hb.auth.token"; private static readonly cookieExpiresAtKey = "hb.auth.expires_at"; private static readonly cookieAttachmentTokenKey = "hb.auth.attachment_token"; user?: UserOut; private _token: CookieRef; private _expiresAt: CookieRef; private _attachmentToken: CookieRef; get token() { return this._token.value; } get expiresAt() { return this._expiresAt.value; } get attachmentToken() { return this._attachmentToken.value; } private constructor(token: string, expiresAt: string, attachmentToken: string) { this._token = useCookie(token); this._expiresAt = useCookie(expiresAt); this._attachmentToken = useCookie(attachmentToken); } static get instance() { if (!this._instance) { this._instance = new AuthContext( AuthContext.cookieTokenKey, AuthContext.cookieExpiresAtKey, AuthContext.cookieAttachmentTokenKey ); } return this._instance; } isExpired() { const expiresAt = this.expiresAt; if (expiresAt === null) { return true; } const expiresAtDate = new Date(expiresAt); const now = new Date(); return now.getTime() > expiresAtDate.getTime(); } isAuthorized() { return !!this._token.value && !this.isExpired(); } invalidateSession() { this.user = undefined; // Delete the cookies this._token.value = null; this._expiresAt.value = null; this._attachmentToken.value = null; console.log("Session invalidated"); window.location.href = "/"; } async login(api: PublicApi, email: string, password: string, stayLoggedIn: boolean) { const r = await api.login(email, password, stayLoggedIn); if (!r.error) { const expiresAt = new Date(r.data.expiresAt); this._token = useCookie(AuthContext.cookieTokenKey, { expires: expiresAt, }); this._expiresAt = useCookie(AuthContext.cookieExpiresAtKey, { expires: expiresAt, }); this._attachmentToken = useCookie(AuthContext.cookieAttachmentTokenKey, { expires: expiresAt, }); this._token.value = r.data.token; this._expiresAt.value = r.data.expiresAt as string; this._attachmentToken.value = r.data.attachmentToken; } return r; } async logout(api: UserClient) { const r = await api.user.logout(); if (!r.error) { this.invalidateSession(); } return r; } } export function useAuthContext(): IAuthContext { return AuthContext.instance; }