Hi everyone,
I’ve been facing a recurring “Invalid certificate” error when working locally with the KYC actor on the Internet Computer (IC). The issue arises when I try to interact with the KYC actor, such as checking user existence, fetching usernames, or verifying KYC status.
-
The error appears inconsistently but frequently.
-
It mostly happens after some time of inactivity or after restarting my local environment. function MyNavbar() {
const { isAuthenticated, login, logout, principal, kycActor } = useAuth();
const [principals, setPrincipal] = useState(“”);
const [isOpen, setIsOpen] = useState(false);
const [copied, setCopied] = useState(false);
const navigate = useNavigate(); // Use navigate for redirection
const [userExists, setUserExists] = useState(false); // State to track if user exists
const [isUserChecked, setIsUserChecked] = useState(false); // State to track if the user check is complete
const [username, setUsername] = useState(“”); // State to store the username
const [isDropdownOpen, setIsDropdownOpen] = useState(false);const handleLogin = async () => {
await login();
if (principal) {
try {
const exists = await kycActor.has_username_for_principal(principal);
setUserExists(exists);
setIsUserChecked(true); // Mark the user check as completeif (exists) { const fetchedUsername = await kycActor.get_username_by_principal(principal); // Fetch the username setUsername(fetchedUsername.Ok || "Unknown User"); // Extract the Ok value, handle errors } } catch (error) { console.error("Error checking user existence:", error); console.error("Detailed Error:", error); console.error("Principal:", principal); console.error("KYC Actor:", kycActor); setIsUserChecked(true); // Ensure we handle error state // Handle specific errors if (error.message.includes("Invalid certificate")) { console.error("Certificate error: Please re-authenticate."); // Optionally, prompt the user to log in again await login(); } }
}
};useEffect(() => {
if (isAuthenticated) {
try {
handleLogin();
setPrincipal(principal);
} catch (error) {
console.error(“Failed to fetch principal:”, error);
setPrincipal(“Error fetching principal”);
}
}
}, [isAuthenticated, principal]);// Effect to handle redirection based on user existence
useEffect(() => {
if (isUserChecked) { // Ensure we navigate only after checking the user status
if (!userExists) {
navigate(‘/signup’);
} else {
navigate(‘/’);
}
}
}, [userExists, isUserChecked]); // Depend on userExists and isUserCheckeduseEffect(() => {
if (!isAuthenticated) {
setIsDropdownOpen(false); // Close the dropdown when the user logs out
}
}, [isAuthenticated]);const userInitial = username ? username.charAt(0).toUpperCase() : “U”; ///////////////////this is myuse auth file import { AuthClient } from “@dfinity/auth-client”;
import React, { createContext, useContext, useEffect, useState } from “react”;
import { canisterId as MercxId, createActor as createMercxActor } from “…/…/declarations/mercx_backend”;
import { canisterId, createActor } from “…/…/declarations/icrc1_ledger_canister”;
import { canisterId as icrcIndexCanisterId, createActor as createIndexActor } from “…/…/declarations/icrc1_index_canister”;
import { canisterId as icpCanisterId, createActor as createIcpActor } from “…/…/declarations/icp_ledger_canister”;
import { canisterId as tommyCanisterId, createActor as createTommyActor } from “…/…/declarations/tommy_icrc1_ledger”;
import { canisterId as kycCanisterId, createActor as createKycActor } from “…/…/declarations/kyc”;
import { canisterId as fxmxCanisterId, createActor as createFXMXActor } from “…/…/declarations/fxmx_icrc1_ledger”;
import { canisterId as fxmxIndexId, createActor as createFXMXindexActor } from “…/…/declarations/fxmx_icrc1_index”;
// Create a React Context for sharing authentication status across the component tree
const AuthContext = createContext();
function detectInAppBrowser() {
const ua = navigator.userAgent.toLowerCase();
return (
ua.includes(“linkedin”) ||
ua.includes(“fban”) || ua.includes(“fbav”) || // Facebook
ua.includes(“instagram”) ||
ua.includes(“twitter”) ||
ua.includes(“edge”) || ua.includes(“edg”)
);
}
function handleRedirect() {
if (detectInAppBrowser()) {
if (window.location.pathname !== “/InappBrowser”) {
window.location.href = “/InappBrowser”; // Redirect only if NOT already there
}
}
}
// Function to determine the correct identity provider URL based on environment and browser
export const getIdentityProvider = () => {
let idpProvider;
// Safeguard against server rendering
// Check if the code is running in a browser environment
if (typeof window !== “undefined”) {
// Determine if the environment is local (not production)
const isLocal = process.env.DFX_NETWORK !== “ic”;
// Safari does not support localhost subdomains
// Check if the user’s browser is Safari to handle specific limitations
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isLocal && isSafari) {
// Safari handling for local development environment
idpProvider = http://localhost:8001/?canisterId=${process.env.CANISTER_ID_INTERNET_IDENTITY}
;
} else if (isLocal) {
// General handling for non-Safari browsers in local development
idpProvider = http://${process.env.CANISTER_ID_INTERNET_IDENTITY}.localhost:8001
;
}
}
return idpProvider;
};
// Default options for the authentication client
export const defaultOptions = {
/**
- @type {import(“@dfinity/auth-client”).AuthClientCreateOptions}
/
createOptions: {
idleOptions: {
// Set to true if you do not want idle functionality
// Opt to disable idle functionality for the auth client
disableIdle: true,
},
},
/* - @type {import(“@dfinity/auth-client”).AuthClientLoginOptions}
*/
loginOptions: {
// Specify the identity provider determined by getIdentityProvider
identityProvider: getIdentityProvider(),
},
};
/**
*
- @param options - Options for the AuthClient
- @param {AuthClientCreateOptions} options.createOptions - Options for the AuthClient.create() method
- @param {AuthClientLoginOptions} options.loginOptions - Options for the AuthClient.login() method
- @returns
*/
// Custom hook to manage authentication state
export const useAuthClient = (options = defaultOptions) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [authClient, setAuthClient] = useState(null);
const [identity, setIdentity] = useState(null);
const [principal, setPrincipal] = useState(null);
const [whoamiActor, setWhoamiActor] = useState(null);
const [icrcIndexActor, setIcrcIndexActor] = useState(null);
const [icpActor, setIcpActor] = useState(null);
const [mercx_Actor, setMercxActor] = useState(null);
const [tommy_Actor, setTommyActor] = useState(null);
const [kycActor, setKycActor] = useState(null); //KYC actor
const [fxmxActor, setFXMXActor] = useState(null);
const [fxmxIndexActor, setFXMXindexActor] = useState(null);
useEffect(() => {
handleRedirect();
// openInExternalBrowser();
// Initialize AuthClient
// Create an AuthClient instance when the component mounts
AuthClient.create(options.createOptions).then(async (client) => {
updateClient(client);
});
}, );
// Function to handle user login
const login = () => {
authClient.login({
…options.loginOptions,
onSuccess: () => {
updateClient(authClient);
},
});
};
// Update local state with the new auth client details
async function updateClient(client) {
const isAuthenticated = await client.isAuthenticated();
setIsAuthenticated(isAuthenticated);
const identity = client.getIdentity();
setIdentity(identity);
const principal = identity.getPrincipal();
setPrincipal(principal);
setAuthClient(client);
// Create an actor with the authenticated identity
const actor = createActor(canisterId, {
agentOptions: {
identity,
},
});
setWhoamiActor(actor);
const indexActor = createIndexActor(icrcIndexCanisterId, {
agentOptions: {
identity,
},
});
setIcrcIndexActor(indexActor);
const IcpActor = createIcpActor(icpCanisterId, {
agentOptions: {
identity,
},
});
setIcpActor(IcpActor);
const MercxActor = createMercxActor(MercxId, {
agentOptions: {
identity,
},
});
setMercxActor(MercxActor);
const tommyActor = createTommyActor(tommyCanisterId, {
agentOptions: {
identity,
},
});
setTommyActor(tommyActor);
// ✅ Create the KYC Actor
const kycActor = createKycActor(kycCanisterId, {
agentOptions: {
identity,
},
});
setKycActor(kycActor);
const FxmxActor = createFXMXActor(fxmxCanisterId, {
agentOptions: {
identity,
},
});
setFXMXActor(FxmxActor);
const FxmxindexActor = createFXMXindexActor(fxmxIndexId, {
agentOptions: {
identity,
},
});
setFXMXindexActor(FxmxindexActor);
}
async function logout() {
await authClient?.logout();
await updateClient(authClient);
}
return {
isAuthenticated,
login,
logout,
authClient,
identity,
principal,
whoamiActor,
icrcIndexActor,
icpActor,
mercx_Actor,
tommy_Actor,
kycActor, // Return KYC actor
fxmxActor,
fxmxIndexActor,
};
};
/**
- @type {React.FC}
*/
// Provider component to wrap the application and provide auth state via context
export const AuthProvider = ({ children }) => {
const auth = useAuthClient();
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
};
//A simple hook that allows any component within the context provider to access the auth state and methods easily.
export const useAuth = () => useContext(AuthContext);