User Verification
Authentication on Tezos
To verify a user owns a Tezos address, it is crucial to send an expression request and then verify the signature on the backend. With kukai-embed, the expression is signed seamlessly in the background, eliminating the necessity for direct user input.
tip
The expression signed by the user adheres to a specific signing standard. For more details on the expression format, refer to the Message Signing page.
Signature Verification Process
- Generate a random challenge string (
nonce) and a uniquerequestIdon the back-end. This will be included in the expression that will be signed by the user:
Back-end
const requestId = "<request_id>"; // optional, system-specific identifier that may be used to uniquely refer to the sign-in request
const nonce = crypto.randomBytes(16).toString("base64");
- On the frontend, use either kukai-embed's
login(withauthParams) orauthenticateto get a signature from the end user. You will need thenonce(challenge string) and therequest_id(you may skip this by providing an empty string) that you created earlier on the back-end.
Front-end
// In one step:
const { authResponse, pk, pkh } = await kukaiEmbed.login({
authParams: { id: "<request_id>", nonce: "<nonce>" },
});
// In two steps:
const { pk, pkh } = await kukaiEmbed.login();
const authResponse = await kukaiEmbed.authenticate("<request_id>", "<nonce>");
Signed Message
When using login with authParams or authenticate, kukai-embed generates and signs a message that includes your request_id and challenge (nonce), as well as other information that might be useful for the authentication process on the back-end.
Example of a signed message
// used in this example: authenticate("<request_id>", "<nonce>")
Tezos Signed Message: {
"currentTime": "1704063600", // in seconds
"domain": "<your_domain>",
"network": "ghostnet | mainnet",
"nonce": "<nonce>",
"publicKey":"<public key>",
"address":"<tezos address>",
"purpose":"authentication",
"requestId":"<request_id>",
}
- Send the following to the backend:
messageandsignature(included inauthResponse)- publicKey (
pk)
- Verify the signature on the backend using @taquito/utils
Back-end
import { verifySignature } from "@taquito/utils";
import { toHex } from "./utils.ts";
const isNonceIncluded = message.includes("<nonce>");
// optional: verify that the request ID matches what you expect or that the timestamp falls within an acceptable time range
if (!isNonceIncluded) {
throw new Error("Expression mismatch");
}
const isVerified = verifySignature(toHex(message), publicKey, signature);
./utils.ts
export function toHex(message: string): string {
const input = Buffer.from(message);
const prefix = Buffer.from("0501", "hex");
const len_bytes = Buffer.from(
message.length.toString(16).padStart(8, "0"),
"hex"
);
const value = Buffer.concat(
[prefix, len_bytes, input],
prefix.length + len_bytes.length + input.length
);
return value.toString("hex");
}