Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 10 additions & 48 deletions src/components/features/auth/AuthProvider.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { useState, useEffect, useCallback } from 'react';
import {
signInWithPopup,
signInWithRedirect,
getRedirectResult,
signOut,
onAuthStateChanged,
} from 'firebase/auth';
import { signInWithPopup, signOut, onAuthStateChanged } from 'firebase/auth';
import { auth, googleProvider } from '../../../services/firebase';
import { validateInvestor, loginWithEmailPassword as apiLoginEmail } from '../../../services/api';

Expand Down Expand Up @@ -72,21 +66,6 @@ export const AuthProvider = ({ children }) => {
setLoading(false);
}

getRedirectResult(auth)
.then(async (result) => {
if (result?.user) {
const errorMessage = await validateAndReject(result.user.email);
if (errorMessage) {
setValidationError(errorMessage);
setIsValidated(true);
await signOut(auth);
} else {
setIsValidated(true);
}
}
})
.catch(() => {});

const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
if (currentUser) {
const session = getStoredSession();
Expand All @@ -107,38 +86,21 @@ export const AuthProvider = ({ children }) => {
return unsubscribe;
}, []);

const isDev = import.meta.env.DEV;

const loginWithGoogle = async () => {
setValidationError(null);
setIsValidated(false);

if (isDev) {
try {
const result = await signInWithPopup(auth, googleProvider);
const errorMessage = await validateAndReject(result.user.email);
if (errorMessage) {
setValidationError(errorMessage);
setIsValidated(true);
await signOut(auth);
return { user: null, error: { code: 'auth/unauthorized', message: errorMessage } };
}
try {
const result = await signInWithPopup(auth, googleProvider);
const errorMessage = await validateAndReject(result.user.email);
if (errorMessage) {
setValidationError(errorMessage);
setIsValidated(true);
return { user: result.user, error: null };
} catch (error) {
return {
user: null,
error: {
code: error?.code ?? 'auth/unknown',
message: error?.message ?? 'Unknown authentication error',
},
};
await signOut(auth);
return { user: null, error: { code: 'auth/unauthorized', message: errorMessage } };
}
}

try {
await signInWithRedirect(auth, googleProvider);
return { user: null, error: null };
setIsValidated(true);
return { user: result.user, error: null };
} catch (error) {
return {
user: null,
Expand Down
54 changes: 4 additions & 50 deletions src/components/features/auth/AuthProvider.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AuthProvider } from './AuthProvider';
import { AuthContext } from './AuthContext';

import { signInWithPopup, getRedirectResult, signOut, onAuthStateChanged } from 'firebase/auth';
import { signInWithPopup, signOut, onAuthStateChanged } from 'firebase/auth';

vi.mock('firebase/auth', () => ({
signInWithPopup: vi.fn(),
signInWithRedirect: vi.fn(),
getRedirectResult: vi.fn(() => Promise.resolve(null)),
signOut: vi.fn(),
onAuthStateChanged: vi.fn(),
}));
Expand Down Expand Up @@ -45,7 +43,6 @@ describe('AuthProvider', () => {
beforeEach(() => {
vi.clearAllMocks();
globalThis.localStorage?.clear();
getRedirectResult.mockResolvedValue(null);
});

it('sets user from onAuthStateChanged and stops loading', async () => {
Expand All @@ -64,7 +61,7 @@ describe('AuthProvider', () => {
expect(screen.getByText('test@example.com')).toBeInTheDocument();
});

it('loginWithGoogle calls signInWithPopup in dev (import.meta.env.DEV=true)', async () => {
it('loginWithGoogle calls signInWithPopup and validates investor', async () => {
onAuthStateChanged.mockImplementation((_auth, cb) => {
cb(null);
return () => {};
Expand All @@ -82,6 +79,7 @@ describe('AuthProvider', () => {
await waitFor(() => {
expect(signInWithPopup).toHaveBeenCalledTimes(1);
});
expect(validateInvestor).toHaveBeenCalledWith('a@b.com');
});

it('loginWithEmail stores session and sets user on success', async () => {
Expand Down Expand Up @@ -268,7 +266,7 @@ describe('AuthProvider', () => {
});
});

it('loginWithGoogle returns error when popup throws generic error', async () => {
it('loginWithGoogle returns error when popup throws', async () => {
signInWithPopup.mockRejectedValueOnce(new Error('Network error'));
onAuthStateChanged.mockImplementation((_auth, cb) => {
cb(null);
Expand All @@ -287,48 +285,4 @@ describe('AuthProvider', () => {
expect(signInWithPopup).toHaveBeenCalledTimes(1);
});
});

it('handles redirect result and validates investor on mount', async () => {
getRedirectResult.mockResolvedValueOnce({ user: { email: 'redirect@example.com' } });
validateInvestor.mockResolvedValueOnce({ valid: true });
onAuthStateChanged.mockImplementation((_auth, cb) => {
cb({ email: 'redirect@example.com' });
return () => {};
});

render(
<AuthProvider>
<ContextConsumer />
</AuthProvider>,
);

await waitFor(() => {
expect(validateInvestor).toHaveBeenCalledWith('redirect@example.com');
});
});

it('handles redirect result with invalid investor', async () => {
getRedirectResult.mockResolvedValueOnce({ user: { email: 'bad@example.com' } });
validateInvestor.mockResolvedValueOnce({
valid: false,
error: 'Investor not found in database',
});
onAuthStateChanged.mockImplementation((_auth, cb) => {
cb(null);
return () => {};
});

render(
<AuthProvider>
<ContextConsumer />
</AuthProvider>,
);

await waitFor(() => {
expect(screen.getByTestId('validation-error')).toHaveTextContent(
/No estás registrado como inversor/,
);
});
expect(signOut).toHaveBeenCalled();
});
});
Loading