refactor: move web auth onto shared platform provider

This commit is contained in:
Saravana Achu Mac 2026-04-04 13:59:13 -07:00
parent d78aeeffc2
commit a4fce709f0
10 changed files with 150 additions and 99 deletions

View File

@ -13,6 +13,7 @@
}, },
"dependencies": { "dependencies": {
"@bytelyst/kill-switch-client": "link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client", "@bytelyst/kill-switch-client": "link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client",
"@bytelyst/react-auth": "link:../../learning_ai/learning_ai_common_plat/packages/react-auth",
"@bytelyst/react-native-platform-sdk": "link:../../learning_ai/learning_ai_common_plat/packages/react-native-platform-sdk", "@bytelyst/react-native-platform-sdk": "link:../../learning_ai/learning_ai_common_plat/packages/react-native-platform-sdk",
"@bytelyst/telemetry-client": "link:../../learning_ai/learning_ai_common_plat/packages/telemetry-client" "@bytelyst/telemetry-client": "link:../../learning_ai/learning_ai_common_plat/packages/telemetry-client"
}, },

6
pnpm-lock.yaml generated
View File

@ -11,6 +11,9 @@ importers:
'@bytelyst/kill-switch-client': '@bytelyst/kill-switch-client':
specifier: link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client specifier: link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client
version: link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client version: link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client
'@bytelyst/react-auth':
specifier: link:../../learning_ai/learning_ai_common_plat/packages/react-auth
version: link:../../learning_ai/learning_ai_common_plat/packages/react-auth
'@bytelyst/react-native-platform-sdk': '@bytelyst/react-native-platform-sdk':
specifier: link:../../learning_ai/learning_ai_common_plat/packages/react-native-platform-sdk specifier: link:../../learning_ai/learning_ai_common_plat/packages/react-native-platform-sdk
version: link:../../learning_ai/learning_ai_common_plat/packages/react-native-platform-sdk version: link:../../learning_ai/learning_ai_common_plat/packages/react-native-platform-sdk
@ -233,6 +236,9 @@ importers:
'@bytelyst/kill-switch-client': '@bytelyst/kill-switch-client':
specifier: link:../../../learning_ai/learning_ai_common_plat/packages/kill-switch-client specifier: link:../../../learning_ai/learning_ai_common_plat/packages/kill-switch-client
version: link:../../../learning_ai/learning_ai_common_plat/packages/kill-switch-client version: link:../../../learning_ai/learning_ai_common_plat/packages/kill-switch-client
'@bytelyst/react-auth':
specifier: link:../../../learning_ai/learning_ai_common_plat/packages/react-auth
version: link:../../../learning_ai/learning_ai_common_plat/packages/react-auth
'@bytelyst/telemetry-client': '@bytelyst/telemetry-client':
specifier: link:../../../learning_ai/learning_ai_common_plat/packages/telemetry-client specifier: link:../../../learning_ai/learning_ai_common_plat/packages/telemetry-client
version: link:../../../learning_ai/learning_ai_common_plat/packages/telemetry-client version: link:../../../learning_ai/learning_ai_common_plat/packages/telemetry-client

View File

