useAuth additional validation checks: localStorage ton_proof save > use > remove & selectWallet check
This commit is contained in:
parent
46f3b22ec9
commit
1b96d049c7
|
|
@ -232,7 +232,7 @@ export const ViewContentPage = () => {
|
|||
{content?.data?.downloadable && (
|
||||
<Button
|
||||
onClick={() => handleDwnldContent()}
|
||||
className={'h-[48px] bg-darkred mb-4'}
|
||||
className={'h-[48px] mb-4'}
|
||||
label={`Скачать контент`}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,36 @@
|
|||
import { useRef } from 'react';
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { useTonConnectUI } from '@tonconnect/ui-react';
|
||||
import { useMutation } from 'react-query';
|
||||
import { request } from '~/shared/libs';
|
||||
import { useWebApp } from '@vkruglikov/react-telegram-web-app';
|
||||
|
||||
const sessionStorageKey = 'auth_v1_token';
|
||||
const tonProofStorageKey = 'stored_ton_proof';
|
||||
const payloadTTLMS = 1000 * 60 * 20;
|
||||
|
||||
export const useAuth = () => {
|
||||
const WebApp = useWebApp();
|
||||
// const wallet = useTonWallet();
|
||||
const [tonConnectUI] = useTonConnectUI();
|
||||
const interval = useRef<ReturnType<typeof setInterval> | undefined>();
|
||||
|
||||
const waitForWalletProof = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => reject(new Error('Timeout waiting for proof')), 30000);
|
||||
const checkProof = setInterval(() => {
|
||||
const currentWallet = tonConnectUI.wallet;
|
||||
// Store ton_proof when it becomes available
|
||||
useEffect(() => {
|
||||
if (
|
||||
currentWallet?.connectItems?.tonProof &&
|
||||
!('error' in currentWallet.connectItems.tonProof)
|
||||
tonConnectUI.wallet?.connectItems?.tonProof &&
|
||||
!('error' in tonConnectUI.wallet.connectItems.tonProof) &&
|
||||
tonConnectUI.wallet.account
|
||||
) {
|
||||
clearInterval(checkProof);
|
||||
clearTimeout(timeout);
|
||||
resolve(currentWallet.connectItems.tonProof.proof);
|
||||
console.log('DEBUG: Storing ton_proof for future use');
|
||||
localStorage.setItem(
|
||||
tonProofStorageKey,
|
||||
JSON.stringify({
|
||||
timestamp: Date.now(),
|
||||
account: tonConnectUI.wallet.account,
|
||||
proof: tonConnectUI.wallet.connectItems.tonProof.proof,
|
||||
})
|
||||
);
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
}, [tonConnectUI.wallet?.connectItems?.tonProof, tonConnectUI.wallet?.account]);
|
||||
|
||||
const makeAuthRequest = async (params: {
|
||||
twa_data: string;
|
||||
|
|
@ -37,6 +39,7 @@ export const useAuth = () => {
|
|||
ton_proof: any;
|
||||
};
|
||||
}) => {
|
||||
try {
|
||||
const res = await request.post<{
|
||||
connected_wallet: null | {
|
||||
version: string;
|
||||
|
|
@ -48,15 +51,82 @@ export const useAuth = () => {
|
|||
|
||||
if (res?.data?.auth_v1_token) {
|
||||
localStorage.setItem(sessionStorageKey, res.data.auth_v1_token);
|
||||
|
||||
// If we sent a proof, it was accepted, so keep record of that
|
||||
if (params.ton_proof) {
|
||||
console.log('DEBUG: Auth with proof successful');
|
||||
}
|
||||
} else {
|
||||
throw new Error('Failed to get auth token');
|
||||
}
|
||||
return res;
|
||||
} catch (error) {
|
||||
// If we were using ton_proof and it failed, clear stored proof
|
||||
if (params.ton_proof) {
|
||||
console.log('DEBUG: Auth with proof failed, clearing stored proof');
|
||||
localStorage.removeItem(tonProofStorageKey);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const makeSelectWalletRequest = async (params: { wallet_address: string }) => {
|
||||
try {
|
||||
const res = await request.post('/auth.selectWallet', params);
|
||||
return res;
|
||||
} catch (error: any) {
|
||||
// Check for 404 error (wallet not found or invalid)
|
||||
if (error.response?.status === 404) {
|
||||
console.log('DEBUG: Wallet selection failed with 404, disconnecting');
|
||||
await tonConnectUI.disconnect();
|
||||
localStorage.removeItem(sessionStorageKey);
|
||||
localStorage.removeItem(tonProofStorageKey);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to prepare the connection parameters with proof requirements
|
||||
const prepareConnectParams = async () => {
|
||||
console.log('DEBUG: Preparing connect parameters');
|
||||
|
||||
// Set to loading state first
|
||||
tonConnectUI.setConnectRequestParameters({ state: 'loading' });
|
||||
|
||||
try {
|
||||
// Get the payload/token from backend
|
||||
const value = await request.post<{ auth_v1_token: string }>('/auth.twa', {
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
|
||||
if (value?.data?.auth_v1_token) {
|
||||
console.log('DEBUG: Got token for connect params');
|
||||
|
||||
// Set the parameters to ready with tonProof requirement
|
||||
tonConnectUI.setConnectRequestParameters({
|
||||
state: 'ready',
|
||||
value: { tonProof: value.data.auth_v1_token },
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
console.log('DEBUG: No token received for connect params');
|
||||
tonConnectUI.setConnectRequestParameters(null);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('DEBUG: Error preparing connect params:', error);
|
||||
tonConnectUI.setConnectRequestParameters(null);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to check if connection is capable of transactions
|
||||
const isConnectionValid = () => {
|
||||
if (!tonConnectUI.connected || !tonConnectUI.wallet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return useMutation(['auth'], async () => {
|
||||
|
|
@ -64,68 +134,178 @@ export const useAuth = () => {
|
|||
let authResult;
|
||||
console.log('DEBUG: Starting auth flow');
|
||||
|
||||
try {
|
||||
// Case 1: Not connected - need to connect and get proof
|
||||
if (!tonConnectUI.connected) {
|
||||
console.log('DEBUG: No wallet connection, starting flow');
|
||||
localStorage.removeItem(sessionStorageKey);
|
||||
|
||||
const refreshPayload = async () => {
|
||||
tonConnectUI.setConnectRequestParameters({ state: 'loading' });
|
||||
const value = await request.post<{ auth_v1_token: string }>('/auth.twa', {
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
// Prepare connection parameters (this sets up the proof requirement)
|
||||
const prepared = await prepareConnectParams();
|
||||
|
||||
if (value?.data?.auth_v1_token) {
|
||||
tonConnectUI.setConnectRequestParameters({
|
||||
state: 'ready',
|
||||
value: { tonProof: value.data.auth_v1_token },
|
||||
});
|
||||
} else {
|
||||
tonConnectUI.setConnectRequestParameters(null);
|
||||
if (!prepared) {
|
||||
throw new Error('Failed to prepare connection parameters');
|
||||
}
|
||||
};
|
||||
|
||||
await refreshPayload();
|
||||
interval.current = setInterval(refreshPayload, payloadTTLMS);
|
||||
// Start periodic refresh of the payload
|
||||
interval.current = setInterval(prepareConnectParams, payloadTTLMS);
|
||||
|
||||
const tonProof = await waitForWalletProof();
|
||||
console.log('DEBUG: Got initial proof', tonProof);
|
||||
// Open the modal - this will not resolve until connection or cancellation
|
||||
await tonConnectUI.openModal();
|
||||
|
||||
// Check if connection was successful
|
||||
if (!tonConnectUI.connected) {
|
||||
console.log('DEBUG: Connection cancelled or failed');
|
||||
throw new Error('Wallet connection cancelled or failed');
|
||||
}
|
||||
|
||||
// Check if we have a proof after connection
|
||||
if (
|
||||
tonConnectUI.wallet?.connectItems?.tonProof &&
|
||||
!('error' in tonConnectUI.wallet.connectItems.tonProof)
|
||||
) {
|
||||
console.log('DEBUG: Got proof after connection');
|
||||
|
||||
try {
|
||||
// Try auth with the proof
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
ton_proof: {
|
||||
account: tonConnectUI.wallet!.account,
|
||||
ton_proof: tonProof,
|
||||
account: tonConnectUI.wallet.account,
|
||||
ton_proof: tonConnectUI.wallet.connectItems.tonProof.proof,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
// If auth with proof fails, throw the error
|
||||
console.error('DEBUG: Auth with fresh proof failed:', error);
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
// Case 3: Connected without proof - already authenticated
|
||||
console.log('DEBUG: No proof available after connection');
|
||||
// If we can't get proof but we're connected, try auth without it
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Case 2: Already connected - try to use stored proof first
|
||||
console.log('DEBUG: Already connected');
|
||||
|
||||
// Check if we have a valid stored proof
|
||||
const storedProofData = localStorage.getItem(tonProofStorageKey);
|
||||
if (storedProofData) {
|
||||
try {
|
||||
const proofData = JSON.parse(storedProofData);
|
||||
|
||||
// Check if the proof matches current wallet and is not too old
|
||||
if (tonConnectUI.wallet?.account?.address === proofData.account.address) {
|
||||
console.log('DEBUG: Using stored proof');
|
||||
|
||||
// Try auth with stored proof but ignore errors
|
||||
try {
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
ton_proof: {
|
||||
account: proofData.account,
|
||||
ton_proof: proofData.proof,
|
||||
},
|
||||
});
|
||||
|
||||
// If successful, remove stored proof as it's been used
|
||||
localStorage.removeItem(tonProofStorageKey);
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'DEBUG: Auth with stored proof failed, proceeding without it'
|
||||
);
|
||||
|
||||
// Fall back to auth without proof
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('DEBUG: Stored proof address mismatch');
|
||||
localStorage.removeItem(tonProofStorageKey);
|
||||
|
||||
// Auth without proof
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('DEBUG: Error parsing stored proof:', error);
|
||||
localStorage.removeItem(tonProofStorageKey);
|
||||
|
||||
// Auth without proof
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// No stored proof, check if we have a live proof
|
||||
if (
|
||||
tonConnectUI.wallet?.connectItems?.tonProof &&
|
||||
!('error' in tonConnectUI.wallet.connectItems.tonProof)
|
||||
) {
|
||||
console.log('DEBUG: Using live proof from wallet');
|
||||
try {
|
||||
// Try auth with the live proof
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
ton_proof: {
|
||||
account: tonConnectUI.wallet.account,
|
||||
ton_proof: tonConnectUI.wallet.connectItems.tonProof.proof,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'DEBUG: Auth with live proof failed, proceeding without it'
|
||||
);
|
||||
|
||||
// Fall back to auth without proof
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Connected without proof - already authenticated
|
||||
console.log('DEBUG: Connected without proof, proceeding without it');
|
||||
authResult = await makeAuthRequest({
|
||||
twa_data: WebApp.initData,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always try to select wallet after auth (this validates the connection)
|
||||
if (tonConnectUI.wallet?.account?.address) {
|
||||
console.log('DEBUG: Selecting wallet', tonConnectUI.wallet.account.address);
|
||||
await makeSelectWalletRequest({ wallet_address: tonConnectUI.wallet.account.address });
|
||||
try {
|
||||
await makeSelectWalletRequest({
|
||||
wallet_address: tonConnectUI.wallet.account.address,
|
||||
});
|
||||
|
||||
// Additional validation check
|
||||
if (!isConnectionValid()) {
|
||||
console.log('DEBUG: Connection validation failed, disconnecting');
|
||||
await tonConnectUI.disconnect();
|
||||
localStorage.removeItem(sessionStorageKey);
|
||||
localStorage.removeItem(tonProofStorageKey);
|
||||
throw new Error('Connection validation failed');
|
||||
}
|
||||
} catch (error) {
|
||||
// Errors from makeSelectWalletRequest are already handled
|
||||
console.error('DEBUG: Wallet selection failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return authResult;
|
||||
|
||||
// Commented this part for two reasons:
|
||||
// 1) When we include ton_proof from the wallet it fails the call for a reason of bad ton_proof
|
||||
// 2) This call could happen only if the first case happened and it means that the ton_proof is already have been stored once before
|
||||
// Case 2: Connected with proof - use it
|
||||
// if (wallet?.connectItems?.tonProof && !("error" in wallet.connectItems.tonProof)) {
|
||||
// console.log("DEBUG: Using existing proof", wallet.connectItems.tonProof.proof);
|
||||
// return makeAuthRequest({
|
||||
// twa_data: WebApp.initData,
|
||||
// ton_proof: {
|
||||
// account: wallet.account,
|
||||
// ton_proof: wallet.connectItems.tonProof.proof,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
} catch (error) {
|
||||
console.error('DEBUG: Auth flow failed:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
clearInterval(interval.current);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue