Sign-In with Solana
Cibrax
Posted on December 20, 2021
Solana has become lately one of the hottest programmable Blockchains after Ethereum. Since the adoption of Solana is growing, and also the number of people using one of their wallets, it might be convenient to start looking into how to support one-click authentication for web sites.
This post will show how to enable that scenario with Phantom.
One-Click authentication with signatures
Either Ethereum or Solana supports the idea of signing text messages with the user's private key available on a wallet. Since only the user owns that private key, and it is the only one who can generate an equivalent signature, this is proof enough to use it as an authentication mechanism. A This scenario uses a combination of Signature + Public Key/Address. As an analogy to a traditional authentication method like username and password, the Public Key/Address would be equivalent to the username, and the signature to a password.
Signing a text message with Phantom
The following code shows how to use Phantom to sign a message. The user will be prompted to authorize this operation.
const message = `Sign this message for authenticating with your wallet. Nonce: ${nonce}`;
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await solana.request({
method: "signMessage",
params: {
message: encodedMessage,
},
});
A nonce was generated server-side and injected in the text message to avoid reply attacks, in which the user signature is intercepted and reused for authentication later on.
This sample uses NextAuth for integrating authentication in a Next.js application. The signature and public key are passed to the SignIn function provided by NextAuth.
signIn('credentials',
{
publicKey: signedMessage.publicKey,
signature: signedMessage.signature,
callbackUrl: `${window.location.origin}/`
})
Verifying the signature on the server side.
The server receives the signature and public key and verifies whether the former is valid. The user is authenticated once this validation passes successfully.
const nonce = req.cookies["auth-nonce"];
const message = `Sign this message for authenticating with your wallet. Nonce: ${nonce}`;
const messageBytes = new TextEncoder().encode(message);
const publicKeyBytes = bs58.decode(credentials.publicKey);
const signatureBytes = bs58.decode(credentials.signature);
const result = nacl.sign.detached.verify(messageBytes, signatureBytes, publicKeyBytes);
if (!result) {
console.log(`authentication failed`);
throw new Error("user can not be authenticated");
}
const user = { name: credentials.publicKey }
return user;
This code retrieves the generated nonce from a session cookie, recreates the text message, and validates the user's signature with the public key passed by the client side.
Once the signature is validated, the public key is set as the username for the user.
The complete sample is available to download from my Github repository solana-login
Posted on December 20, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.