@ -19,6 +19,7 @@
}, },
"dependencies": { "dependencies": {
"@bytelyst/kill-switch-client": "link:../../../learning_ai/learning_ai_common_plat/packages/kill-switch-client", "@bytelyst/kill-switch-client": "link:../../../learning_ai/learning_ai_common_plat/packages/kill-switch-client",
"@bytelyst/react-auth": "link:../../../learning_ai/learning_ai_common_plat/packages/react-auth",
"@bytelyst/telemetry-client": "link:../../../learning_ai/learning_ai_common_plat/packages/telemetry-client", "@bytelyst/telemetry-client": "link:../../../learning_ai/learning_ai_common_plat/packages/telemetry-client",
"@supabase/supabase-js": "^2.90.1", "@supabase/supabase-js": "^2.90.1",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",

View File

@ -7,29 +7,37 @@ import { tableNameProfiles, tableNameUsers } from '../lib/const';
const { const {
getSessionMock, getSessionMock,
onAuthStateChangeMock,
signOutMock, signOutMock,
fromMock, fromMock,
usersSingleMock, usersSingleMock,
profilesLimitMock, profilesLimitMock,
profilesInsertMock, profilesInsertMock,
unsubscribeMock unsubscribeMock,
tradingAuthState
} = vi.hoisted(() => ({ } = vi.hoisted(() => ({
getSessionMock: vi.fn(), getSessionMock: vi.fn(),
onAuthStateChangeMock: vi.fn(),
signOutMock: vi.fn(), signOutMock: vi.fn(),
fromMock: vi.fn(), fromMock: vi.fn(),
usersSingleMock: vi.fn(), usersSingleMock: vi.fn(),
profilesLimitMock: vi.fn(), profilesLimitMock: vi.fn(),
profilesInsertMock: vi.fn(), profilesInsertMock: vi.fn(),
unsubscribeMock: vi.fn() unsubscribeMock: vi.fn(),
tradingAuthState: {
user: { id: 'user-1', email: 'sarah@example.com', role: 'admin', name: 'Sarah Algo' } as any,
isLoading: false,
logout: vi.fn()
}
}));
vi.mock('../lib/tradingAuth', () => ({
TradingAuthProvider: ({ children }: { children: React.ReactNode }) => children,
useTradingAuth: () => tradingAuthState
})); }));
vi.mock('../lib/supabaseClient', () => ({ vi.mock('../lib/supabaseClient', () => ({
supabase: { supabase: {
auth: { auth: {
getSession: getSessionMock, getSession: getSessionMock,
onAuthStateChange: onAuthStateChangeMock,
signOut: signOutMock signOut: signOutMock
}, },
from: fromMock from: fromMock
@ -52,19 +60,15 @@ const Probe = () => {
describe('AuthContext DOM behavior', () => { describe('AuthContext DOM behavior', () => {
beforeEach(() => { beforeEach(() => {
getSessionMock.mockReset(); getSessionMock.mockReset();
onAuthStateChangeMock.mockReset();
signOutMock.mockReset(); signOutMock.mockReset();
fromMock.mockReset(); fromMock.mockReset();
usersSingleMock.mockReset(); usersSingleMock.mockReset();
profilesLimitMock.mockReset(); profilesLimitMock.mockReset();
profilesInsertMock.mockReset(); profilesInsertMock.mockReset();
unsubscribeMock.mockReset(); unsubscribeMock.mockReset();
tradingAuthState.user = { id: 'user-1', email: 'sarah@example.com', role: 'admin', name: 'Sarah Algo' };
onAuthStateChangeMock.mockReturnValue({ tradingAuthState.isLoading = false;
data: { tradingAuthState.logout.mockReset();
subscription: { unsubscribe: unsubscribeMock }
}
});
signOutMock.mockResolvedValue({ error: null }); signOutMock.mockResolvedValue({ error: null });
usersSingleMock.mockResolvedValue({ usersSingleMock.mockResolvedValue({
@ -115,7 +119,7 @@ describe('AuthContext DOM behavior', () => {
}); });
const dispatchSpy = vi.spyOn(window, 'dispatchEvent'); const dispatchSpy = vi.spyOn(window, 'dispatchEvent');
const { unmount } = render( render(
<AuthProvider> <AuthProvider>
<Probe /> <Probe />
</AuthProvider> </AuthProvider>
@ -133,12 +137,10 @@ describe('AuthContext DOM behavior', () => {
]); ]);
expect(dispatchSpy).toHaveBeenCalled(); expect(dispatchSpy).toHaveBeenCalled();
expect((dispatchSpy.mock.calls[0][0] as Event).type).toBe('profiles-updated'); expect((dispatchSpy.mock.calls[0][0] as Event).type).toBe('profiles-updated');
unmount();
expect(unsubscribeMock).toHaveBeenCalledTimes(1);
}, 20000); }, 20000);
it('handles no initial session gracefully', async () => { it('handles no initial session gracefully', async () => {
tradingAuthState.user = null;
getSessionMock.mockResolvedValue({ data: { session: null } }); getSessionMock.mockResolvedValue({ data: { session: null } });
render(<AuthProvider><Probe /></AuthProvider>); render(<AuthProvider><Probe /></AuthProvider>);
@ -151,15 +153,14 @@ describe('AuthContext DOM behavior', () => {
it('handles auth state changes with no session', async () => { it('handles auth state changes with no session', async () => {
getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
render(<AuthProvider><Probe /></AuthProvider>); const { rerender } = render(<AuthProvider><Probe /></AuthProvider>);
await waitFor(() => { await waitFor(() => {
expect(screen.getByTestId('user')).toHaveTextContent('u1'); expect(screen.getByTestId('user')).toHaveTextContent('u1');
}); });
const authChangeCb = onAuthStateChangeMock.mock.calls[0][0]; tradingAuthState.user = null;
// Simulate sign out event rerender(<AuthProvider><Probe /></AuthProvider>);
authChangeCb('SIGNED_OUT', null);
await waitFor(() => { await waitFor(() => {
expect(screen.getByTestId('user')).toHaveTextContent('none'); expect(screen.getByTestId('user')).toHaveTextContent('none');
@ -169,6 +170,7 @@ describe('AuthContext DOM behavior', () => {
it('logs error when profile fetch fails', async () => { it('logs error when profile fetch fails', async () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { }); const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' };
getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
usersSingleMock.mockResolvedValue({ data: null, error: { message: 'Profile Not Found' } }); usersSingleMock.mockResolvedValue({ data: null, error: { message: 'Profile Not Found' } });
@ -182,6 +184,7 @@ describe('AuthContext DOM behavior', () => {
it('handles unexpected errors in fetchProfile', async () => { it('handles unexpected errors in fetchProfile', async () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { }); const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' };
getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
usersSingleMock.mockImplementation(() => { throw new Error('Crashed'); }); usersSingleMock.mockImplementation(() => { throw new Error('Crashed'); });
@ -195,6 +198,7 @@ describe('AuthContext DOM behavior', () => {
it('handles unexpected errors in ensureDefaultProfile', async () => { it('handles unexpected errors in ensureDefaultProfile', async () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { }); const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' };
getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
profilesLimitMock.mockImplementation(() => { throw new Error('Limit Error'); }); profilesLimitMock.mockImplementation(() => { throw new Error('Limit Error'); });

View File

@ -8,6 +8,15 @@ import {
useAuth useAuth
} from './AuthContext'; } from './AuthContext';
vi.mock('../lib/tradingAuth', () => ({
TradingAuthProvider: ({ children }: { children: React.ReactNode }) => children,
useTradingAuth: () => ({
user: null,
isLoading: false,
logout: vi.fn(),
})
}));
vi.mock('../lib/supabaseClient', () => { vi.mock('../lib/supabaseClient', () => {
const query: any = { const query: any = {
select: vi.fn(() => query), select: vi.fn(() => query),
@ -21,9 +30,6 @@ vi.mock('../lib/supabaseClient', () => {
supabase: { supabase: {
auth: { auth: {
getSession: vi.fn(async () => ({ data: { session: null } })), getSession: vi.fn(async () => ({ data: { session: null } })),
onAuthStateChange: vi.fn(() => ({
data: { subscription: { unsubscribe: vi.fn() } }
})),
signOut: vi.fn(async () => ({ error: null })) signOut: vi.fn(async () => ({ error: null }))
}, },
from: vi.fn(() => query) from: vi.fn(() => query)

View File

@ -2,6 +2,7 @@ import React, { createContext, useContext, useEffect, useState } from 'react';
import type { User, Session } from '@supabase/supabase-js'; import type { User, Session } from '@supabase/supabase-js';
import { supabase } from '../lib/supabaseClient'; import { supabase } from '../lib/supabaseClient';
import { tableNameUsers, tableNameProfiles } from '../lib/const'; import { tableNameUsers, tableNameProfiles } from '../lib/const';
import { TradingAuthProvider, useTradingAuth } from '../lib/tradingAuth';
// Define the shape of our extended user profile // Define the shape of our extended user profile
export interface UserProfile { export interface UserProfile {
@ -75,39 +76,49 @@ export const buildDefaultProfilePayload = (userId: string) => ({
}); });
export function AuthProvider({ children }: { children: React.ReactNode }) { export function AuthProvider({ children }: { children: React.ReactNode }) {
return (
<TradingAuthProvider>
<AuthBridge>{children}</AuthBridge>
</TradingAuthProvider>
);
}
function AuthBridge({ children }: { children: React.ReactNode }) {
const tradingAuth = useTradingAuth();
const [session, setSession] = useState<Session | null>(null); const [session, setSession] = useState<Session | null>(null);
const [user, setUser] = useState<User | null>(null); const [user, setUser] = useState<User | null>(null);
const [profile, setProfile] = useState<UserProfile | null>(null); const [profile, setProfile] = useState<UserProfile | null>(null);
const [loading, setLoading] = useState(true); const [profileLoading, setProfileLoading] = useState(true);
useEffect(() => { useEffect(() => {
// 1. Get initial session let active = true;
supabase.auth.getSession().then(({ data: { session } }) => { const syncSession = async () => {
setSession((session as Session | null) ?? null); if (!tradingAuth.user?.id) {
setUser((session?.user as User | null) ?? null); if (!active) return;
if (session?.user) { setSession(null);
fetchProfile(session.user.id); setUser(null);
} else {
setLoading(false);
}
});
// 2. Listen for changes
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
setSession((session as Session | null) ?? null);
setUser((session?.user as User | null) ?? null);
if (session?.user) {
fetchProfile(session.user.id);
} else {
setProfile(null); setProfile(null);
setLoading(false); setProfileLoading(false);
return;
} }
});
return () => subscription.unsubscribe(); const { data: { session: nextSession } } = await supabase.auth.getSession();
}, []); if (!active) return;
const normalizedSession = (nextSession as Session | null) ?? null;
const normalizedUser = (normalizedSession?.user as User | null) ?? buildFallbackAuthUser(tradingAuth.user);
setSession(normalizedSession);
setUser(normalizedUser);
await fetchProfile(tradingAuth.user.id, normalizedUser);
};
const fetchProfile = async (userId: string) => { void syncSession();
return () => {
active = false;
};
}, [tradingAuth.user?.id]);
const fetchProfile = async (userId: string, authUserOverride?: User | null) => {
try { try {
const { data, error } = await supabase const { data, error } = await supabase
.from(tableNameUsers) .from(tableNameUsers)
@ -117,7 +128,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
if (error) { if (error) {
console.error('Error fetching user profile:', error); console.error('Error fetching user profile:', error);
setProfile(buildFallbackProfile(user)); setProfile(buildFallbackProfile(authUserOverride ?? user));
ensureDefaultProfile(userId); ensureDefaultProfile(userId);
} else { } else {
setProfile(data as UserProfile); setProfile(data as UserProfile);
@ -126,9 +137,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
} }
} catch (err) { } catch (err) {
console.error('Unexpected error fetching profile:', err); console.error('Unexpected error fetching profile:', err);
setProfile(buildFallbackProfile(user)); setProfile(buildFallbackProfile(authUserOverride ?? user));
} finally { } finally {
setLoading(false); setProfileLoading(false);
} }
}; };
@ -152,6 +163,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const signOut = async () => { const signOut = async () => {
await supabase.auth.signOut(); await supabase.auth.signOut();
tradingAuth.logout();
setSession(null); setSession(null);
setUser(null); setUser(null);
setProfile(null); setProfile(null);
@ -167,7 +179,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
session, session,
user, user,
profile, profile,
loading, loading: tradingAuth.isLoading || profileLoading,
signOut, signOut,
refreshProfile refreshProfile
}; };
@ -175,6 +187,21 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
} }
const buildFallbackAuthUser = (authUser: { id: string; email?: string; role?: string; name?: string; } | null): User | null => {
if (!authUser?.id) return null;
return {
id: authUser.id,
email: authUser.email || '',
aud: 'authenticated',
app_metadata: {},
user_metadata: {
role: authUser.role || 'member',
displayName: authUser.name || authUser.email || '',
},
created_at: new Date(0).toISOString(),
} as User;
};
export const useAuth = () => { export const useAuth = () => {
const context = useContext(AuthContext); const context = useContext(AuthContext);
if (context === undefined) { if (context === undefined) {

View File

@ -18,6 +18,14 @@ vi.mock('../components/AuthContext', () => ({
}) })
})); }));
vi.mock('../lib/tradingAuth', () => ({
useTradingAuth: () => ({
login: vi.fn(async () => true),
register: vi.fn(async () => true),
error: null,
})
}));
vi.mock('../lib/supabaseClient', () => { vi.mock('../lib/supabaseClient', () => {
const query: any = { const query: any = {
select: vi.fn(() => query), select: vi.fn(() => query),

View File

@ -4,17 +4,26 @@ import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { Login } from './Login'; import { Login } from './Login';
const { signInWithPasswordMock, signUpMock, resetPasswordForEmailMock } = vi.hoisted(() => ({ const { loginMock, registerMock, resetPasswordForEmailMock, tradingAuthState } = vi.hoisted(() => ({
signInWithPasswordMock: vi.fn(), loginMock: vi.fn(),
signUpMock: vi.fn(), registerMock: vi.fn(),
resetPasswordForEmailMock: vi.fn() resetPasswordForEmailMock: vi.fn(),
tradingAuthState: {
error: null as string | null
}
}));
vi.mock('../lib/tradingAuth', () => ({
useTradingAuth: () => ({
login: loginMock,
register: registerMock,
error: tradingAuthState.error,
})
})); }));
vi.mock('../lib/supabaseClient', () => ({ vi.mock('../lib/supabaseClient', () => ({
supabase: { supabase: {
auth: { auth: {
signInWithPassword: signInWithPasswordMock,
signUp: signUpMock,
resetPasswordForEmail: resetPasswordForEmailMock resetPasswordForEmail: resetPasswordForEmailMock
} }
} }
@ -22,16 +31,18 @@ vi.mock('../lib/supabaseClient', () => ({
describe('Login DOM flow', () => { describe('Login DOM flow', () => {
beforeEach(() => { beforeEach(() => {
signInWithPasswordMock.mockReset(); loginMock.mockReset();
signUpMock.mockReset(); registerMock.mockReset();
resetPasswordForEmailMock.mockReset(); resetPasswordForEmailMock.mockReset();
signInWithPasswordMock.mockResolvedValue({ error: null }); tradingAuthState.error = null;
signUpMock.mockResolvedValue({ error: null }); loginMock.mockResolvedValue(true);
registerMock.mockResolvedValue(true);
resetPasswordForEmailMock.mockResolvedValue({ error: null }); resetPasswordForEmailMock.mockResolvedValue({ error: null });
}); });
it('submits sign-in credentials and surfaces auth errors', async () => { it('submits sign-in credentials and surfaces auth errors', async () => {
signInWithPasswordMock.mockResolvedValueOnce({ error: { message: 'Invalid login credentials' } }); tradingAuthState.error = 'Invalid login credentials';
loginMock.mockResolvedValueOnce(false);
const user = userEvent.setup(); const user = userEvent.setup();
render(<Login />); render(<Login />);
@ -41,10 +52,7 @@ describe('Login DOM flow', () => {
await user.click(screen.getByRole('button', { name: 'Sign In' })); await user.click(screen.getByRole('button', { name: 'Sign In' }));
await waitFor(() => { await waitFor(() => {
expect(signInWithPasswordMock).toHaveBeenCalledWith({ expect(loginMock).toHaveBeenCalledWith('user@demo.com', 'bad-password');
email: 'user@demo.com',
password: 'bad-password'
});
expect(screen.getByText('Invalid login credentials')).toBeInTheDocument(); expect(screen.getByText('Invalid login credentials')).toBeInTheDocument();
}); });
@ -65,10 +73,7 @@ describe('Login DOM flow', () => {
await user.click(screen.getByRole('button', { name: 'Sign Up' })); await user.click(screen.getByRole('button', { name: 'Sign Up' }));
await waitFor(() => { await waitFor(() => {
expect(signUpMock).toHaveBeenCalledWith({ expect(registerMock).toHaveBeenCalledWith('new@demo.com', 'StrongPassword1!', 'new');
email: 'new@demo.com',
password: 'StrongPassword1!'
});
expect(screen.getByText('Check your email for the confirmation link!')).toBeInTheDocument(); expect(screen.getByText('Check your email for the confirmation link!')).toBeInTheDocument();
}); });
}); });

View File

@ -1,7 +1,9 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { supabase } from '../lib/supabaseClient'; import { supabase } from '../lib/supabaseClient';
import { useTradingAuth } from '../lib/tradingAuth';
export function Login() { export function Login() {
const tradingAuth = useTradingAuth();
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -24,18 +26,12 @@ export function Login() {
if (error) throw error; if (error) throw error;
setMessage('Password reset link sent! Check your email.'); setMessage('Password reset link sent! Check your email.');
} else if (isSignUp) { } else if (isSignUp) {
const { error } = await supabase.auth.signUp({ const ok = await tradingAuth.register(email, password, email.split('@')[0] || 'Trader');
email, if (!ok) throw new Error(tradingAuth.error || 'Registration failed');
password,
});
if (error) throw error;
setMessage('Check your email for the confirmation link!'); setMessage('Check your email for the confirmation link!');
} else { } else {
const { error } = await supabase.auth.signInWithPassword({ const ok = await tradingAuth.login(email, password);
email, if (!ok) throw new Error(tradingAuth.error || 'Login failed');
password,
});
if (error) throw error;
} }
} catch (err: any) { } catch (err: any) {
setError(err.message); setError(err.message);

View File

@ -1,16 +1,14 @@
import { createAuthProvider, type BaseUser } from '@bytelyst/react-auth'; import { createAuthProvider, type BaseUser } from '@bytelyst/react-auth';
import { getRuntimeEnvironment } from './runtime.js'; import { tradingRuntime } from './runtime';
export interface TradingAuthUser extends BaseUser { export interface TradingAuthUser extends BaseUser {
id: string; id: string;
plan?: string; plan?: string;
} }
const runtime = getRuntimeEnvironment('web');
export const tradingWebAuth = createAuthProvider<TradingAuthUser>({ export const tradingWebAuth = createAuthProvider<TradingAuthUser>({
baseUrl: runtime.platformApiUrl, baseUrl: tradingRuntime.platformApiUrl,
productId: runtime.productId, productId: tradingRuntime.productId,
storagePrefix: 'invttrdg_web', storagePrefix: 'invttrdg_web',
loginEndpoint: '/auth/login', loginEndpoint: '/auth/login',
registerEndpoint: '/auth/register', registerEndpoint: '/auth/register',
@ -18,7 +16,7 @@ export const tradingWebAuth = createAuthProvider<TradingAuthUser>({
changePasswordEndpoint: '/auth/change-password', changePasswordEndpoint: '/auth/change-password',
deleteAccountEndpoint: '/auth/account', deleteAccountEndpoint: '/auth/account',
refreshEndpoint: '/auth/refresh', refreshEndpoint: '/auth/refresh',
mapLoginResponse: data => { mapLoginResponse: (data: unknown) => {
const response = data as { const response = data as {
user: TradingAuthUser; user: TradingAuthUser;
accessToken: string; accessToken: string;
@ -34,4 +32,3 @@ export const tradingWebAuth = createAuthProvider<TradingAuthUser>({
export const TradingAuthProvider = tradingWebAuth.AuthProvider; export const TradingAuthProvider = tradingWebAuth.AuthProvider;
export const useTradingAuth = tradingWebAuth.useAuth; export const useTradingAuth = tradingWebAuth.useAuth;