diff --git a/packages/use-auth-client/src/use-auth-client.ts b/packages/use-auth-client/src/use-auth-client.ts index 05d0d238..6d0619ea 100644 --- a/packages/use-auth-client/src/use-auth-client.ts +++ b/packages/use-auth-client/src/use-auth-client.ts @@ -5,7 +5,35 @@ import { AuthClientLoginOptions, InternetIdentityAuthResponseSuccess, } from '@dfinity/auth-client'; -import { Identity } from '@dfinity/agent'; +import { + type Identity, + type Agent, + type HttpAgentOptions, + type ActorConfig, + HttpAgent, + Actor, +} from '@dfinity/agent'; +import type { IDL } from '@dfinity/candid'; +import { Principal } from '@dfinity/principal'; + +export interface CreateActorOptions { + /** + * @see {@link Agent} + */ + agent?: Agent; + /** + * @see {@link HttpAgentOptions} + */ + agentOptions?: HttpAgentOptions; + /** + * @see {@link ActorConfig} + */ + actorOptions?: ActorConfig; + + idlFactory: IDL.InterfaceFactory; + + canisterId: Principal | string; +} /** * Options for the useAuthClient hook @@ -19,6 +47,10 @@ export type UseAuthClientOptions = { * Options passed during the login of the auth client */ loginOptions?: AuthClientLoginOptions; + /** + * Options to create an actor using the auth client identity + */ + actorOptions?: CreateActorOptions; }; /** @@ -31,17 +63,39 @@ export type UseAuthClientOptions = { export function useAuthClient(options?: UseAuthClientOptions) { const [authClient, setAuthClient] = React.useState(null); const [identity, setIdentity] = React.useState(null); + const [actor, setActor] = React.useState(null); const [isAuthenticated, setIsAuthenticated] = React.useState(false); // load the auth client on mount React.useEffect(() => { - AuthClient.create(options?.createOptions).then(async client => { + AuthClient.create({ + ...options?.createOptions, + idleOptions: { + ...options?.createOptions?.idleOptions, + onIdle: + options?.createOptions?.idleOptions?.onIdle ?? + (() => { + logout(); + }), + }, + }).then(async client => { setAuthClient(client); setIdentity(client.getIdentity()); setIsAuthenticated(await client.isAuthenticated()); }); }, []); + React.useEffect(() => { + if (identity && options?.actorOptions) { + createActor({ + ...options.actorOptions, + agentOptions: { ...options?.actorOptions?.agentOptions, identity }, + }).then(actor => { + setActor(actor); + }); + } + }, [identity]); + /** * Login through your configured identity provider * Wraps the onSuccess and onError callbacks with promises for convenience @@ -78,10 +132,12 @@ export function useAuthClient(options?: UseAuthClientOptions) { setIsAuthenticated(false); setIdentity(null); await authClient.logout(); + setActor(await createActor(options?.actorOptions)); } } return { + actor, authClient, identity, isAuthenticated, @@ -89,3 +145,28 @@ export function useAuthClient(options?: UseAuthClientOptions) { logout, }; } + +const createActor = async (options: CreateActorOptions) => { + const agent = options.agent || (await HttpAgent.create({ ...options.agentOptions })); + + if (options.agent && options.agentOptions) { + console.warn( + 'Detected both agent and agentOptions passed to createActor. Ignoring agentOptions and proceeding with the provided agent.', + ); + } + + // Fetch root key for certificate validation during development + if (process.env.DFX_NETWORK !== 'ic') { + agent.fetchRootKey().catch(err => { + console.warn('Unable to fetch root key. Check to ensure that your local replica is running'); + console.error(err); + }); + } + + // Creates an actor with using the candid interface and the HttpAgent + return Actor.createActor(options.idlFactory, { + agent, + canisterId: options.canisterId, + ...options.actorOptions, + }); +};