remove dumb auth
This commit is contained in:
@@ -1,115 +1,11 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { setupAuthInterceptors, TOKEN_KEY } from './authInterceptor';
|
||||
|
||||
type RequestConfig = {
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
|
||||
type ResponseValue = {
|
||||
status?: number;
|
||||
data?: unknown;
|
||||
};
|
||||
|
||||
type ResponseError = {
|
||||
response?: {
|
||||
status?: number;
|
||||
};
|
||||
};
|
||||
|
||||
function createMockAxiosInstance() {
|
||||
let nextRequestId = 1;
|
||||
let nextResponseId = 1;
|
||||
|
||||
const requestHandlers = new Map<
|
||||
number,
|
||||
[(config: RequestConfig) => RequestConfig, (error: unknown) => Promise<never>]
|
||||
>();
|
||||
const responseHandlers = new Map<
|
||||
number,
|
||||
[(response: ResponseValue) => ResponseValue, (error: ResponseError) => Promise<never>]
|
||||
>();
|
||||
|
||||
return {
|
||||
interceptors: {
|
||||
request: {
|
||||
use: vi.fn((fulfilled, rejected) => {
|
||||
const id = nextRequestId++;
|
||||
requestHandlers.set(id, [fulfilled, rejected]);
|
||||
return id;
|
||||
}),
|
||||
eject: vi.fn((id: number) => {
|
||||
requestHandlers.delete(id);
|
||||
}),
|
||||
},
|
||||
response: {
|
||||
use: vi.fn((fulfilled, rejected) => {
|
||||
const id = nextResponseId++;
|
||||
responseHandlers.set(id, [fulfilled, rejected]);
|
||||
return id;
|
||||
}),
|
||||
eject: vi.fn((id: number) => {
|
||||
responseHandlers.delete(id);
|
||||
}),
|
||||
},
|
||||
},
|
||||
getRequestHandler(id = 1) {
|
||||
return requestHandlers.get(id);
|
||||
},
|
||||
getResponseHandler(id = 1) {
|
||||
return responseHandlers.get(id);
|
||||
},
|
||||
};
|
||||
}
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { setupAuthInterceptors } from './authInterceptor';
|
||||
|
||||
describe('setupAuthInterceptors', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
it('is a no-op when auth is handled by HttpOnly cookies', () => {
|
||||
const cleanup = setupAuthInterceptors();
|
||||
|
||||
it('registers request and response interceptors and adds the auth header when a token exists', () => {
|
||||
const axiosInstance = createMockAxiosInstance();
|
||||
|
||||
setupAuthInterceptors(axiosInstance as never);
|
||||
|
||||
expect(axiosInstance.interceptors.request.use).toHaveBeenCalledTimes(1);
|
||||
expect(axiosInstance.interceptors.response.use).toHaveBeenCalledTimes(1);
|
||||
|
||||
localStorage.setItem(TOKEN_KEY, 'token-123');
|
||||
|
||||
const requestHandler = axiosInstance.getRequestHandler()?.[0];
|
||||
const config: { headers: Record<string, string> } = { headers: {} };
|
||||
const nextConfig = requestHandler?.(config);
|
||||
|
||||
expect(nextConfig).toBe(config);
|
||||
expect(config.headers.Authorization).toBe('Bearer token-123');
|
||||
});
|
||||
|
||||
it('clears the auth token on 401 responses', async () => {
|
||||
const axiosInstance = createMockAxiosInstance();
|
||||
setupAuthInterceptors(axiosInstance as never);
|
||||
|
||||
localStorage.setItem(TOKEN_KEY, 'token-123');
|
||||
|
||||
const responseErrorHandler = axiosInstance.getResponseHandler()?.[1];
|
||||
|
||||
await expect(responseErrorHandler?.({ response: { status: 401 } })).rejects.toEqual({
|
||||
response: { status: 401 },
|
||||
});
|
||||
expect(localStorage.getItem(TOKEN_KEY)).toBeNull();
|
||||
});
|
||||
|
||||
it('ejects previous interceptors before installing a new set', () => {
|
||||
const firstInstance = createMockAxiosInstance();
|
||||
const secondInstance = createMockAxiosInstance();
|
||||
|
||||
const cleanup = setupAuthInterceptors(firstInstance as never);
|
||||
setupAuthInterceptors(secondInstance as never);
|
||||
|
||||
expect(firstInstance.interceptors.request.eject).toHaveBeenCalledWith(1);
|
||||
expect(firstInstance.interceptors.response.eject).toHaveBeenCalledWith(1);
|
||||
|
||||
cleanup();
|
||||
expect(firstInstance.interceptors.request.eject).toHaveBeenCalledWith(1);
|
||||
expect(firstInstance.interceptors.response.eject).toHaveBeenCalledWith(1);
|
||||
expect(typeof cleanup).toBe('function');
|
||||
expect(() => cleanup()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,46 +1,3 @@
|
||||
import axios, { type AxiosInstance } from 'axios';
|
||||
|
||||
const TOKEN_KEY = 'antholume_token';
|
||||
|
||||
let interceptorCleanup: (() => void) | null = null;
|
||||
|
||||
export function setupAuthInterceptors(axiosInstance: AxiosInstance = axios) {
|
||||
if (interceptorCleanup) {
|
||||
interceptorCleanup();
|
||||
}
|
||||
|
||||
const requestInterceptorId = axiosInstance.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem(TOKEN_KEY);
|
||||
if (token && config.headers) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
const responseInterceptorId = axiosInstance.interceptors.response.use(
|
||||
response => {
|
||||
return response;
|
||||
},
|
||||
error => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
interceptorCleanup = () => {
|
||||
axiosInstance.interceptors.request.eject(requestInterceptorId);
|
||||
axiosInstance.interceptors.response.eject(responseInterceptorId);
|
||||
};
|
||||
|
||||
return interceptorCleanup;
|
||||
export function setupAuthInterceptors() {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export { TOKEN_KEY };
|
||||
export default axios;
|
||||
|
||||
@@ -2,14 +2,11 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import axios from 'axios';
|
||||
import { ToastProvider } from './components/ToastContext';
|
||||
import { setupAuthInterceptors } from './auth/authInterceptor';
|
||||
import { ThemeProvider, initializeThemeMode } from './theme/ThemeProvider';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
setupAuthInterceptors(axios);
|
||||
initializeThemeMode();
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
|
||||
Reference in New Issue
Block a user