73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
|
import type { AuthState, LoginSuccessPayload } from '@/features/auth/authTypes';
|
|
import { STORAGE_KEYS } from '@/utils/storage';
|
|
import { clearAuthTokens, getAccessToken, isAccessTokenExpired, storeAuthTokens } from '@/utils/authToken';
|
|
|
|
const clearPersistedAuthState = () => {
|
|
localStorage.removeItem(STORAGE_KEYS.accessToken);
|
|
localStorage.removeItem(STORAGE_KEYS.user);
|
|
localStorage.removeItem(STORAGE_KEYS.authState);
|
|
clearAuthTokens();
|
|
};
|
|
|
|
const loadPersistedAuthState = (): AuthState => {
|
|
const loggedOutState: AuthState = { user: null, accessToken: null, isAuthenticated: false };
|
|
const storedAuth = localStorage.getItem(STORAGE_KEYS.authState);
|
|
const storedAccessToken = getAccessToken();
|
|
|
|
if (!storedAuth) {
|
|
return loggedOutState;
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(storedAuth) as AuthState;
|
|
const accessToken = parsed.accessToken || storedAccessToken;
|
|
|
|
if (accessToken && parsed.user && !isAccessTokenExpired()) {
|
|
return { user: parsed.user, accessToken, isAuthenticated: true };
|
|
}
|
|
} catch {
|
|
// fall through to the logged-out default below
|
|
}
|
|
|
|
clearPersistedAuthState();
|
|
return loggedOutState;
|
|
};
|
|
|
|
const authSlice = createSlice({
|
|
name: 'auth',
|
|
initialState: loadPersistedAuthState(),
|
|
reducers: {
|
|
loginSuccess: (state, action: PayloadAction<LoginSuccessPayload>) => {
|
|
state.user = action.payload.user;
|
|
state.accessToken = action.payload.accessToken;
|
|
state.isAuthenticated = true;
|
|
|
|
localStorage.setItem(STORAGE_KEYS.accessToken, action.payload.accessToken);
|
|
localStorage.setItem(STORAGE_KEYS.user, JSON.stringify(action.payload.user));
|
|
storeAuthTokens({
|
|
access_token: action.payload.accessToken,
|
|
refresh_token: action.payload.refreshToken ?? '',
|
|
});
|
|
localStorage.setItem(
|
|
STORAGE_KEYS.authState,
|
|
JSON.stringify({
|
|
user: action.payload.user,
|
|
accessToken: action.payload.accessToken,
|
|
isAuthenticated: true,
|
|
}),
|
|
);
|
|
},
|
|
logout: (state) => {
|
|
state.user = null;
|
|
state.accessToken = null;
|
|
state.isAuthenticated = false;
|
|
|
|
clearPersistedAuthState();
|
|
},
|
|
hydrateAuth: () => loadPersistedAuthState(),
|
|
},
|
|
});
|
|
|
|
export const { loginSuccess, logout, hydrateAuth } = authSlice.actions;
|
|
export default authSlice.reducer;
|