Introduction
W3Auth Tokens are Http Bearer Tokens used to authentication against Crust Cloud Http APIs.
The format of Crust Cloud Http API headers is:
Authorization: Bearer <W3Auth Token>
W3Auth Tokens are generated in following approach:
base64(JSON.stringify({
data: <Typed Data to Sign>,
signature: <Typed Data Signature>
}))
The essential part of a W3Auth Token is a typed data and its ethereum signature in compliance with EIP712.
Typed Data to Sign
Below is a sample typed data to sign:
{
domain: {
chainId: '1',
name: 'cloud3.cc',
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
version: '1',
},
message: {
description: 'Sign for W3 Bucket Access Authentication',
signingAddress: '0x2E189548D45E24A84CDD8270B5A38071B4dA325D',
tokenAddress: '0xB0B0b0b0b0b0B000000000000000000000000000',
tokenId: '1000001',
effectiveTimestamp: 1665386666,
expirationTimestamp: 0
},
primaryType: 'W3Bucket',
types: {
W3Bucket: [
{ name: 'description', type: 'string' },
{ name: 'signingAddress', type: 'address' },
{ name: 'tokenAddress', type: 'address' },
{ name: 'tokenId', type: 'string' },
{ name: 'effectiveTimestamp', type: 'uint256' },
{ name: 'expirationTimestamp', type: 'uint256' },
],
},
};
where
- domain
chainId
: Chaid ID of Ethereum-compatible blockchain, which are defined by EIP-155name
: Name of the dapp where the signature is generatedverifyingContract
: Address of the smart contract that are intended to verify the signature
- message
signingAddress
: Account address to sign the typed datatokenAddress
: Contract address of the W3Bucket NFTtokenId
: W3Bucket token IDeffectiveTimestamp
: Unix timestamp (seconds since 1970-01-01 00:00:00 UTC) when the signature will be valid fromexpirationTimestamp
: Unix timestamp (seconds since 1970-01-01 00:00:00 UTC) when the signature will expire.0
for never expire
Crust Cloud API services will perform a series validations against a W3Auth Token:
- Verify
signature
is signed bydata.message.signingAddress
- Verify
data.message.signingAddress
is the owner of NFT token specified bydata.message.tokenAddress
anddata.message.tokenId
- Verify
signature
is still valid viadata.message.effectiveTimestamp
anddata.message.expirationTimestamp
- Verify
data.message.chainId
match the chain on which the NFT contract is deployed
MetaMask Signing
EIP712 typed data could be signed using MetaMask.
Below is a sample signing dialog MetaMask shows when signing a typed data:
Sample Code
const typedData = {
domain: {
chainId: '1',
name: 'cloud3.cc',
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
version: '1',
},
message: {
description: 'Sign for W3 Bucket Access Authentication',
signingAddress: '0x2E189548D45E24A84CDD8270B5A38071B4dA325D',
tokenAddress: '0xB0B0b0b0b0b0B000000000000000000000000000',
tokenId: '1000001',
effectiveTimestamp: 1665386666,
expirationTimestamp: 0
},
primaryType: 'W3Bucket',
types: {
W3Bucket: [
{ name: 'description', type: 'string' },
{ name: 'signingAddress', type: 'address' },
{ name: 'tokenAddress', type: 'address' },
{ name: 'tokenId', type: 'string' },
{ name: 'effectiveTimestamp', type: 'uint256' },
{ name: 'expirationTimestamp', type: 'uint256' },
],
},
};
// Sign
const signature = await provider.getSigner(account)._signTypedData(typedData.domain, typedData.types, typedData.message);
/**
* Sign use @metamask/eth-sig-util
*
* import { signTypedData, SignTypedDataVersion } from "@metamask/eth-sig-util"
*
* const dataForMetaMask = {
* ...data,
* types: {
* ...data.types,
* EIP712Domain: [
* { name: "name", type: "string" },
* { name: "version", type: "string" },
* { name: "chainId", type: "uint256" },
* { name: "verifyingContract", type: "address" }
* ]
* }
* }
* const signature = signTypedData({ privateKey, data: dataForMetaMask, version: SignTypedDataVersion.V4})
*/
console.log('signature', signature);
// Verify Signature
const hash = ethers.utils._TypedDataEncoder.hash(typedData.domain, typedData.types, typedData.message);
console.log('hash', hash);
const recoverAddress = ethers.utils.recoverAddress(hash, signature);
console.log('recoverAddress', recoverAddress);
console.log(`signature valid: ${recoverAddress == account}`);
// Generate Bearer Token
const bearerTokenData = {
data: typedData,
signature: signature
};
const bearerToken = window.btoa(JSON.stringify(bearerTokenData));
console.log('Bearer token', bearerToken);
console.log('Bearer token decoded', JSON.parse(window.atob(bearerToken)));