diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts index 459141ad..c33db87a 100644 --- a/frontend/src/stores/auth.ts +++ b/frontend/src/stores/auth.ts @@ -7,9 +7,11 @@ export const useAuthStore = defineStore("auth", { state: (): { user: IUser | null; jwt: string; + logoutTimer: number | null; } => ({ user: null, jwt: "", + logoutTimer: null, }), getters: { // user and jwt getter removed, no longer needed @@ -37,5 +39,8 @@ export const useAuthStore = defineStore("auth", { clearUser() { this.$reset(); }, + setLogoutTimer(logoutTimer: number | null) { + this.logoutTimer = logoutTimer; + }, }, }); diff --git a/frontend/src/types/user.d.ts b/frontend/src/types/user.d.ts index b81806fc..c8c1e38e 100644 --- a/frontend/src/types/user.d.ts +++ b/frontend/src/types/user.d.ts @@ -64,3 +64,8 @@ interface IRegexp { } type UserTheme = "light" | "dark" | ""; + +interface SessionToken { + token: string; + expiresAt: string; +} \ No newline at end of file diff --git a/frontend/src/utils/auth.ts b/frontend/src/utils/auth.ts index b868d90f..0ac285db 100644 --- a/frontend/src/utils/auth.ts +++ b/frontend/src/utils/auth.ts @@ -5,17 +5,29 @@ import { jwtDecode } from "jwt-decode"; import { baseURL, noAuth } from "./constants"; import { StatusError } from "@/api/utils"; -export function parseToken(token: string) { +export function parseToken(body: SessionToken) { // falsy or malformed jwt will throw InvalidTokenError - const data = jwtDecode(token); + const data = jwtDecode(body.token); - document.cookie = `auth=${token}; Path=/; SameSite=Strict;`; + document.cookie = `auth=${body.token}; Path=/; SameSite=Strict;`; - localStorage.setItem("jwt", token); + localStorage.setItem("jwt", body.token); const authStore = useAuthStore(); - authStore.jwt = token; + authStore.jwt = body.token; authStore.setUser(data.user); + + const expiresAt = new Date(body.expiresAt); + + if (authStore.logoutTimer) { + clearTimeout(authStore.logoutTimer); + } + + authStore.setLogoutTimer( + window.setTimeout(() => { + logout(); + }, expiresAt.getTime() - Date.now()) + ); } export async function validateLogin() { @@ -44,11 +56,12 @@ export async function login( body: JSON.stringify(data), }); - const body = await res.text(); if (res.status === 200) { + const body = await res.json(); parseToken(body); } else { + const body = await res.text(); throw new StatusError( body || `${res.status} ${res.statusText}`, res.status @@ -64,11 +77,12 @@ export async function renew(jwt: string) { }, }); - const body = await res.text(); if (res.status === 200) { + const body = await res.json(); parseToken(body); } else { + const body = await res.text(); throw new StatusError( body || `${res.status} ${res.statusText}`, res.status diff --git a/http/auth.go b/http/auth.go index 0ecaed14..aaed0d02 100644 --- a/http/auth.go +++ b/http/auth.go @@ -214,8 +214,13 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use return http.StatusInternalServerError, err } - w.Header().Set("Content-Type", "text/plain") - if _, err := w.Write([]byte(signed)); err != nil { + response := map[string]interface{}{ + "token": signed, + "expiresAt": claims.ExpiresAt.Time.Format(time.RFC3339), // fecha en string ISO 8601 + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(response); err != nil { return http.StatusInternalServerError, err } return 0, nil