fix: dumb auth
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
getAuthenticatedAuthState,
|
||||
getUnauthenticatedAuthState,
|
||||
resolveAuthStateFromMe,
|
||||
validateAuthMutationResponse,
|
||||
authUserFromMutation,
|
||||
} from './authHelpers';
|
||||
|
||||
interface AuthContextType extends AuthState {
|
||||
@@ -63,7 +63,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
},
|
||||
});
|
||||
|
||||
const user = validateAuthMutationResponse(response, 200);
|
||||
const user = authUserFromMutation(response);
|
||||
if (!user) {
|
||||
setAuthState(getUnauthenticatedAuthState());
|
||||
throw new Error('Login failed');
|
||||
@@ -91,7 +91,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
},
|
||||
});
|
||||
|
||||
const user = validateAuthMutationResponse(response, 201);
|
||||
const user = authUserFromMutation(response);
|
||||
if (!user) {
|
||||
setAuthState(getUnauthenticatedAuthState());
|
||||
throw new Error('Registration failed');
|
||||
@@ -113,7 +113,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
logoutMutation.mutate(undefined, {
|
||||
onSuccess: async () => {
|
||||
setAuthState(getUnauthenticatedAuthState());
|
||||
await queryClient.removeQueries({ queryKey: getGetMeQueryKey() });
|
||||
queryClient.removeQueries({ queryKey: getGetMeQueryKey() });
|
||||
navigate('/login');
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,9 +2,8 @@ import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
getCheckingAuthState,
|
||||
getUnauthenticatedAuthState,
|
||||
normalizeAuthenticatedUser,
|
||||
resolveAuthStateFromMe,
|
||||
validateAuthMutationResponse,
|
||||
authUserFromMutation,
|
||||
type AuthState,
|
||||
} from './authHelpers';
|
||||
|
||||
@@ -15,20 +14,6 @@ const previousState: AuthState = {
|
||||
};
|
||||
|
||||
describe('authHelpers', () => {
|
||||
it('normalizes a valid authenticated user payload', () => {
|
||||
expect(normalizeAuthenticatedUser({ username: 'evan', is_admin: true })).toEqual({
|
||||
username: 'evan',
|
||||
is_admin: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects invalid authenticated user payloads', () => {
|
||||
expect(normalizeAuthenticatedUser(null)).toBeNull();
|
||||
expect(normalizeAuthenticatedUser({ username: 'evan' })).toBeNull();
|
||||
expect(normalizeAuthenticatedUser({ username: 123, is_admin: true })).toBeNull();
|
||||
expect(normalizeAuthenticatedUser({ username: 'evan', is_admin: 'yes' })).toBeNull();
|
||||
});
|
||||
|
||||
it('returns a checking state while preserving previous auth information', () => {
|
||||
expect(
|
||||
getCheckingAuthState({
|
||||
@@ -49,6 +34,7 @@ describe('authHelpers', () => {
|
||||
meData: {
|
||||
status: 200,
|
||||
data: { username: 'evan', is_admin: false },
|
||||
headers: new Headers(),
|
||||
},
|
||||
meError: undefined,
|
||||
meLoading: false,
|
||||
@@ -66,6 +52,8 @@ describe('authHelpers', () => {
|
||||
resolveAuthStateFromMe({
|
||||
meData: {
|
||||
status: 401,
|
||||
data: { code: 401, message: 'unauthorized' },
|
||||
headers: new Headers(),
|
||||
},
|
||||
meError: undefined,
|
||||
meLoading: false,
|
||||
@@ -105,9 +93,7 @@ describe('authHelpers', () => {
|
||||
it('returns the previous state with checking disabled when there is no decisive me result', () => {
|
||||
expect(
|
||||
resolveAuthStateFromMe({
|
||||
meData: {
|
||||
status: 204,
|
||||
},
|
||||
meData: undefined,
|
||||
meError: undefined,
|
||||
meLoading: false,
|
||||
previousState: {
|
||||
@@ -123,35 +109,31 @@ describe('authHelpers', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('validates auth mutation responses by expected status and payload shape', () => {
|
||||
it('extracts the user from successful login and register responses', () => {
|
||||
expect(
|
||||
validateAuthMutationResponse(
|
||||
{
|
||||
authUserFromMutation({
|
||||
status: 200,
|
||||
data: { username: 'evan', is_admin: false },
|
||||
},
|
||||
200
|
||||
)
|
||||
headers: new Headers(),
|
||||
})
|
||||
).toEqual({ username: 'evan', is_admin: false });
|
||||
|
||||
expect(
|
||||
validateAuthMutationResponse(
|
||||
{
|
||||
authUserFromMutation({
|
||||
status: 201,
|
||||
data: { username: 'evan', is_admin: false },
|
||||
},
|
||||
200
|
||||
)
|
||||
).toBeNull();
|
||||
data: { username: 'evan', is_admin: true },
|
||||
headers: new Headers(),
|
||||
})
|
||||
).toEqual({ username: 'evan', is_admin: true });
|
||||
});
|
||||
|
||||
it('returns null for unsuccessful auth mutation responses', () => {
|
||||
expect(
|
||||
validateAuthMutationResponse(
|
||||
{
|
||||
status: 200,
|
||||
data: { username: 'evan' },
|
||||
},
|
||||
200
|
||||
)
|
||||
authUserFromMutation({
|
||||
status: 401,
|
||||
data: { code: 401, message: 'unauthorized' },
|
||||
headers: new Headers(),
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
export interface AuthUser {
|
||||
username: string;
|
||||
is_admin: boolean;
|
||||
}
|
||||
import type {
|
||||
getMeResponse,
|
||||
loginResponse,
|
||||
registerResponse,
|
||||
} from '../generated/anthoLumeAPIV1';
|
||||
import type { LoginResponse } from '../generated/model';
|
||||
|
||||
export type AuthUser = LoginResponse;
|
||||
|
||||
export interface AuthState {
|
||||
isAuthenticated: boolean;
|
||||
@@ -9,11 +13,6 @@ export interface AuthState {
|
||||
isCheckingAuth: boolean;
|
||||
}
|
||||
|
||||
interface ResponseLike {
|
||||
status?: number;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
export function getUnauthenticatedAuthState(): AuthState {
|
||||
return {
|
||||
isAuthenticated: false,
|
||||
@@ -38,27 +37,8 @@ export function getAuthenticatedAuthState(user: AuthUser): AuthState {
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeAuthenticatedUser(value: unknown): AuthUser | null {
|
||||
if (!value || typeof value !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!('username' in value) || typeof value.username !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!('is_admin' in value) || typeof value.is_admin !== 'boolean') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
username: value.username,
|
||||
is_admin: value.is_admin,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveAuthStateFromMe(params: {
|
||||
meData?: ResponseLike;
|
||||
meData?: getMeResponse;
|
||||
meError?: unknown;
|
||||
meLoading: boolean;
|
||||
previousState: AuthState;
|
||||
@@ -70,10 +50,7 @@ export function resolveAuthStateFromMe(params: {
|
||||
}
|
||||
|
||||
if (meData?.status === 200) {
|
||||
const user = normalizeAuthenticatedUser(meData.data);
|
||||
if (user) {
|
||||
return getAuthenticatedAuthState(user);
|
||||
}
|
||||
return getAuthenticatedAuthState(meData.data);
|
||||
}
|
||||
|
||||
if (meError || meData?.status === 401) {
|
||||
@@ -86,13 +63,8 @@ export function resolveAuthStateFromMe(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export function validateAuthMutationResponse(
|
||||
response: ResponseLike,
|
||||
expectedStatus: number
|
||||
export function authUserFromMutation(
|
||||
response: loginResponse | registerResponse
|
||||
): AuthUser | null {
|
||||
if (response.status !== expectedStatus) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalizeAuthenticatedUser(response.data);
|
||||
return response.status === 200 || response.status === 201 ? response.data : null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user