Internet Identity only works once

I’m trying to make this example code work.

So basically I’ve got a “Log in” button and a “Greet” button. And here’s what’s happening:

  • I click the “Greet” button: it displays “Hello, 2vxsx-fae!”
  • I click “Log in”: another tab opens and I log into my Internet Identity, then the tab closes
  • I click the “Greet” button again: it displays “Hello, a5sm3-2jqyf-of34i-2y4rc-jras6-ll7yf-in4ha-…!”

So far, so good!

But here comes the issue: if I click the “Greet” button a third time, it says “Hello, 2vxsx-fae!” again, as if I was logged out :worried:

I doubt this is the expected behavior. Is there anything else I must do so that the user remains logged in for some time?

Thanks a lot for your help!

I know it’s probably not what you want to hear but, there is probably an issue in your code :wink:.

Unless you have set a session duration to least only few seconds (default duration is 30 minutes), I would not see any other reason why this quirk happens than a race condition or something similar.

Have you double checked that you are accessing the same authenticated object and that you do not trigger some unexpected logout?

1 Like

Sorry did not noticed you shared some code. Are you exactly trying out the example you linked or did you tweaked it?

1 Like

I tweaked it a bit, so you’re probably right. But nothing crazy.

In the backend, I haven’t touched anything:

public query ({ caller }) func greet() : async Text {
    return "Hello, " # Principal.toText(caller) # "!";
};

In the frontend, I’m using React:

import { useState } from 'react';
import { createActor, myapp_backend } from 'declarations/myapp_backend';
import { AuthClient } from "@dfinity/auth-client";
import { HttpAgent } from "@dfinity/agent";

export default function Home() {

    const [greeting, setGreeting] = useState('');
    let actor = myapp_backend;

    function handleSubmit(event) {
        event.preventDefault();
        actor.greet().then((greeting) => {
            setGreeting(greeting);
        });
        return false;
    }

    async function login(event) {
        event.preventDefault();

        let authClient = await AuthClient.create();
        let internetProvider = process.env.DFX_NETWORK == 'local' ? 'http://' + process.env.CANISTER_ID_INTERNET_IDENTITY + '.localhost:4943' : 'https://identity.ic0.app/';

        await new Promise((resolve) => {
            authClient.login({
                identityProvider: internetProvider,
                onSuccess: resolve,
            });
        });

        const identity = authClient.getIdentity();
        const agent = new HttpAgent({identity});
        actor = createActor(process.env.CANISTER_ID_MYAPP_BACKEND, {
            agent,
        });

        return false;
    }

    return (
        <>
        <img src="/logo2.svg" alt="DFINITY logo" />
            <br />
        <br />
        <form action="#" onSubmit={login}>
            <button className="success" id="login">Log in</button>
        </form>
        <br />
        <form action="#" onSubmit={handleSubmit}>
            <label htmlFor="name">Enter your name: &nbsp;</label>
            <input id="name" alt="Name" type="text" />
            <button className="primary" type="submit">Greet</button>
        </form>
        <section id="greeting">{greeting}</section>
        </>
    );
}

See anything wrong?

Once you set state, your component re-renders, thus the actor variable is re-created. Which means it won’t be the actor it was after login with the II identity instance agent.

Put your actor either outside the component, in a ref or state to persist it across renders.

2 Likes

Or the all page is reloaded because the form is submitted. Indeed as said above or a “quick and dirty init just for test purpose only” like:

useEffect(() => {
   const authClient = await AuthClient.create();

        const identity = authClient.getIdentity();
        const agent = new HttpAgent({identity});
   actor = createActor(process.env.CANISTER_ID_MYAPP_BACKEND, {
            agent,
        });
},[]);

i.e. when you create a new authclient, per default, it creates a client for what’s availalbe in indexedb. So if nothing is present, it create an anonymous, if something is present, it create a client for it regardless if the length of the session is over or not.

1 Like

That seems to be avoided with event.preventDefault() at least, it was the first thing on my mind though till I saw the React code posted.

1 Like

Thanks a lot guys. That indeed was the issue! I’ll make sure to keep that in mind in the future :pray:

2 Likes