Identify User Wallets
User-Identification in accomplished by signing a message with the private key of a user. The signature includes information about the signing address, making it possible to identify and verify a user address.
Connex provides a Signing-Service (opens in a new tab) aimed to solve this in combination with Sync(2) in a user friendly way.
For the user this is a perfect way for identification since signing does not create a transaction or any traces on the blockchain.
Signing
Connex
The signing service prompts the user to sign a payload. The payload can be anything and is presented to the user as the message to sign.
const certificate = await connex.vendor
.sign("cert", {
purpose: "identification",
payload: {
type: "text",
content: "content so sign"
}
})
.request();
The returned signed certificate anatomy is like this:
{
"annex": {
"domain": "jp76b3.csb.app",
"signer": "0x2f3da21ad07657ad6608d251e8f3d3fe7e57ea0e",
"timestamp": 1665558056
},
"signature": "0x7fe7389f2dd15e8e4c1511539e626bd8f4d4d006a769e4b0243c6333a1d6571724126f597c212033a939ba19c212bac17b1bedc9b6f69dca547962af5a72a02901"
}
annex.signer
includes the wanted user address and the timestamp
the time of signing.
In the web this can be used instantly. For backend communication it needs to be verified on the backend side.
If only the signing service is used in the application, the vendor
can be used directly without defining a node to connect to:
// main, test or genesis ID if it's private network
const vendor = new Connex.Vendor('main')
thor-devkit
In none-UI environments the same can be accomplished using the wallets private key directly and thor-devkit (opens in a new tab):
import { Certificate, secp256k1, blake2b256 } from 'thor-devkit'
const certificate = {
purpose: 'identification',
payload: {
type: 'text',
content: 'content to sign'
},
domain: 'localhost',
timestamp: Math.floor(+(new Date())/1000),
signer: wallet.address
}
const jsonStr = Certificate.encode(certificate)
const signature = secp256k1.sign(blake2b256(jsonStr), privateKey)
certificate.signature = `0x${signature.toString('hex')}`
Verification
thor-devkit
thor-devkit (opens in a new tab) provides the necessary functionality to verify the signed message.
The verification requires three important parts
- The signed message (object that was passed for signing)
- The signer information (annex)
- And the signature
The verification will throw if it fails, otherwise it will silently work without a return value:
const certificate = {
purpose: "identification",
payload: {
type: "text",
content: "content so sign"
}
};
const message = await connex.vendor.sign("cert", certificate).request();
try {
Certificate.verify({
...certificate,
...message.annex,
signature: message.signature
});
}
catch (err) {
// signing failed, err.message contains more details
console.error(err)
}
Notes
In addition to the identification
also agreement
is a valid purpose. It can be used to formally agree to terms and archive the result.
To prevent someone from re-using a signed certificate, using a custom payload can ensure the signature is for the right purpose.
Example
An example project signing a message and verifying it is available as Sandbox here: