Integrating with Internet Identity
May 18, 2021
Hi again! It's been an exciting couple of weeks. In case you missed it, I was a part of the Internet Computer Genesis event, and gave a talk on static site generators based on my blog post from a few weeks back. I've go ahead and added the video to the original page if you want to check that out.
Another topic with a lot of emphasis during the event was the new Internet Identity application. I ended up joining that team, and was heavily involved in getting the first prototype running, as well as managing the JavaScript packages that support the app, and a new package dedicated to integrating with it.
Here's a link to the Github Repository where you can explore the code we'll cover here: https://github.com/krpeacock/auth-client-demo
Back up, what's an Internet Identity?
To start, check out Dom's intro, and my colleague Joachim's presentation from the Genesis event.
The Internet Identity is a new authentication provider, filling a similar niche to the ubiquitous Login with Google or Facebook buttons you will see around the web.
From a user perspective, it is a way to bypass having to plug you information into every new site, so that you can just click a couple buttons and get on with whatever you wanted to do. From a developer perspective, this is another way that you can reduce friction for users on your app, so that you don't have to implement your own authentication strategy. It's a win-win, and that's why these tools are popular!
The Internet Identity at this stage knows almost nothing about you. There is no username or password. You don't provide an email to sign up. There are no seed phrases, One-time-passwords, authenticator apps, or email confirmations. It is built on the browser standard of Web Authentication, which has a significant level of support now and is getting better every year.
The Internet Identity app associates your various devices and gives you an identity that you can use seamlessly across applications once you've set them up.
I'm sold - how do I get started?
Glad to hear it! Getting started is fairly simple, thanks to our npm package, @dfinity/auth-client.
To start, I've set up an extremely simple
// Main.mo
actor {
public shared (msg) func whoami() : async Principal {
msg.caller
};
};
This actor has one method,
Next, we'll set up our frontend logic. I'll share the full script here, and then break down the steps.
// index.ts
import { Actor, HttpAgent } from "@dfinity/agent";
import { AuthClient } from "@dfinity/auth-client";
import idlFactory from "./did";
import type { _SERVICE } from "./did";
import { renderIndex } from "./views";
import { renderLoggedIn } from "./views/loggedIn";
const init = async () => {
const authClient = await AuthClient.create();
if (await authClient.isAuthenticated()) {
handleAuthenticated(authClient);
}
renderIndex();
const loginButton = document.getElementById(
"loginButton"
) as HTMLButtonElement;
loginButton.onclick = async () => {
await authClient.login({
onSuccess: async () => {
handleAuthenticated(authClient);
},
});
};
};
async function handleAuthenticated(authClient: AuthClient) {
const identity = await authClient.getIdentity();
const agent = new HttpAgent({ identity });
console.log(process.env.CANISTER_ID);
const whoami_actor = Actor.createActor<_SERVICE>(idlFactory, {
agent,
canisterId: process.env.CANISTER_ID as string,
});
renderLoggedIn(whoami_actor, authClient);
}
init();
Setting up
Okay, so let's cover the core logic. First, we import the AuthClient from
const authClient = await AuthClient.create();
If the user has previously logged in, by the time that the
if (await authClient.isAuthenticated()) {
handleAuthenticated(authClient);
}
Then, we add an event listener to our login button that dispatches the authClient
loginButton.onclick = async () => {
await authClient.login({
onSuccess: async () => {
handleAuthenticated(authClient);
},
});
};
The login method will open a new window, pointing to https://identity.ic0.app by default. Your user can then go authenticate, and once they complete the flow, the Internet Identity app will send a
Important development note
Signatures from identity.ic0.app won't be accepted on your local replica.
The Internet Identity signs its delegation using the root (public) key of the Internet Computer, and for now the development replica in
await authClient.login({
onSuccess: async () => {
handleAuthenticated(authClient);
},
identityProvider: "http://localhost:8000?canisterId={identity_canister_id}"
});
Using the identity
After logging in with an Internet Identity, the
For this, I've passed the
async function handleAuthenticated(authClient: AuthClient) {
const identity = await authClient.getIdentity();
const agent = new HttpAgent({ identity });
console.log(process.env.CANISTER_ID);
const whoami_actor = Actor.createActor<_SERVICE>(idlFactory, {
agent,
canisterId: process.env.CANISTER_ID as string,
});
renderLoggedIn(whoami_actor, authClient);
}
The
That agent can be used to create an actor (note: the
You can check out the code for that view in https://github.com/krpeacock/auth-client-demo/blob/main/src/frontend/src/views/loggedIn.ts.
Final result after authenticating
Final thoughts
The Internet Identity app still has some growing pains to go through, but I'm very pleased with how simple it is to add it to a web app on the IC. Authentication and protecting PII is always a difficult challenge, and this is an option that will allow users to use your app with a consistent identity without divulging any personal information or trusting you to secure a password for them.
I hope this guide was helpful, and as always, I look forward to seeing what you build!