description |
---|
By using certificate signing, you can implement stateless session management in applications like Next.js. |
With useWallet()
and the WalletButton
, users can be prompted to sign into an application.
When signing in, users are asked to sign a message. The signed message and signature can then be used to verify ownership of a specific address.
The DAppKitProvider
needs to have requireCertificate
enabled to share it with the application:
<DAppKitProvider
nodeUrl="https://mainnet.vechain.org/"
requireCertificate
usePersistence
>
{children}
</DAppKitProvider>
To make sign-in more secure, you can use custom challenges. These challenges are shown to the user during sign-in. You can set a custom certificate in the DAppKitProvider
. This certificate can come from a server-side generated message:
<DAppKitProvider
nodeUrl="https://mainnet.vechain.org/"
requireCertificate
usePersistence
connectionCertificate={{
message: {
purpose: "identification",
payload: {
type: "text",
content: sessionChallenge,
},
},
}}
>
{children}
</DAppKitProvider>
When requireCertificate
is enabled, you can get the connectionCertificate
from the useWallet()
hook. After the user connects their wallet with the WalletButton
, you can use the certificate for verification.
"use client"; // This is a client component
import { type ReactElement, useEffect, useState } from "react";
import { WalletButton, useWallet } from "@vechain/dapp-kit-react";
const Button = (): ReactElement => {
const { connectionCertificate } = useWallet();
useEffect(() => {
// handle certificate
}, [connectionCertificate]);
return (
<div className="container">
<WalletButton />
</div>
);
};
const HomePage = (): ReactElement => {
return <Button />;
};
// eslint-disable-next-line import/no-default-export
export default HomePage;
A common way to pass authentication to backends is by using the authorization
header.
You can Base64 encode the received certificate to easily send it to an API:
const encodedCertificate = btoa(JSON.stringify(connectionCertificate));
fetch("/api/verify", {
method: "GET",
headers: {
authorization: encodedCertificate,
},
})
To verify the certificate, you need to decode it from base64 and parse it back to a JSON object.
You can use certificate.verify()
to check if the certificate is valid. If the certificate has been tampered with, it will throw an error.
The certificate contains important information for access control in the application. Key attributes include:
signer
: the address that signed the message (in lower case)payload
: the message signed by the userdomain
: the domain where the wallet signed the messagetimestamp
: the unix timestamp when the message was signed
Here is an example API function that verifies a received certificate:
import type { NextApiRequest, NextApiResponse } from "next";
import { certificate } from "@vechain/sdk-core";
export default function handler(
req: NextApiRequest,
res: NextApiResponse<object>
) {
const authHeader = req.headers.authorization;
const decodedAuthHeader = atob(authHeader ?? "");
const decodedCertificate = JSON.parse(decodedAuthHeader);
if (!decodedCertificate) {
return res.json({
status: "missing",
authHeader,
decodedAuthHeader,
decodedCertificate,
});
}
try {
// verify it
certificate.verify(decodedCertificate);
// further verify attributes like signer, domain or payload of the decodedCertificate
// verify timestamp/max. validity
return res.json({
status: "verified",
authHeader,
certificate,
user: decodedCertificate.signer,
});
} catch (err: any) {
return res.json({
status: "failed",
authHeader,
decodedAuthHeader,
decodedCertificate,
errorMessage: err.message,
});
}
}
A Next.js example is available on StackBlitz: