0.0.6 • Published 19 days ago

w3t v0.0.6

Weekly downloads
-
License
MIT
Repository
-
Last release
19 days ago

Web3 Token

Development

Build: npm run build

Test: npm run test

Usage

Install

npm i w3t

Client

import {Web3Token} from "w3t";
import * as web3 from "web3";

//Mock the user in the crypto wallet. Use your web3provider install it.
const accounts = web3.eth.accounts.create();

// All users have the same msgMetadata
const msgMetadata = {
    domain: "example.com",
    statement: "Please Sign the Message.",
    uri: "https://example.com",
  // version: "1",
  // chainId: 1
}

const w3tokenClient = new Web3Token(msgMetadata);

// Each user have different msgPayload
const msgPayload = {
    address: accounts.address, // mock the user
    // nonce, issuedAt, expirationTime
}
const message = w3tokenClient.prepareMessage(msgPayload, {expiresIn:'12h'});

//Invoke the wallet and sign the `message`
const signature = web3.eth.accounts.sign(message, accounts.privateKey);

const token = w3tokenClient.create(signature.signature);

//Set the token in request headers and request the server
//... headers:{'Authorization': `Bear ${token}`}

Server

import {Web3Token} from "w3t";

//Get the token from the request headers
const token = req.headers.authorization.split(' ')[1]

//The server has the same msgMetadata
const msgMetadata = {
    domain: "example.com",
    statement: "Please Sign the Message.",
    uri: "https://example.com",
  // version: "1",
  // chainId: 1
}

const w3tokenServer = new Web3Token(msgMetadata);

try{
    const decoded_token = await w3tokenServer.verify(token, {maxAge:'3d'});
    const addr = decoded_token.payload.address;
    //...
}catch(e){
    //verify failed
    //...
}

Compatible With JSON Web Token

Server

import jwt from 'jsonwebtoken';
const token = req.headers.authorization.split(' ')[1];
const decoded_token =  jwt.decode(token);

if (decoded_token.header.typ === 'w3t'){
    // Web3 Token
} else if (decoded_token.header.typ ==='jwt'){
    // JSON Web Token
} else{
    // Invalid token
}

Message

Web3Token uses the EIP-4361 format message, which can avoid Blind Message Attack.

Some fields of EIP-4361 are still in dispute, such as chainId. However, in order to be compatible with EIP-4361, Web3Token still retains these fields.

Web3Token divides the message into two parts: msgMetadata and msgPayload.

On a website, fields such as domain and statement are the same in different messages. We extract these static fields as msgMetadata and omit these fields in web3token. Variable fields such as address in each message constitute msgPayload.

The client and server have the same msgMetadata, which is loaded during the web3Token initialization. The preparemessage() and verify() will automatically load msgMetadata when executed.

API

Web3Token Header

{alg: "ECDSA", typ: "w3t"}

MsgMetadata : Object

NameEIP4361 RequiredWeb3Token RequiredExample
domainrequiredrequiredexample.com
statementoptionaloptionalPlease sign this message.
urirequiredrequiredhttps://example.com/login
versionrequiredoptional (default)default: 1 (MUST be 1)
chainIdrequiredoptional (default)default: 1

MsgPayload : Object

NameEIP4361 RequiredWeb3Token RequiredExample
addressrequiredrequired0x12345...
noncerequiredoptional (default)f040lq6s, default: siwe.generateNonce()
issued-atrequiredoptional (default)2021-09-30T16:25:24Z, default: now()
expiration-timeoptionaloptional (default)2021-09-30T22:25:24Z, default: now()+6h
not-beforeoptionaloptional2021-09-30T16:25:24Z
request-idoptionaloptional1
resourcesoptionaloptionalhttps://example.com/my-web2-claim.json
  • nonce: The Web3Token does not required a nonce, but the EIP4361 required, so this implementation just retains the field.

  • expiration-time: EIP4361 does not required the expiration-time, but Web3Token required because expiration-time can resist Replay Attacks.

  • nonce, issued-at and expiration-time have default values.

constructor(msgMetadata, options)

NameRequiredDescriptionExample
msgMetadatarequiredThe static fields in messages, see Table MsgMetadata.{ domain: 'example.com', statement: 'Please sign this message.', uri: 'localhost', version: 1, chainId: 1,}
optionsoptionaldefault: {maxAge:7d, expiresIn:6h}{}
options.maxAgeoptionalServer Only: the longest valid duration of the client-issued token.{maxAge:7d}, See ms.
options.expiresInoptionalClient Only: the valid duration of the token. expiresIn MUST <= maxAge{expiresIn:1d} See ms.

prepareMessage(msgPayload, expiresIn)

NameRequiredDescriptionExample
msgPayloadrequiredSee Table MsgPayload{address:0x12345... }
optionsoptionaldefault: {expiresIn:6h}
options.expiresInoptionalexpiration-time = issued-at + expiresIn1d, default: 6h

Returns:

TypeDescriptionExample
stringthe messageexample.com wants you to sign in with your Ethereum account:0x123456789...

create(signature)

NameRequiredDescriptionExample
signaturerequiredthe user signed the message of prepareMessage()'0x98765...

Returns:

TypeDescriptionExample
stringthe token valueaaaa.bbbb.cccc

verify(token, maxAge)

NameRequiredDescriptionExample
tokenrequiredSee Table MsgPayload{address:0x12345... } or eyJzaWduYXR1cmUiOiI...
optionsoptionaldefault: {maxAge:7d}
options.maxAgeoptionalmaxAge >= issued-at - expiration-time3d, default: 7d

Returns:

TypeDescriptionExample
objectthe decoded token{header:{alg: "ECDSA", typ: "web3token"},payload:{...MsgPayload},signature:'0x98765...'}

Security Consideration

  • Blind Message Attacks

EIP-4361

  • HTTPS
  • Replay Attacks
0.0.6

19 days ago

0.0.3

22 days ago

0.0.5

22 days ago

0.0.4

22 days ago

0.0.2

27 days ago

0.0.1

28 days ago