NFID
Overview
NFID is a form of digital identity built on ICP. Similar to Internet Identity, NFID allows users to sign into services without downloading an app or extension and can be used across several different dapps.
NFID is different from Internet Identity, however, in the fact that is allows you to sign in to a dapp using your email or Google account. With Internet Identity, these log in options aren't available. This makes NFID a great option for those that want to use email or Google authentication methods.
NFID also supports signing in with a passkey stored on a mobile device or local keychain, which is the same method used by Internet Identity.
Using NFID
Prerequisites
Before starting the guide, verify the following:
You have
Node.js
16.0.0
or newer installed for frontend development and can install packages usingnpm install
in your project. For information about installing node for your local operating system and package manager, see the Node website.You have downloaded and installed the IC SDK package as described in the download and install page.
Check out the GitHub repo for this project to clone and deploy the project quickly.
Create a new project
First, create a new project with the dfx new
command:
dfx new NFID_project
If using dfx
v0.17.0 and newer, select 'Motoko' and 'Vanilla JS' when prompted.
Edit the backend canister
Replace the default Motoko backend canister code (src/NFID_backend/main.mo
) with the following:
import Principal "mo:base/Principal";
actor {
public shared query({caller}) func greet(name : Text) : async Text {
return "Hello, " # name # "! " # "Your PrincipalId is: " # Principal.toText(caller);
};
};
Update the project's dfx.json
file.
Replace the dfx.json
file's content with the following configuration:
{
"canisters": {
"NFID_backend": {
"main": "src/NFID_backend/main.mo",
"type": "motoko"
},
"NFID_frontend": {
"dependencies": [
"NFID_backend"
],
"frontend": {
"entrypoint": "src/NFID_frontend/src/index.html"
},
"source": [
"src/NFID_frontend/assets"
],
"type": "assets"
}
},
"defaults": {
"build": {
"args": "",
"packtool": ""
}
},
"version": 1
}
Install the auth-client
package.
Install the DFINITY auth-client
package with the command:
npm install --save @dfinity/auth-client
Create a file called index.html
in the subdirectory src/NFID_frontend/src/
.
Insert the following code into the index.html
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>NFID example</title>
<base href="/" />
<link rel="icon" href="favicon.ico" />
<link type="text/css" rel="stylesheet" href="main.css" />
</head>
<body>
<main>
<img src="logo2.svg" alt="DFINITY logo" />
<br />
<br />
<div>
<button id="login">Log me in</button>
</div>
<div id="principalId"></div>
<form action="#">
<label for="name">Enter your name: </label>
<input id="name" alt="Name" type="text" />
<button type="submit">Click Me!</button>
</form>
<section id="greeting"></section>
</main>
</body>
</html>
Create an index.js
file in the src/NFID_frontend/src
subdirectory.
Insert the following code into this new index.js
file:
import { Actor } from "@dfinity/agent";
import { AuthClient } from "@dfinity/auth-client";
import { NFID_backend } from "../../declarations/NFID_backend";
let authClient = null;
async function init() {
authClient = await AuthClient.create();
}
document.querySelector("form").addEventListener("submit", async (e) => {
e.preventDefault();
const button = e.target.querySelector("button");
const name = document.getElementById("name").value.toString();
button.setAttribute("disabled", true);
// Interact with foo actor, calling the greet method
const greeting = await NFID_backend.greet(name);
button.removeAttribute("disabled");
document.getElementById("greeting").innerText = greeting;
return false;
});
function handleSuccess() {
const principalId = authClient.getIdentity().getPrincipal().toText();
document.getElementById(
"principalId"
).innerText = `Your PrincipalId: ${principalId}`;
}
document.getElementById("login").addEventListener("click", async (e) => {
if (!authClient) throw new Error("AuthClient not initialized");
authClient.login({
onSuccess: handleSuccess,
});
});
init();
Deploy your project
Deploy the project with the command:
dfx deploy
You will receive the URL for the frontend and backend canisters running locally:
Deployed canisters.
URLs:
Frontend canister via browser
NFID_frontend: http://127.0.0.1:4943/?canisterId=br5f7-7uaaa-aaaaa-qaaca-cai
Backend canister via Candid interface:
NFID_backend: http://127.0.0.1:4943/?canisterId=bw4dl-smaaa-aaaaa-qaacq-cai&id=be2us-64aaa-aaaaa-qaabq-cai
Open the NFID_frontend
URL in your web browser. You'll see the following UI:
Configure NFID
Currently, the Log me in
button doesn't have any functionality when clicked. Let's configure it to use NFID.
Replace the existing index.js
code with the following:
import { Actor } from "@dfinity/agent";
import { AuthClient } from "@dfinity/auth-client";
import { NFID_backend } from "../../declarations/NFID_backend";
let authClient = null;
async function init() {
authClient = await AuthClient.create();
}
document.querySelector("form").addEventListener("submit", async (e) => {
e.preventDefault();
const button = e.target.querySelector("button");
const name = document.getElementById("name").value.toString();
button.setAttribute("disabled", true);
// Interact with foo actor, calling the greet method
const greeting = await NFID_backend.greet(name);
button.removeAttribute("disabled");
document.getElementById("greeting").innerText = greeting;
return false;
});
function handleSuccess() {
const principalId = authClient.getIdentity().getPrincipal().toText();
document.getElementById(
"principalId"
).innerText = `Your PrincipalId: ${principalId}`;
Actor.agentOf(NFID_backend).replaceIdentity(
authClient.getIdentity()
);
}
document.getElementById("login").addEventListener("click", async (e) => {
if (!authClient) throw new Error("AuthClient not initialized");
const APP_NAME = "NFID example";
const APP_LOGO = "https://nfid.one/icons/favicon-96x96.png";
const CONFIG_QUERY = `?applicationName=${APP_NAME}&applicationLogo=${APP_LOGO}`;
const identityProvider = `https://nfid.one/authenticate${CONFIG_QUERY}`;
authClient.login({
identityProvider,
onSuccess: handleSuccess,
windowOpenerFeatures: `
left=${window.screen.width / 2 - 525 / 2},
top=${window.screen.height / 2 - 705 / 2},
toolbar=0,location=0,menubar=0,width=525,height=705
`,
});
});
init();
Redeploy the canisters
To implement the changes, redeploy the canisters with dfx deploy
.
Now, when you click 'Log me in', you'll get an NFID login prompt: