useAuth.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. let _refreshInFlight = null;
  41. function doRefresh() {
  42. if (_refreshInFlight) return _refreshInFlight;
  43. _refreshInFlight = (async () => {
  44. const rt = session.value?.refreshToken;
  45. if (!rt) throw new Error("no_refresh_token");
  46. const result = await refreshTokenRequest({ refreshToken: rt });
  47. session.value = {
  48. ...session.value,
  49. accessToken: result.accessToken,
  50. refreshToken: result.refreshToken ?? session.value?.refreshToken ?? null
  51. };
  52. persistSession(session.value);
  53. setAccessToken(result.accessToken);
  54. scheduleProactiveRefresh(result.accessToken);
  55. return result.accessToken;
  56. })().finally(() => {
  57. _refreshInFlight = null;
  58. });
  59. return _refreshInFlight;
  60. }
  61. function scheduleProactiveRefresh(token) {
  62. clearTimeout(_proactiveRefreshTimer);
  63. if (!token) return;
  64. try {
  65. const payload = JSON.parse(atob(token.split(".")[1]));
  66. const delay = payload.exp * 1000 - Date.now() - 60_000;
  67. if (delay <= 0) return;
  68. _proactiveRefreshTimer = setTimeout(() => {
  69. doRefresh().catch(() => {
  70. });
  71. }, delay);
  72. } catch {
  73. }
  74. }
  75. function setupAuth() {
  76. const token = session.value?.accessToken ?? null;
  77. setAccessToken(token);
  78. if (!token) return;
  79. scheduleProactiveRefresh(token);
  80. setRefreshCallback(doRefresh);
  81. setAuthErrorCallback(() => {
  82. clearTimeout(_proactiveRefreshTimer);
  83. resetChatSingleton();
  84. session.value = null;
  85. persistSession(null);
  86. setAccessToken(null);
  87. setRefreshCallback(null);
  88. setAuthErrorCallback(null);
  89. window.location.href = "/login";
  90. });
  91. }
  92. setupAuth();
  93. async function login({ login, senha, rememberMe = false }) {
  94. const payload = await loginRequest({ login, senha, rememberMe });
  95. session.value = {
  96. usuario: payload.usuario,
  97. accessToken: payload.accessToken ?? null,
  98. refreshToken: payload.refreshToken ?? null,
  99. rememberMe,
  100. savedAt: new Date().toISOString()
  101. };
  102. persistSession(session.value);
  103. setupAuth();
  104. return payload;
  105. }
  106. async function logout() {
  107. clearTimeout(_proactiveRefreshTimer);
  108. const rt = session.value?.refreshToken;
  109. try {
  110. await logoutRequest({ refreshToken: rt });
  111. } finally {
  112. resetChatSingleton();
  113. session.value = null;
  114. persistSession(null);
  115. setAccessToken(null);
  116. setRefreshCallback(null);
  117. setAuthErrorCallback(null);
  118. }
  119. }
  120. export function useAuth() {
  121. return {
  122. session: readonly(session),
  123. user,
  124. isAuthenticated,
  125. login,
  126. logout
  127. };
  128. }