ultisuite-client/lib/api/auth-store.ts
R3D347HR4Y 5304790ed5
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(auth): enhance session management and identity provider settings
- Added SessionGuard component to manage session expiration and online status.
- Updated AuthProvider to streamline session fetching and handling.
- Introduced IdentityProvidersSection for managing OAuth, SAML, and LDAP identity providers.
- Implemented identity provider guides for easier configuration.
- Enhanced mail settings with infinite scroll option for improved user experience.
- Updated global styles and layout components for better consistency across the application.
2026-06-09 09:36:46 +02:00

104 lines
2.9 KiB
TypeScript

"use client"
import { create } from "zustand"
import { persist } from "zustand/middleware"
import { debouncedPersistJSONStorage } from "@/lib/stores/debounced-json-storage"
import { useSessionGuardStore } from "@/lib/auth/session-guard-store"
import type { PlatformUser } from "@/lib/auth/jwt-claims"
const AUTH_STORAGE_KEY = "ulti-auth"
const LEGACY_AUTH_KEYS = ["ultimail-auth", "ultidrive-auth"] as const
export { AUTH_STORAGE_KEY, LEGACY_AUTH_KEYS }
function migrateLegacyAuthStorage() {
if (typeof window === "undefined") return
try {
if (localStorage.getItem(AUTH_STORAGE_KEY)) return
for (const legacy of LEGACY_AUTH_KEYS) {
const raw = localStorage.getItem(legacy)
if (raw) {
localStorage.setItem(AUTH_STORAGE_KEY, raw)
return
}
}
} catch {
/* private mode / quota */
}
}
migrateLegacyAuthStorage()
interface AuthState {
accessToken: string | null
refreshToken: string | null
expiresAt: number | null
user: PlatformUser | null
login: (
accessToken: string,
refreshToken: string,
expiresAt: number,
user?: PlatformUser | null
) => void
logout: () => void
isAuthenticated: () => boolean
}
export const useAuthStore = create<AuthState>()(
persist(
(set, get) => ({
accessToken: null,
refreshToken: null,
expiresAt: null,
user: null,
login: (accessToken, refreshToken, expiresAt, user = null) => {
set({ accessToken, refreshToken, expiresAt, user })
useSessionGuardStore.getState().clear()
},
logout: () => {
set({
accessToken: null,
refreshToken: null,
expiresAt: null,
user: null,
})
if (typeof window !== "undefined") {
try {
localStorage.removeItem(AUTH_STORAGE_KEY)
for (const legacy of LEGACY_AUTH_KEYS) {
localStorage.removeItem(legacy)
}
} catch {
/* private mode / quota */
}
}
},
isAuthenticated: () => {
const { accessToken, expiresAt, refreshToken } = get()
if (!accessToken) return false
if (expiresAt && Date.now() < expiresAt) return true
// Access token expired — session may still be renewed via refresh token.
return Boolean(refreshToken)
},
}),
{
name: AUTH_STORAGE_KEY,
storage: debouncedPersistJSONStorage,
version: 1,
migrate: (persisted) => {
const state = (persisted as { state?: AuthState }).state
if (state) {
state.accessToken = null
state.refreshToken = null
state.expiresAt = null
}
return persisted as { state: AuthState; version: number }
},
// Tokens stay in httpOnly cookies + in-memory store (via /api/auth/session).
partialize: (state) => ({
user: state.user,
}),
}
)
)