useAuth.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { computed, readonly, ref } from "vue";
  2. import { loginRequest, logoutRequest, refreshTokenRequest } from "../api/auth.js";
  3. import { setAccessToken, setRefreshCallback, setAuthErrorCallback } from "../api/client.js";
  4. import { resetChatSingleton } from "./useChat.js";
  5. const LOCAL_STORAGE_KEY = "auth.session";
  6. const SESSION_STORAGE_KEY = "auth.session.temp";
  7. function getStorage(kind) {
  8. if (typeof window === "undefined") return null;
  9. return kind === "local" ? window.localStorage : window.sessionStorage;
  10. }
  11. function loadStoredSession() {
  12. for (const kind of ["local", "session"]) {
  13. try {
  14. const storage = getStorage(kind);
  15. const raw = storage?.getItem(kind === "local" ? LOCAL_STORAGE_KEY : SESSION_STORAGE_KEY);
  16. if (!raw) continue;
  17. const parsed = JSON.parse(raw);
  18. if (parsed?.usuario) return parsed;
  19. } catch {}
  20. }
  21. return null;
  22. }
  23. function persistSession(sess) {
  24. try {
  25. getStorage("local")?.removeItem(LOCAL_STORAGE_KEY);
  26. getStorage("session")?.removeItem(SESSION_STORAGE_KEY);
  27. if (sess?.rememberMe) {
  28. getStorage("local")?.setItem(LOCAL_STORAGE_KEY, JSON.stringify(sess));
  29. return;
  30. }
  31. if (sess) {
  32. getStorage("session")?.setItem(SESSION_STORAGE_KEY, JSON.stringify(sess));
  33. }
  34. } catch {}
  35. }
  36. const session = ref(loadStoredSession());
  37. const user = computed(() => session.value?.usuario ?? null);
  38. const isAuthenticated = computed(() => Boolean(user.value?.Id));
  39. let _proactiveRefreshTimer = null;
  40. function scheduleProactiveRefresh(token) {
  41. clearTimeout(_proactiveRefreshTimer);
  42. if (!token) return;
  43. try {
  44. const payload = JSON.parse(atob(token.split(".")[1]));
  45. const delay = payload.exp * 1000 - Date.now() - 60_000;
  46. if (delay <= 0) return;
  47. _proactiveRefreshTimer = setTimeout(async () => {
  48. const rt = session.value?.refreshToken;
  49. if (!rt) return;
  50. try {
  51. const result = await refreshTokenRequest({ refreshToken: rt });
  52. session.value = { ...session.value, accessToken: result.accessToken };
  53. persistSession(session.value);
  54. setAccessToken(result.accessToken);
  55. scheduleProactiveRefresh(result.accessToken);
  56. } catch {
  57. // 401 interceptor handles expired token on next request
  58. }
  59. }, delay);
  60. } catch {
  61. // token não é JWT válido, ignora
  62. }
  63. }
  64. function setupAuth() {
  65. const token = session.value?.accessToken ?? null;
  66. setAccessToken(token);
  67. if (!token) return;
  68. scheduleProactiveRefresh(token);
  69. setRefreshCallback(async () => {
  70. const rt = session.value?.refreshToken;
  71. if (!rt) throw new Error("no_refresh_token");
  72. const result = await refreshTokenRequest({ refreshToken: rt });
  73. session.value = { ...session.value, accessToken: result.accessToken };
  74. persistSession(session.value);
  75. setAccessToken(result.accessToken);
  76. scheduleProactiveRefresh(result.accessToken);
  77. return result.accessToken;
  78. });
  79. setAuthErrorCallback(() => {
  80. clearTimeout(_proactiveRefreshTimer);
  81. resetChatSingleton();
  82. session.value = null;
  83. persistSession(null);
  84. setAccessToken(null);
  85. setRefreshCallback(null);
  86. setAuthErrorCallback(null);
  87. window.location.href = "/login";
  88. });
  89. }
  90. setupAuth();
  91. async function login({ login, senha, rememberMe = false }) {
  92. const payload = await loginRequest({ login, senha, rememberMe });
  93. session.value = {
  94. usuario: payload.usuario,
  95. accessToken: payload.accessToken ?? null,
  96. refreshToken: payload.refreshToken ?? null,
  97. rememberMe,
  98. savedAt: new Date().toISOString()
  99. };
  100. persistSession(session.value);
  101. setupAuth();
  102. return payload;
  103. }
  104. async function logout() {
  105. clearTimeout(_proactiveRefreshTimer);
  106. const rt = session.value?.refreshToken;
  107. try {
  108. await logoutRequest({ refreshToken: rt });
  109. } finally {
  110. resetChatSingleton();
  111. session.value = null;
  112. persistSession(null);
  113. setAccessToken(null);
  114. setRefreshCallback(null);
  115. setAuthErrorCallback(null);
  116. }
  117. }
  118. function clearSession() {
  119. session.value = null;
  120. persistSession(null);
  121. setAccessToken(null);
  122. setRefreshCallback(null);
  123. setAuthErrorCallback(null);
  124. }
  125. export function useAuth() {
  126. return {
  127. session: readonly(session),
  128. user,
  129. isAuthenticated,
  130. login,
  131. logout,
  132. clearSession
  133. };
  134. }