mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
update usermanagement to legacy mode
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
export * from './lib/base-api';
|
||||
export * from './lib/query-and-mutation';
|
||||
export * from './lib/session';
|
||||
export { baseQueryWithReauth, configureTokenRefresh } from './lib/base-api/base-query-with-reauth';
|
||||
|
||||
53
libs/api/src/lib/base-api/base-query-with-reauth.ts
Normal file
53
libs/api/src/lib/base-api/base-query-with-reauth.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { fetchBaseQuery, type BaseQueryFn } from '@reduxjs/toolkit/query/react';
|
||||
import type { FetchArgs, FetchBaseQueryError } from '@reduxjs/toolkit/query';
|
||||
import { resolveSessionContext } from '../session';
|
||||
|
||||
const BASE_API_URL =
|
||||
(import.meta as { env?: Record<string, string> }).env?.['VITE_BASE_API_URL'] ??
|
||||
'http://localhost:3001/api';
|
||||
|
||||
let _onTokenExpired: (() => Promise<string>) | null = null;
|
||||
let _onAuthFailure: (() => void) | null = null;
|
||||
|
||||
export function configureTokenRefresh(config: {
|
||||
onTokenExpired: () => Promise<string>;
|
||||
onAuthFailure: () => void;
|
||||
}) {
|
||||
_onTokenExpired = config.onTokenExpired;
|
||||
_onAuthFailure = config.onAuthFailure;
|
||||
}
|
||||
|
||||
export const baseQueryWithReauth: BaseQueryFn<
|
||||
string | FetchArgs,
|
||||
unknown,
|
||||
FetchBaseQueryError
|
||||
> = async (args, api, extraOptions) => {
|
||||
const baseQuery = fetchBaseQuery({
|
||||
baseUrl: BASE_API_URL,
|
||||
prepareHeaders: (headers) => {
|
||||
const { token, sessionHeaders } = resolveSessionContext(
|
||||
api.getState() as { auth?: { token?: string } },
|
||||
);
|
||||
if (token) headers.set('Authorization', `Bearer ${token}`);
|
||||
Object.entries(sessionHeaders).forEach(([k, v]) => headers.set(k, v));
|
||||
return headers;
|
||||
},
|
||||
});
|
||||
|
||||
let result = await baseQuery(args, api, extraOptions);
|
||||
|
||||
if (result.error?.status === 401) {
|
||||
if (_onTokenExpired) {
|
||||
try {
|
||||
await _onTokenExpired();
|
||||
result = await baseQuery(args, api, extraOptions);
|
||||
} catch {
|
||||
_onAuthFailure?.();
|
||||
}
|
||||
} else {
|
||||
_onAuthFailure?.();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -1,23 +1,9 @@
|
||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||
import { resolveSessionContext } from '../session';
|
||||
|
||||
const BASE_API_URL =
|
||||
(import.meta as { env?: Record<string, string> }).env?.['VITE_BASE_API_URL'] ??
|
||||
'http://localhost:3001/api';
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
import { baseQueryWithReauth } from './base-query-with-reauth';
|
||||
|
||||
export const baseApi = createApi({
|
||||
reducerPath: 'baseApi',
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: BASE_API_URL,
|
||||
prepareHeaders: (headers, { getState }) => {
|
||||
const { token, sessionHeaders } = resolveSessionContext(
|
||||
getState() as { auth?: { token?: string } },
|
||||
);
|
||||
if (token) headers.set('Authorization', `Bearer ${token}`);
|
||||
Object.entries(sessionHeaders).forEach(([k, v]) => headers.set(k, v));
|
||||
return headers;
|
||||
},
|
||||
}),
|
||||
baseQuery: baseQueryWithReauth,
|
||||
tagTypes: ['Api'],
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export { AuthConfigProvider, useAuthConfig } from './lib/AuthConfig';
|
||||
export type { AuthConfigValue } from './lib/AuthConfig';
|
||||
export { AuthShell, BrandMark } from './lib/components/AuthShell';
|
||||
export { ProtectedRoute } from './lib/components/ProtectedRoute';
|
||||
export { LoginPage } from './lib/pages/LoginPage';
|
||||
export { SignupPage } from './lib/pages/SignupPage';
|
||||
export { ForgotPasswordPage } from './lib/pages/ForgotPasswordPage';
|
||||
@@ -8,4 +9,5 @@ export { OTPVerificationPage } from './lib/pages/OTPVerificationPage';
|
||||
export { authReducer, loginSuccess, setUser, logout, hydrateAuth } from './lib/store/auth.slice';
|
||||
export { signupReducer, setSignupData, setSignupStep, resetSignup } from './lib/store/signup.slice';
|
||||
export { configureAuthStorage, authStorage } from './lib/utils/auth-storage';
|
||||
export { refreshAccessToken } from './lib/utils/refresh-token';
|
||||
export type { AuthUser, AuthState, LoginPayload } from './lib/types/auth.types';
|
||||
|
||||
24
libs/auth/src/lib/components/ProtectedRoute.tsx
Normal file
24
libs/auth/src/lib/components/ProtectedRoute.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Navigate, Outlet, useLocation } from 'react-router-dom';
|
||||
import type { ReactNode } from 'react';
|
||||
import { authStorage } from '../utils/auth-storage';
|
||||
|
||||
interface ProtectedRouteProps {
|
||||
children?: ReactNode;
|
||||
loginPath?: string;
|
||||
}
|
||||
|
||||
function getTokenFromCookie(): string | undefined {
|
||||
const match = document.cookie.match(/(?:^|;\s*)auth-token=([^;]*)/);
|
||||
return match ? decodeURIComponent(match[1]) : undefined;
|
||||
}
|
||||
|
||||
export function ProtectedRoute({ children, loginPath = '/login' }: ProtectedRouteProps) {
|
||||
const location = useLocation();
|
||||
const token = authStorage.getToken() ?? getTokenFromCookie();
|
||||
|
||||
if (!token) {
|
||||
return <Navigate to={loginPath} state={{ from: location }} replace />;
|
||||
}
|
||||
|
||||
return children ? <>{children}</> : <Outlet />;
|
||||
}
|
||||
@@ -25,5 +25,7 @@ export const authStorage = {
|
||||
[key('auth-token'), key('refresh-token'), key('auth-user')].forEach((k) =>
|
||||
localStorage.removeItem(k),
|
||||
);
|
||||
document.cookie =
|
||||
'auth-token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; SameSite=Lax';
|
||||
},
|
||||
};
|
||||
|
||||
31
libs/auth/src/lib/utils/refresh-token.ts
Normal file
31
libs/auth/src/lib/utils/refresh-token.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { authStorage } from './auth-storage';
|
||||
|
||||
const BASE_API_URL =
|
||||
(import.meta as { env?: Record<string, string> }).env?.['VITE_BASE_API_URL'] ??
|
||||
'http://localhost:3001/api';
|
||||
|
||||
interface RefreshResponse {
|
||||
token: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export async function refreshAccessToken(): Promise<string> {
|
||||
const refreshToken = authStorage.getRefreshToken();
|
||||
if (!refreshToken) throw new Error('No refresh token available');
|
||||
|
||||
const response = await fetch(`${BASE_API_URL}/auth/refresh`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ refreshToken }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
authStorage.clear();
|
||||
throw new Error('Token refresh failed');
|
||||
}
|
||||
|
||||
const data: RefreshResponse = await response.json();
|
||||
authStorage.setToken(data.token);
|
||||
if (data.refreshToken) authStorage.setRefreshToken(data.refreshToken);
|
||||
return data.token;
|
||||
}
|
||||
Reference in New Issue
Block a user