├── src ├── utils │ ├── index.ts │ ├── isHexString.ts │ └── timespan.ts ├── jwa │ ├── index.ts │ ├── sign.ts │ └── verify.ts ├── jws │ ├── index.ts │ ├── toString.ts │ ├── sign.ts │ └── verify.ts ├── schemas │ ├── index.ts │ ├── verifySchemas.ts │ └── signSchemas.ts ├── errors │ ├── index.ts │ ├── jws.ts │ ├── jwa.ts │ ├── baseDjwtError.ts │ └── jwt.ts ├── index.ts ├── decode.ts ├── types.ts ├── sign.ts └── verify.ts ├── test ├── sharedFixtures │ ├── ethers │ │ ├── index.ts │ │ ├── ethersSign.ts │ │ └── keystoreWallet.json │ ├── web3 │ │ ├── index.ts │ │ ├── web3Verify.ts │ │ ├── web3Sign.ts │ │ ├── keystore.json │ │ └── tokenUtils.ts │ ├── metamask │ │ ├── index.ts │ │ ├── metamaskVerify.ts │ │ └── metamaskSign.ts │ ├── bitcoinjs │ │ ├── index.ts │ │ ├── verifyBitcoin.ts │ │ └── signBitcoin.ts │ ├── polkadot.js │ │ ├── index.ts │ │ ├── verifyPolkadot.ts │ │ ├── keystore.json │ │ └── signPolkadot.ts │ └── index.ts ├── integration │ ├── sign.spec.ts │ ├── decode.spec.ts │ └── verify.spec.ts └── error │ └── errorTests.spec.ts ├── img └── djwt-logo.jpeg ├── examples ├── web3 │ ├── web3Verify.ts │ ├── package.json │ ├── web3Sign.ts │ ├── keystore.json │ ├── tokenUtils.ts │ ├── index.ts │ └── tsconfig.json ├── bitcoinjs │ ├── verifyBitcoin.ts │ ├── package.json │ ├── signBitcoin.ts │ ├── index.ts │ └── tsconfig.json ├── metamask │ ├── metamaskVerify.ts │ ├── metamaskSign.ts │ ├── package.json │ ├── index.ts │ └── tsconfig.json ├── ethers │ ├── package.json │ ├── ethersSign.ts │ ├── keystoreWallet.json │ ├── index.ts │ └── tsconfig.json ├── polkadot.js │ ├── package.json │ ├── verifyPolkadot.ts │ ├── keystore.json │ ├── index.ts │ ├── wallet.ts │ └── tsconfig.json └── README.md ├── tsconfig.json ├── CONTRIBUTING.md ├── rollup.config.mjs ├── LICENSE ├── package.json ├── .gitignore ├── README.md ├── jest.config.js └── DOCS.md /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./timespan"; -------------------------------------------------------------------------------- /src/jwa/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./sign"; 2 | export * from "./verify" -------------------------------------------------------------------------------- /test/sharedFixtures/ethers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ethersSign"; -------------------------------------------------------------------------------- /src/jws/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./sign"; 2 | export * from "./verify"; 3 | -------------------------------------------------------------------------------- /img/djwt-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amany9000/dJWT/HEAD/img/djwt-logo.jpeg -------------------------------------------------------------------------------- /src/schemas/index.ts: -------------------------------------------------------------------------------- 1 | export * from './signSchemas'; 2 | export * from './verifySchemas'; -------------------------------------------------------------------------------- /test/sharedFixtures/web3/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./web3Sign"; 2 | export * from "./web3Verify"; -------------------------------------------------------------------------------- /test/sharedFixtures/metamask/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./metamaskSign"; 2 | export * from "./metamaskVerify"; -------------------------------------------------------------------------------- /test/sharedFixtures/bitcoinjs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './signBitcoin'; 2 | export * from './verifyBitcoin'; 3 | -------------------------------------------------------------------------------- /test/sharedFixtures/polkadot.js/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./signPolkadot"; 2 | export * from "./verifyPolkadot"; -------------------------------------------------------------------------------- /src/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./baseDjwtError"; 2 | export * from "./jwt"; 3 | export * from "./jws"; 4 | export * from "./jwa"; 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from './decode'; 3 | export * from './verify'; 4 | export * from './sign'; 5 | export * from './errors'; 6 | export * from './types'; -------------------------------------------------------------------------------- /examples/web3/web3Verify.ts: -------------------------------------------------------------------------------- 1 | import { eth } from "web3"; 2 | 3 | export const web3Verify = (data: string, signature: string) => eth.accounts.recover(data, signature); -------------------------------------------------------------------------------- /test/sharedFixtures/web3/web3Verify.ts: -------------------------------------------------------------------------------- 1 | import { eth } from "web3"; 2 | 3 | export const web3Verify = (data: string, signature: string) => eth.accounts.recover(data, signature); -------------------------------------------------------------------------------- /test/sharedFixtures/index.ts: -------------------------------------------------------------------------------- 1 | export * from './web3'; 2 | export * from './ethers'; 3 | export * from './polkadot.js'; 4 | export * from './bitcoinjs'; 5 | export * from './metamask'; -------------------------------------------------------------------------------- /src/jwa/sign.ts: -------------------------------------------------------------------------------- 1 | import type { Signer } from "../types"; 2 | 3 | export async function signPayload( 4 | payload: string, 5 | signer: Signer 6 | ): Promise { 7 | return signer(payload); 8 | } 9 | -------------------------------------------------------------------------------- /examples/bitcoinjs/verifyBitcoin.ts: -------------------------------------------------------------------------------- 1 | import {verify} from 'bitcoinjs-message'; 2 | 3 | export function verifyBitcoin(msg: string, signature: string, address: string) { 4 | return verify(msg, address, signature); 5 | } 6 | -------------------------------------------------------------------------------- /examples/metamask/metamaskVerify.ts: -------------------------------------------------------------------------------- 1 | import {recoverPersonalSignature} from "@metamask/eth-sig-util"; 2 | 3 | export const metamaskVerify = (message: string, signature: string) => recoverPersonalSignature({data: message, signature}); 4 | -------------------------------------------------------------------------------- /test/sharedFixtures/bitcoinjs/verifyBitcoin.ts: -------------------------------------------------------------------------------- 1 | import {verify} from 'bitcoinjs-message'; 2 | 3 | export function verifyBitcoin(msg: string, signature: string, address: string) { 4 | return verify(msg, address, signature); 5 | } 6 | -------------------------------------------------------------------------------- /test/sharedFixtures/metamask/metamaskVerify.ts: -------------------------------------------------------------------------------- 1 | import {recoverPersonalSignature} from "@metamask/eth-sig-util"; 2 | 3 | export const metamaskVerify = (message: string, signature: string) => recoverPersonalSignature({data: message, signature}); 4 | -------------------------------------------------------------------------------- /src/jws/toString.ts: -------------------------------------------------------------------------------- 1 | import { Buffer } from "buffer"; 2 | 3 | export function toString(obj: object): string { 4 | if (typeof obj === "string") return obj; 5 | if (typeof obj === "number" || Buffer.isBuffer(obj)) return obj.toString(); 6 | return JSON.stringify(obj); 7 | } 8 | -------------------------------------------------------------------------------- /examples/metamask/metamaskSign.ts: -------------------------------------------------------------------------------- 1 | import {personalSign} from "@metamask/eth-sig-util"; 2 | 3 | export function metamaskSign(message: string): string{ 4 | const key = Buffer.from("4af1bceebf7f3634ec3cff8a2c38e51178d5d4ce585c52d6043e5e2cc3418bb0", 'hex'); 5 | const signature = personalSign({ privateKey: key, data: message }); 6 | return signature; 7 | } 8 | -------------------------------------------------------------------------------- /examples/web3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web3-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.ts", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "djwt": "^1.0.0", 13 | "web3": "^4.2.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/isHexString.ts: -------------------------------------------------------------------------------- 1 | 2 | export function isHexString(str: string): { str: string, isHex: boolean } { 3 | if (str.slice(0, 2) === '0x') { 4 | str = str.split('0x')[1]; 5 | } 6 | const regExp = /^[0-9a-fA-F]+$/; 7 | 8 | if (regExp.test(str)) 9 | return { str, isHex: true } 10 | 11 | return { str, isHex: false }; 12 | } 13 | -------------------------------------------------------------------------------- /examples/metamask/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metamask", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.ts", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "amany9000", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@metamask/eth-sig-util": "^7.0.0", 13 | "djwt": "^1.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/ethers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ethers-test", 3 | "version": "1.0.0", 4 | "description": "Testing project for ethers", 5 | "main": "index.ts", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "amany9000", 10 | "license": "ISC", 11 | "dependencies": { 12 | "djwt": "^1.0.0", 13 | "ethers": "^6.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/web3/web3Sign.ts: -------------------------------------------------------------------------------- 1 | import {getWeb3, getWallet} from "./tokenUtils"; 2 | 3 | export async function web3Sign(payload: string) { 4 | try { 5 | const web3 = await getWeb3(); 6 | const account = await getWallet(web3); 7 | return account.sign(payload).signature; 8 | } 9 | catch (err : any) { 10 | console.log("error", err); 11 | throw err; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/sharedFixtures/web3/web3Sign.ts: -------------------------------------------------------------------------------- 1 | import {getWeb3, getWallet} from "./tokenUtils"; 2 | 3 | export async function web3Sign(payload: string) { 4 | try { 5 | const web3 = await getWeb3(); 6 | const account = await getWallet(web3); 7 | return account.sign(payload).signature; 8 | } 9 | catch (err : any) { 10 | console.log("error", err); 11 | throw err; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/polkadot.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polkadot_test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.ts", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "amany9000", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@polkadot/keyring": "^12.5.1", 13 | "@polkadot/util-crypto": "^12.5.1", 14 | "djwt": "^1.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/sharedFixtures/metamask/metamaskSign.ts: -------------------------------------------------------------------------------- 1 | import {personalSign} from "@metamask/eth-sig-util"; 2 | 3 | export function metamaskSign(message: string): string{ 4 | //Address: 0x29c76e6ad8f28bb1004902578fb108c507be341b 5 | const key = Buffer.from("4af1bceebf7f3634ec3cff8a2c38e51178d5d4ce585c52d6043e5e2cc3418bb0", 'hex'); 6 | const signature = personalSign({ privateKey: key, data: message }); 7 | return signature; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /examples/bitcoinjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitcoinjs-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.ts", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "amany9000", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bitcoinjs-message": "^2.2.0", 13 | "djwt": "^1.0.0", 14 | "ecpair": "^2.1.0", 15 | "tiny-secp256k1": "^2.2.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | You can create a jwt with any signing function as long as your singature verifier function is compatible. Added examples are for web3, ethers, polkadot.js, bitcoinjs, metamask. 3 | 4 | ## Running an example 5 | For running the web3 example. 6 | 7 | ```sh 8 | cd web3 9 | npm i 10 | ts-node . 11 | ``` 12 | - All the project in the directory have the same structure. 13 | - I'm using ts-node, you can also use tsc to run the examples. -------------------------------------------------------------------------------- /examples/polkadot.js/verifyPolkadot.ts: -------------------------------------------------------------------------------- 1 | import { u8aToHex } from "@polkadot/util"; 2 | import { 3 | decodeAddress, 4 | signatureVerify 5 | } from "@polkadot/util-crypto"; 6 | 7 | export const validateSigPolkadot = (payload: string, signature: string, address: string) : boolean => { 8 | const publicKey = decodeAddress(address); 9 | const hexPublicKey = u8aToHex(publicKey); 10 | 11 | return signatureVerify(payload, signature, hexPublicKey).isValid; 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /examples/web3/keystore.json: -------------------------------------------------------------------------------- 1 | [{"version":3,"id":"4c623a57-f30c-4e3d-a312-3d9fa98bb62e","address":"231a5147b7c2bdf1dc8449da0def741077447bcd","crypto":{"ciphertext":"c7bd026fe0c175a0a08466db2d6f79d1d8d57cd3aecaa484a3fa097d69f16c78","cipherparams":{"iv":"070db32f5e94e514b130a02225bd7f39"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"n":8192,"r":8,"p":1,"dklen":32,"salt":"aec72c0f21f988ff44493168112f5d18e14e230e078f34bc9b1dac6176cfa963"},"mac":"4ae1f57d6ee76ac4ed0f777409d989d54475f5cd8ca9e1cef3dd0518c9e7caaa"}}] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "src", 4 | "declaration": true, 5 | "declarationDir": "types", 6 | "module": "esnext", 7 | "target": "es6", 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "moduleResolution": "node", 12 | "strict": true, 13 | "esModuleInterop": true 14 | }, 15 | "include": ["src"], 16 | "exclude": [ 17 | "node_modules", 18 | "lib" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /test/sharedFixtures/web3/keystore.json: -------------------------------------------------------------------------------- 1 | [{"version":3,"id":"4c623a57-f30c-4e3d-a312-3d9fa98bb62e","address":"231a5147b7c2bdf1dc8449da0def741077447bcd","crypto":{"ciphertext":"c7bd026fe0c175a0a08466db2d6f79d1d8d57cd3aecaa484a3fa097d69f16c78","cipherparams":{"iv":"070db32f5e94e514b130a02225bd7f39"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"n":8192,"r":8,"p":1,"dklen":32,"salt":"aec72c0f21f988ff44493168112f5d18e14e230e078f34bc9b1dac6176cfa963"},"mac":"4ae1f57d6ee76ac4ed0f777409d989d54475f5cd8ca9e1cef3dd0518c9e7caaa"}}] -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | ## Setup 4 | 5 | - Fork Repository. 6 | 7 | - Clone Repository. 8 | 9 | ```sh 10 | git clone https://github.com/{Your_Username}/dJWT.git 11 | cd dJWT 12 | ``` 13 | - Install Dependencies. 14 | 15 | ```sh 16 | npm install 17 | ``` 18 | 19 | - Run Tests. 20 | 21 | ```sh 22 | npm test 23 | ``` 24 | 25 | ## Contributing 26 | 27 | - Have a look at the open issues. Pick an unassigned issue or create one. 28 | 29 | - Create a new branch and make changes. 30 | 31 | - Send a Pull Request after making changes. -------------------------------------------------------------------------------- /examples/ethers/ethersSign.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from "ethers"; 2 | import fs from "fs"; 3 | 4 | export async function signEthers(payloadAndHeader: string) { 5 | const keystoreWallet = (fs.readFileSync('./keystoreWallet.json')).toString(); 6 | 7 | const wallet = await ethers.Wallet.fromEncryptedJson(keystoreWallet, "pass_12345678"); 8 | 9 | let messageDigest = ethers.hashMessage(payloadAndHeader) 10 | const signingKey = new ethers.SigningKey(wallet.privateKey); 11 | 12 | const signature = signingKey.sign(messageDigest); 13 | return signature.serialized; 14 | }; -------------------------------------------------------------------------------- /test/sharedFixtures/ethers/ethersSign.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from "ethers"; 2 | import fs from "fs"; 3 | 4 | export async function signEthers(payloadAndHeader: string) { 5 | const keystoreWallet = (fs.readFileSync('test/sharedFixtures/ethers/keystoreWallet.json')).toString(); 6 | 7 | const wallet = await ethers.Wallet.fromEncryptedJson(keystoreWallet, "pass_12345678"); 8 | 9 | let messageDigest = ethers.hashMessage(payloadAndHeader) 10 | const signingKey = new ethers.SigningKey(wallet.privateKey); 11 | 12 | const signature = signingKey.sign(messageDigest); 13 | return signature.serialized; 14 | }; -------------------------------------------------------------------------------- /test/sharedFixtures/polkadot.js/verifyPolkadot.ts: -------------------------------------------------------------------------------- 1 | import { u8aToHex } from "@polkadot/util"; 2 | import { 3 | decodeAddress, 4 | signatureVerify, 5 | cryptoWaitReady 6 | } from "@polkadot/util-crypto"; 7 | 8 | export const validateSigPolkadot = async (payload: string, signature: string, address: string) : Promise => { 9 | // unnecessary, added here just to test an async Verifier 10 | await cryptoWaitReady(); 11 | 12 | const publicKey = decodeAddress(address); 13 | const hexPublicKey = u8aToHex(publicKey); 14 | 15 | return signatureVerify(payload, signature, hexPublicKey).isValid; 16 | }; 17 | 18 | -------------------------------------------------------------------------------- /src/errors/jws.ts: -------------------------------------------------------------------------------- 1 | import { BaseDjwtError } from './baseDjwtError'; 2 | 3 | export class JwsDecodingError extends BaseDjwtError { 4 | public jwt?: string; 5 | 6 | public constructor(message: string, token?: string) { 7 | super(message); 8 | this.jwt = token; 9 | } 10 | } 11 | 12 | export class JwsEncodingError extends BaseDjwtError { 13 | public jwt?: string; 14 | 15 | public constructor(message: string, token?: string) { 16 | super( 17 | message + ` ${token}` 18 | ); 19 | this.jwt = token; 20 | } 21 | } 22 | 23 | export class JwsVerifyError extends BaseDjwtError { 24 | 25 | public constructor(message : string) { 26 | super(message); 27 | } 28 | } -------------------------------------------------------------------------------- /src/utils/timespan.ts: -------------------------------------------------------------------------------- 1 | import ms from "ms"; 2 | import { TimespanDecodingError } from "../errors"; 3 | 4 | export function timespan(time: string | number, timestamp: number): number { 5 | if (typeof time === "string") { 6 | let milliseconds: any = ms(time); 7 | if (typeof milliseconds === "undefined") { 8 | throw new TimespanDecodingError("Error while decoding time", time); 9 | } 10 | return Math.floor(timestamp + milliseconds / 1000); 11 | } else if (typeof time === "number") { 12 | return timestamp + time; 13 | } else { 14 | throw new TimespanDecodingError( 15 | "Time is not of the tpye number or string", 16 | time 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/bitcoinjs/signBitcoin.ts: -------------------------------------------------------------------------------- 1 | import {ECPairFactory} from 'ecpair'; // v4.x.x 2 | import * as TinySecp256k1Interface from 'tiny-secp256k1'; 3 | import {sign} from 'bitcoinjs-message'; 4 | 5 | 6 | 7 | export function signBitcoin(message: string){ 8 | const ecPair = ECPairFactory(TinySecp256k1Interface); 9 | 10 | let keyPair = ecPair.fromWIF('KynD8ZKdViVo5W82oyxvE18BbG6nZPVQ8Td8hYbwU94RmyUALUik') 11 | 12 | let privateKey = keyPair.privateKey; 13 | if(!privateKey){ 14 | throw new Error("Unable to decode PrivateKey"); 15 | } 16 | 17 | let signature = sign(message, privateKey, keyPair.compressed) 18 | return signature.toString('base64'); 19 | 20 | } -------------------------------------------------------------------------------- /src/errors/jwa.ts: -------------------------------------------------------------------------------- 1 | import { BaseDjwtError } from './baseDjwtError'; 2 | 3 | export class JwaVerifyError extends BaseDjwtError { 4 | public argument: any; 5 | public constructor(message: string, arg: any) { 6 | super(message); 7 | this.argument = arg; 8 | } 9 | } 10 | 11 | export class JwaAddressIncorrectError extends BaseDjwtError { 12 | public expectedAddress? : string; 13 | public returnedAddress? : string; 14 | 15 | public constructor(expectedAddress: string, returnedAddress: string) { 16 | super(`Expected address : ${expectedAddress}, does not match with Returned Address: ${returnedAddress}`); 17 | this.expectedAddress = expectedAddress; 18 | this.returnedAddress = returnedAddress; 19 | } 20 | } -------------------------------------------------------------------------------- /test/sharedFixtures/bitcoinjs/signBitcoin.ts: -------------------------------------------------------------------------------- 1 | import {ECPairFactory} from 'ecpair'; // v4.x.x 2 | import * as TinySecp256k1Interface from 'tiny-secp256k1'; 3 | import {sign} from 'bitcoinjs-message'; 4 | 5 | 6 | 7 | export function signBitcoin(message: string){ 8 | const ecPair = ECPairFactory(TinySecp256k1Interface); 9 | 10 | //Address: 1HZwtseQ9YoRteyAxzt6Zq43u3Re5JKPbk 11 | let keyPair = ecPair.fromWIF('KynD8ZKdViVo5W82oyxvE18BbG6nZPVQ8Td8hYbwU94RmyUALUik') 12 | 13 | let privateKey = keyPair.privateKey; 14 | if(!privateKey){ 15 | throw new Error("Unable to decode PrivateKey"); 16 | } 17 | 18 | let signature = sign(message, privateKey, keyPair.compressed) 19 | return signature.toString('base64'); 20 | 21 | } -------------------------------------------------------------------------------- /src/jwa/verify.ts: -------------------------------------------------------------------------------- 1 | import type { Verifier } from "../types"; 2 | import { JwaVerifyError, JwaAddressIncorrectError } from "../errors"; 3 | 4 | export async function jwaVerify( 5 | verifier: Verifier, 6 | payload: string, 7 | signature: string, 8 | address: string 9 | ): Promise { 10 | const result = await verifier(payload, signature, address); 11 | switch (typeof result) { 12 | case "boolean": { 13 | return result as boolean; 14 | } 15 | case "string": { 16 | if (result === address) return true; 17 | else throw new JwaAddressIncorrectError(address, result); 18 | } 19 | default: 20 | throw new JwaVerifyError( 21 | "verifier() does not return boolean or string", 22 | result 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/web3/tokenUtils.ts: -------------------------------------------------------------------------------- 1 | import Web3 from 'web3'; 2 | import * as fs from 'fs'; 3 | 4 | 5 | export async function getWallet(web3 : Web3){ 6 | const readKeystore = JSON.parse(fs.readFileSync("keystore.json").toString()); 7 | const decryptedAccount = await web3.eth.accounts.decrypt(readKeystore[0], 'pass_12345678'); 8 | return decryptedAccount; 9 | } 10 | 11 | 12 | export async function getWeb3(){ 13 | return new Web3(); 14 | } 15 | 16 | 17 | export async function getContract(web3 : Web3, address? : string){ 18 | const abi = JSON.parse(fs.readFileSync("artifact.json").toString())["abi"] 19 | 20 | // @user, this address will come from environment variable 21 | return address ? new web3.eth.Contract(abi, address) : new web3.eth.Contract(abi); 22 | } 23 | -------------------------------------------------------------------------------- /examples/ethers/keystoreWallet.json: -------------------------------------------------------------------------------- 1 | {"address":"145831eba8085d78c1d30a9c108aad8a1501d6e0","id":"90754b13-a9e1-439f-b835-57b00d2a25ab","version":3,"Crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"303d7d686b1b6f42c63ef0a8e05d5bea"},"ciphertext":"a32dae5380d4dbc006ca0e52c38d2a0f8da9dc57872233b5b106f9ee3e135e19","kdf":"scrypt","kdfparams":{"salt":"48a94036803a79197f2c60d69bf4bbc919f3baec2703121f2dbfce52aeae7ef9","n":131072,"dklen":32,"p":1,"r":8},"mac":"2b4a2aa19c2e45ba3159ce2e3e955ef6ee7e94863f6871de792e7b859279fec9"},"x-ethers":{"client":"ethers/6.6.1","gethFilename":"UTC--2023-07-07T22-56-30.0Z--145831eba8085d78c1d30a9c108aad8a1501d6e0","path":"m/44'/60'/0'/0/0","locale":"en","mnemonicCounter":"46fd0d5ad3ccab1a46f31b10cd4b2aee","mnemonicCiphertext":"25e3d82d8d26582832d18afad88148c0","version":"0.1"}} -------------------------------------------------------------------------------- /test/sharedFixtures/ethers/keystoreWallet.json: -------------------------------------------------------------------------------- 1 | {"address":"145831eba8085d78c1d30a9c108aad8a1501d6e0","id":"90754b13-a9e1-439f-b835-57b00d2a25ab","version":3,"Crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"303d7d686b1b6f42c63ef0a8e05d5bea"},"ciphertext":"a32dae5380d4dbc006ca0e52c38d2a0f8da9dc57872233b5b106f9ee3e135e19","kdf":"scrypt","kdfparams":{"salt":"48a94036803a79197f2c60d69bf4bbc919f3baec2703121f2dbfce52aeae7ef9","n":131072,"dklen":32,"p":1,"r":8},"mac":"2b4a2aa19c2e45ba3159ce2e3e955ef6ee7e94863f6871de792e7b859279fec9"},"x-ethers":{"client":"ethers/6.6.1","gethFilename":"UTC--2023-07-07T22-56-30.0Z--145831eba8085d78c1d30a9c108aad8a1501d6e0","path":"m/44'/60'/0'/0/0","locale":"en","mnemonicCounter":"46fd0d5ad3ccab1a46f31b10cd4b2aee","mnemonicCiphertext":"25e3d82d8d26582832d18afad88148c0","version":"0.1"}} -------------------------------------------------------------------------------- /test/sharedFixtures/web3/tokenUtils.ts: -------------------------------------------------------------------------------- 1 | import Web3 from 'web3'; 2 | import fs from "fs"; 3 | 4 | 5 | export async function getWallet(web3 : Web3){ 6 | const readKeystore = JSON.parse(fs.readFileSync("test/sharedFixtures/web3/keystore.json").toString()); 7 | const decryptedAccount = await web3.eth.accounts.decrypt(readKeystore[0], 'pass_12345678'); 8 | return decryptedAccount; 9 | } 10 | 11 | 12 | export async function getWeb3(){ 13 | return new Web3(); 14 | } 15 | 16 | 17 | export async function getContract(web3 : Web3, address? : string){ 18 | const abi = JSON.parse(fs.readFileSync("artifact.json").toString())["abi"] 19 | 20 | // @user, this address will come from environment variable 21 | return address ? new web3.eth.Contract(abi, address) : new web3.eth.Contract(abi); 22 | } 23 | -------------------------------------------------------------------------------- /src/decode.ts: -------------------------------------------------------------------------------- 1 | import { decodeJws } from "./jws"; 2 | import { TokenOrPayload, DecodeOptions } from "./types"; 3 | import { VerificationError } from "./errors"; 4 | import { decodeOptionsSchema } from "./schemas"; 5 | 6 | export function decodeJWT( 7 | jwtString: string, 8 | options?: Partial 9 | ): TokenOrPayload { 10 | options = Object.assign({}, options); 11 | 12 | const optionsParseResult = decodeOptionsSchema.safeParse(options); 13 | if (!optionsParseResult.success) { 14 | throw new VerificationError( 15 | JSON.parse(optionsParseResult.error.message)[0].message 16 | ); 17 | } 18 | 19 | const decoded = decodeJws(jwtString, options.sigEncoding); 20 | const payload = decoded.payload; 21 | if (options.complete) 22 | return { 23 | header: decoded.header, 24 | payload: payload, 25 | signature: decoded.signature, 26 | }; 27 | else return payload; 28 | } 29 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import typescript from "@rollup/plugin-typescript"; 2 | import { nodeResolve } from "@rollup/plugin-node-resolve"; 3 | import commonjs from "@rollup/plugin-commonjs"; 4 | 5 | import pkg from "./package.json" assert { type: "json" }; 6 | 7 | const createOutput = (config) => ({ 8 | sourcemap: true, 9 | ...(config || {}), 10 | }); 11 | 12 | /** 13 | * @type {import('rollup').RollupOptions} 14 | */ 15 | const config = { 16 | input: "src/index.ts", 17 | output: [ 18 | { 19 | file: pkg.main, 20 | format: "cjs", 21 | }, 22 | { 23 | file: pkg.module, 24 | format: "esm", 25 | }, 26 | ].map(createOutput), 27 | plugins: [typescript(), nodeResolve({ preferBuiltins: false }), commonjs()], 28 | external: [ 29 | ...Object.keys(pkg.dependencies || {}), // Exclude dependencies 30 | ...Object.keys(pkg.devDependencies || {}), // Exclude peerDependencies 31 | ] 32 | }; 33 | 34 | export default config; 35 | -------------------------------------------------------------------------------- /examples/polkadot.js/keystore.json: -------------------------------------------------------------------------------- 1 | {"mnemonicEncrypted":"243,176,82,176,187,51,168,8,100,161,9,146,95,153,53,189,96,109,4,144,164,164,255,188,185,216,62,110,88,208,50,183,114,119,235,39,211,37,252,85,168,52,86,195,7,16,18,71,31,149,44,156,44,39,109,148,210,113,29,142,18,70,240,157,240,150,82,71,151,234,34,139,65,148,205,137,124,209,180,134,212,254,66,109,206,14,161,72,113,227,32,137,152,244,37","keyPairs":["{\"encoded\":\"D25x2QUPJImzEYwpJ9wy3mle0mhnVAB7W0MEFjgF5g4AgAAAAQAAAAgAAADZKh++ukYDojtiWVw1kqPZl2QICRb7Qll28YF/lVA7bMkzr42aVMXzU8Xj66YQZYyABU1CiKRzTQq51lZFmFmwB3lVFdODZ/RZ0IA1xIhKJhPrSMrmhCq3yclSADrBse/qE18p/Q3jSh1saf7mRAs+awyIkh+wOizSElkziWIyQ6n0I1RkRHRZZZ95YehdFWb/OxnqfbxkSyLnCdG/\",\"encoding\":{\"content\":[\"pkcs8\",\"sr25519\"],\"type\":[\"scrypt\",\"xsalsa20-poly1305\"],\"version\":\"3\"},\"address\":\"5GHZTxY7iZ8PK2ipPeB7oF33KiURKhyRPXBhyBhiQVHGfS1A\",\"meta\":{}}"],"encKeyPairs":null,"encryptionNonce":"84,245,247,62,187,156,24,133,187,171,250,255,246,204,121,200,71,143,121,128,38,144,17,249"} -------------------------------------------------------------------------------- /test/sharedFixtures/polkadot.js/keystore.json: -------------------------------------------------------------------------------- 1 | {"mnemonicEncrypted":"87,216,103,209,201,22,101,24,61,123,82,132,39,182,153,213,174,156,187,156,107,128,235,146,179,139,19,19,219,32,142,57,189,14,215,23,75,109,235,195,103,140,197,172,46,159,195,64,71,110,120,220,160,112,182,150,184,141,243,2,87,224,53,41,164,202,67,153,20,147,115,149,2,190,208,172,230,133,231,250,104,63,188,16,227,150,103,251,43,92","keyPairs":["{\"encoded\":\"JI8KbST9oLnMRT5mvLeHY2MIdbhGQ/w4xC6XrT3uCS8AgAAAAQAAAAgAAAB+jlhnojSqAyf6kcnMcJmsiNZdOTOoXxS16o/NxP9lLtnbX5fKkh87k1WgsOsUJ118Wjjjih7ENj+Uqf9Tozz3/udKBRxw5/fBrkr++y8StwK4kLaae2dFGGbP95w8JZBu5Ya8SqwmiMtcOXB53LqiP7yBc9Kg1umdoYz/qV7cjyLGjXO6miAfCtROUtijc+KVQtqBiS8uiCQxhKF2\",\"encoding\":{\"content\":[\"pkcs8\",\"sr25519\"],\"type\":[\"scrypt\",\"xsalsa20-poly1305\"],\"version\":\"3\"},\"address\":\"5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E\",\"meta\":{}}"],"encKeyPairs":null,"encryptionNonce":"233,54,111,167,2,119,225,178,202,146,180,19,83,152,135,86,160,62,120,228,25,117,138,4"} -------------------------------------------------------------------------------- /examples/bitcoinjs/index.ts: -------------------------------------------------------------------------------- 1 | import { signBitcoin } from "./signBitcoin"; 2 | import { verifyBitcoin } from "./verifyBitcoin"; 3 | import { sign, verify } from "djwt"; 4 | 5 | (async function test() { 6 | const address = "1HZwtseQ9YoRteyAxzt6Zq43u3Re5JKPbk"; 7 | const algorithm = "ES256k"; 8 | 9 | const payload = { 10 | nonce: 654321, 11 | iat: 1582062696, 12 | exp: 1782098690, 13 | iss: address, 14 | nbf: 100000000, 15 | sub: address, 16 | jti: "324221", 17 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"], 18 | }; 19 | const token = await sign(payload, signBitcoin, { algorithm }); 20 | 21 | console.log(`Signed JWT token: ${token}`); 22 | 23 | const receivedToken = verify(token, verifyBitcoin, { 24 | complete: true, 25 | nonce: 654321, 26 | maxAge: 10000000000, 27 | issuer: address, 28 | jwtid: "324221", 29 | subject: address, 30 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 31 | algorithm, 32 | }); 33 | 34 | console.log( 35 | `Decode JWT token JSON returned after verification:`, 36 | receivedToken 37 | ); 38 | })(); 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Aman Yadav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/web3/index.ts: -------------------------------------------------------------------------------- 1 | import {web3Sign} from "./web3Sign"; 2 | import {web3Verify} from "./web3Verify"; 3 | import {sign, verify} from "djwt"; 4 | 5 | (async function test() { 6 | const address = "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD"; 7 | const algorithm = "ES256k"; 8 | 9 | const payload = { 10 | nonce: 654321, 11 | iat: 1582062696, 12 | exp: 1782098690, 13 | iss: address, 14 | nbf: 100000000, 15 | sub: address, 16 | jti: "324221", 17 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"] 18 | }; 19 | const token = await sign(payload, web3Sign, { algorithm }); 20 | 21 | console.log(`Signed JWT token: ${token}`) 22 | 23 | const receivedToken = verify(token, web3Verify, { 24 | complete: true, 25 | nonce: 654321, 26 | maxAge: 10000000000, 27 | issuer: address, 28 | jwtid: "324221", 29 | subject: address, 30 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 31 | algorithm, 32 | }); 33 | 34 | console.log(`Decode JWT token JSON returned after verification:`, receivedToken) 35 | })() -------------------------------------------------------------------------------- /examples/ethers/index.ts: -------------------------------------------------------------------------------- 1 | import {sign, verify} from "djwt"; 2 | import {verifyMessage} from "ethers"; 3 | 4 | import {signEthers} from "./ethersSign"; 5 | 6 | (async function test() { 7 | const address = "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0"; 8 | const algorithm = "ES256k"; 9 | 10 | const payload = { 11 | nonce: 654321, 12 | iat: 1582062696, 13 | exp: 1782098690, 14 | iss: address, 15 | nbf: 100000000, 16 | sub: address, 17 | jti: "324221", 18 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"] 19 | }; 20 | const token = await sign(payload, signEthers, { algorithm }); 21 | 22 | console.log(`Signed JWT token: ${token}`) 23 | 24 | const receivedToken = verify(token, verifyMessage, { 25 | complete: true, 26 | nonce: 654321, 27 | maxAge: 10000000000, 28 | issuer: address, 29 | jwtid: "324221", 30 | subject: address, 31 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 32 | algorithm, 33 | }); 34 | 35 | console.log(`Decode JWT token JSON returned after verification:`, receivedToken) 36 | })() -------------------------------------------------------------------------------- /examples/metamask/index.ts: -------------------------------------------------------------------------------- 1 | import {metamaskSign} from "./metamaskSign"; 2 | import {metamaskVerify} from "./metamaskVerify"; 3 | import {sign, verify} from "djwt"; 4 | 5 | (async function test() { 6 | const address = "0x29c76e6ad8f28bb1004902578fb108c507be341b"; 7 | const algorithm = "ES256k"; 8 | 9 | const payload = { 10 | nonce: 654321, 11 | iat: 1582062696, 12 | exp: 1782098690, 13 | iss: address, 14 | nbf: 100000000, 15 | sub: address, 16 | jti: "324221", 17 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"] 18 | }; 19 | const token = await sign(payload, metamaskSign, { algorithm }); 20 | 21 | console.log(`Signed JWT token: ${token}`) 22 | 23 | const receivedToken = verify(token, metamaskVerify, { 24 | complete: true, 25 | nonce: 654321, 26 | maxAge: 10000000000, 27 | issuer: address, 28 | jwtid: "324221", 29 | subject: address, 30 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 31 | algorithm, 32 | }); 33 | 34 | console.log(`Decode JWT token JSON returned after verification:`, receivedToken) 35 | })() -------------------------------------------------------------------------------- /src/errors/baseDjwtError.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface DjwtError extends Error { 3 | readonly name: string; 4 | readonly code?: number; 5 | readonly stack?: string; 6 | } 7 | 8 | export abstract class BaseDjwtError extends Error implements DjwtError { 9 | public override readonly name: string; 10 | public readonly code?: number; 11 | public override stack: string | undefined; 12 | public innerError: Error | Error[] | undefined; 13 | 14 | public constructor(msg?: string, innerError: Error | Error[] = new Error("dJWT Error")) { 15 | super(msg); 16 | this.innerError = innerError; 17 | this.name = this.constructor.name; 18 | if (typeof Error.captureStackTrace === 'function') { 19 | Error.captureStackTrace(new.target.constructor); 20 | } else { 21 | this.stack = new Error().stack; 22 | } 23 | } 24 | 25 | public static convertToString(value: Error) { 26 | if (value === null || value === undefined) return 'undefined'; 27 | 28 | const result = JSON.stringify(value); 29 | return result; 30 | } 31 | 32 | public toJSON() { 33 | return { 34 | name: this.name, 35 | code: this.code, 36 | message: this.message, 37 | innerError: this.innerError, 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/polkadot.js/index.ts: -------------------------------------------------------------------------------- 1 | import {signPayload} from "./wallet"; 2 | import { validateSigPolkadot } from "./verifyPolkadot"; 3 | 4 | import { sign, verify } from "djwt"; 5 | 6 | 7 | (async function test() { 8 | const address = "5GHZTxY7iZ8PK2ipPeB7oF33KiURKhyRPXBhyBhiQVHGfS1A"; 9 | const algorithm = "SR25519"; 10 | 11 | const payload = { 12 | nonce: 654321, 13 | iat: 1582062696, 14 | exp: 1782098690, 15 | iss: address, 16 | nbf: 100000000, 17 | sub: address, 18 | jti: "324221", 19 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"] 20 | }; 21 | const token = await sign(payload, signPayload, { algorithm }); 22 | 23 | console.log(`Signed JWT token: ${token}`) 24 | 25 | const receivedToken = verify(token, validateSigPolkadot, { 26 | complete: true, 27 | nonce: 654321, 28 | maxAge: 10000000000, 29 | issuer: address, 30 | jwtid: "324221", 31 | subject: address, 32 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 33 | algorithm, 34 | }); 35 | 36 | console.log(`Decode JWT token JSON returned after verification:`, receivedToken) 37 | })() -------------------------------------------------------------------------------- /src/jws/sign.ts: -------------------------------------------------------------------------------- 1 | import { Buffer } from "buffer"; 2 | import { toString } from "./toString"; 3 | import { signPayload } from "../jwa"; 4 | import type { Payload, Header, Signer } from "../types"; 5 | import { isHexString } from "../utils/isHexString"; 6 | import { JwsEncodingError } from "../errors/jws" 7 | 8 | function base64url(string: & string, encoding: BufferEncoding): string { 9 | 10 | if (encoding === 'hex') { 11 | let { str, isHex } = isHexString(string); 12 | if (!isHex) 13 | throw new JwsEncodingError('Non-Hexstring provided with hex encoding', string); 14 | string = str; 15 | } 16 | 17 | return Buffer.from(string, encoding) 18 | .toString("base64") 19 | .replace(/=/g, "") 20 | .replace(/\+/g, "-") 21 | .replace(/\//g, "_"); 22 | } 23 | 24 | function jwsSecuredInput( 25 | header: Header, 26 | payload: Payload, 27 | ): string { 28 | let encodedHeader = base64url(toString(header), 'utf-8'); 29 | let encodedPayload = base64url(toString(payload), 'utf-8'); 30 | return `${encodedHeader}.${encodedPayload}`; 31 | } 32 | 33 | export async function signJws( 34 | header: Header, 35 | payload: Payload, 36 | signer: Signer, 37 | sigEncoding: BufferEncoding 38 | ): Promise { 39 | let securedInput = jwsSecuredInput(header, payload); 40 | let signature = await signPayload(securedInput, signer); 41 | return `${securedInput}.${base64url(signature, sigEncoding)}` 42 | } 43 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type SignOptions = { 2 | algorithm: string; 3 | header: Header; 4 | sigEncoding: BufferEncoding; 5 | noTimestamp: boolean; 6 | expiresIn: number | string; 7 | notBefore: number | string; 8 | }; 9 | 10 | export type DecodeOptions = { 11 | sigEncoding: BufferEncoding; 12 | complete: boolean; 13 | }; 14 | 15 | export type VerifyOptions = DecodeOptions & { 16 | audience: string | string[]; 17 | issuer: string | string[]; 18 | subject: string; 19 | jwtid: string; 20 | clockTimestamp: number; 21 | nonce: number; 22 | ignoreNotBefore: boolean; 23 | clockTolerance: number; 24 | ignoreExpiration: number; 25 | maxAge: number | string; 26 | algorithm: string; 27 | }; 28 | 29 | export type Payload = { 30 | iss: string; 31 | nonce: number; 32 | exp: number; 33 | iat?: number; 34 | nbf?: number; 35 | sub?: string; 36 | jti?: string; 37 | aud?: string | string[]; 38 | }; 39 | 40 | export type Header = { 41 | alg: string; 42 | }; 43 | 44 | export type Token = { 45 | header: Header; 46 | payload: Payload; 47 | signature: string; 48 | }; 49 | 50 | export type TokenOrPayload = Partial; 51 | 52 | export interface Signer { 53 | (payload: string): Promise | string; 54 | } 55 | 56 | export interface Verifier { 57 | (payload: string, signature: string, address: string): 58 | Promise 59 | | boolean 60 | | string; 61 | } 62 | -------------------------------------------------------------------------------- /examples/polkadot.js/wallet.ts: -------------------------------------------------------------------------------- 1 | import { Keyring } from "@polkadot/keyring"; 2 | import { stringToU8a, u8aToHex } from "@polkadot/util"; 3 | import { 4 | blake2AsHex, 5 | cryptoWaitReady 6 | } from "@polkadot/util-crypto"; 7 | import { readFile} from "fs"; 8 | 9 | 10 | type KeyStore = { 11 | mnemonicEncrypted: string; 12 | encryptionNonce: string; 13 | keyPairs: string[] | null; 14 | encKeyPairs: string[] | null; 15 | }; 16 | 17 | function getKeyStore() { 18 | return new Promise((resolve, reject) => { 19 | readFile("keystore.json", (err, keyStoreString) => { 20 | if(err) 21 | resolve(undefined); 22 | if(keyStoreString) 23 | resolve(JSON.parse(keyStoreString.toString())); 24 | }); 25 | }); 26 | } 27 | 28 | async function getKeyPairById(id: number) { 29 | const keyStore = await getKeyStore(); 30 | 31 | if (keyStore && keyStore.keyPairs) return keyStore.keyPairs[id]; 32 | 33 | return undefined; 34 | } 35 | 36 | export async function signPayload(payload: string | Uint8Array) { 37 | await cryptoWaitReady(); 38 | 39 | const hash = blake2AsHex("1234").slice(34); 40 | 41 | const keyPairID = 0 42 | 43 | const keypairString = await getKeyPairById(keyPairID); 44 | 45 | if (keypairString === undefined) { 46 | throw new Error("Keypair of given ID not present"); 47 | } 48 | 49 | return new Promise<`0x${string}`>((resolve, reject) => { 50 | const keyring = new Keyring({ type: "sr25519" }); 51 | const keypair = keyring.addFromJson(JSON.parse(keypairString)); 52 | keypair.unlock(hash); 53 | 54 | let signature; 55 | if (typeof(payload) === "string") 56 | signature = keypair.sign(stringToU8a(payload)); 57 | else 58 | signature = keypair.sign(payload); 59 | 60 | const hex = u8aToHex(signature); 61 | resolve(hex); 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /test/sharedFixtures/polkadot.js/signPolkadot.ts: -------------------------------------------------------------------------------- 1 | import { Keyring } from "@polkadot/keyring"; 2 | import { stringToU8a, u8aToHex } from "@polkadot/util"; 3 | 4 | import { 5 | cryptoWaitReady, 6 | blake2AsHex 7 | } from "@polkadot/util-crypto"; 8 | 9 | import { readFile} from "fs"; 10 | 11 | 12 | type KeyStore = { 13 | mnemonicEncrypted: string; 14 | encryptionNonce: string; 15 | keyPairs: string[] | null; 16 | encKeyPairs: string[] | null; 17 | }; 18 | 19 | function getKeyStore() { 20 | return new Promise((resolve, _) => { 21 | readFile("./test/sharedFixtures/polkadot.js/keystore.json", (err, keyStoreString) => { 22 | if(err){ 23 | throw new Error("Keystore file not found") 24 | } 25 | if(keyStoreString) 26 | resolve(JSON.parse(keyStoreString.toString())); 27 | }); 28 | }); 29 | } 30 | 31 | async function getKeyPairById(id: number) { 32 | const keyStore = await getKeyStore(); 33 | 34 | if (keyStore && keyStore.keyPairs) return keyStore.keyPairs[id]; 35 | 36 | return undefined; 37 | } 38 | 39 | 40 | 41 | //Address: 5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E 42 | export async function signPolkadot(payload: string): Promise<`0x${string}`> { 43 | await cryptoWaitReady(); 44 | 45 | const keypairString = await getKeyPairById(0); 46 | 47 | if (keypairString === undefined) { 48 | throw new Error("Keypair of given ID not present"); 49 | } 50 | 51 | return new Promise<`0x${string}`>((resolve, _) => { 52 | const keyring = new Keyring({ type: "sr25519" }); 53 | const keypair = keyring.addFromJson(JSON.parse(keypairString)); 54 | 55 | //Password for keystore 56 | const hash = blake2AsHex("1234").slice(34); 57 | 58 | keypair.unlock(hash); 59 | 60 | const signature = keypair.sign(stringToU8a(payload)); 61 | resolve(u8aToHex(signature)); 62 | }); 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/errors/jwt.ts: -------------------------------------------------------------------------------- 1 | import { BaseDjwtError } from "./baseDjwtError"; 2 | 3 | export class TokenExpiredError extends BaseDjwtError { 4 | public expiredAt: number; 5 | 6 | public constructor(message: string, expiredAt: number) { 7 | super(message); 8 | this.expiredAt = expiredAt; 9 | } 10 | } 11 | 12 | export class NotBeforeError extends BaseDjwtError { 13 | public date: number; 14 | 15 | public constructor(message: string, date: number) { 16 | super(message); 17 | this.date = date; 18 | } 19 | } 20 | 21 | export class TimespanDecodingError extends BaseDjwtError { 22 | public time: number | string; 23 | 24 | public constructor(message: string, date: number | string) { 25 | super(message); 26 | this.time = date; 27 | } 28 | } 29 | 30 | export class VerificationError extends BaseDjwtError { 31 | public constructor(message: string) { 32 | super(message); 33 | } 34 | } 35 | export class OptionsVerificationError extends BaseDjwtError { 36 | public incorrectValue: string | number; 37 | public expectedValue: string | number; 38 | 39 | public constructor( 40 | message: string, 41 | incorrectValue: string | number, 42 | expectedValue: string | number 43 | ) { 44 | super( 45 | message + ` ${incorrectValue} is not equal to expected: ${expectedValue}` 46 | ); 47 | this.incorrectValue = incorrectValue; 48 | this.expectedValue = expectedValue; 49 | } 50 | } 51 | 52 | export class InvalidPayloadError extends BaseDjwtError { 53 | public constructor(message: string) { 54 | super(message); 55 | } 56 | } 57 | 58 | export class InvalidSignOptionsError extends BaseDjwtError { 59 | public payloadValue?: string; 60 | public optionsValue?: string; 61 | 62 | public constructor( 63 | message: string, 64 | payloadVal?: string, 65 | optionsVal?: string 66 | ) { 67 | super(message); 68 | this.payloadValue = payloadVal; 69 | this.optionsValue = optionsVal; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "djwt", 3 | "version": "2.0.0", 4 | "description": "A JWT Library with blockchain key based signing for JWS.", 5 | "main": "lib/index.js", 6 | "module": "lib/index.esm.js", 7 | "types": "lib/types/index.d.ts", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "test": "jest", 13 | "build": "rollup -c", 14 | "prepublish:public:patch": "npm run build", 15 | "prepublish:public:minor": "npm run build", 16 | "publish:public:patch": "npm version patch --force && npm publish --access public", 17 | "publish:public:minor": "npm version minor --force && npm publish --access public", 18 | "publish:public": "npm run publish:public:patch" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/amany9000/dJWT.git" 23 | }, 24 | "keywords": [ 25 | "blockchain", 26 | "jwt", 27 | "ethereum", 28 | "bitcoin", 29 | "polkadot", 30 | "jws", 31 | "signature" 32 | ], 33 | "author": "amany9000", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/amany9000/dJWT/issues" 37 | }, 38 | "homepage": "https://github.com/amany9000/dJWT#readme", 39 | "dependencies": { 40 | "buffer": "^6.0.3", 41 | "ms": "^2.1.3", 42 | "zod": "^3.22.4" 43 | }, 44 | "devDependencies": { 45 | "@metamask/eth-sig-util": "^7.0.0", 46 | "@polkadot/keyring": "^12.5.1", 47 | "@polkadot/util-crypto": "^12.5.1", 48 | "@rollup/plugin-commonjs": "^25.0.7", 49 | "@rollup/plugin-node-resolve": "^15.2.3", 50 | "@rollup/plugin-typescript": "11.1.5", 51 | "@truffle/hdwallet-provider": "^2.1.15", 52 | "@types/jest": "^29.5.5", 53 | "@types/lodash": "^4.14.196", 54 | "@types/ms": "^0.7.31", 55 | "@types/node": "^20.4.5", 56 | "bitcoinjs-message": "^2.2.0", 57 | "ecpair": "^2.1.0", 58 | "ethers": "^6.7.1", 59 | "jest": "^29.7.0", 60 | "rollup": "^3.29.4", 61 | "tiny-secp256k1": "^2.2.3", 62 | "ts-jest": "^29.1.1", 63 | "typescript": "^5.2.2", 64 | "web3": "^4.1.2" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/sign.ts: -------------------------------------------------------------------------------- 1 | import { timespan } from "./utils"; 2 | import { signJws } from "./jws"; 3 | import { payloadSchema, signOptionsSchema } from "./schemas"; 4 | import { InvalidPayloadError, InvalidSignOptionsError } from "./errors"; 5 | 6 | import type { SignOptions, Payload, Header, Signer } from "./types"; 7 | 8 | export async function signJWT( 9 | payload: Payload, 10 | signer: Signer, 11 | options: Partial & 12 | (Pick | Pick) 13 | ): Promise { 14 | const payloadParseResult = payloadSchema.safeParse(payload); 15 | if (!payloadParseResult.success) { 16 | throw new InvalidPayloadError( 17 | JSON.parse(payloadParseResult.error.message)[0].message 18 | ); 19 | } 20 | 21 | const optionsParseResult = signOptionsSchema.safeParse(options); 22 | if (!optionsParseResult.success) { 23 | throw new InvalidSignOptionsError( 24 | JSON.parse(optionsParseResult.error.message)[0].message 25 | ); 26 | } 27 | 28 | // Default encoding values 29 | const sigEncoding = options.sigEncoding || "hex"; 30 | 31 | let header: Header | undefined = options.header; 32 | 33 | if (!header) { 34 | if (!options.algorithm) 35 | throw new InvalidSignOptionsError( 36 | "Either header or algorithm is required in options" 37 | ); 38 | 39 | header = { 40 | alg: options.algorithm, 41 | }; 42 | } 43 | 44 | const timestamp = payload.iat || Math.floor(Date.now() / 1000); 45 | 46 | if (options.noTimestamp) { 47 | delete payload.iat; 48 | } else { 49 | payload.iat = timestamp; 50 | } 51 | 52 | if (options.expiresIn !== undefined) { 53 | if (payload.exp !== undefined) 54 | throw new InvalidSignOptionsError( 55 | 'Bad "options.expiresIn" option the payload already has an "exp" property.' 56 | ); 57 | else payload.exp = timespan(options.expiresIn, timestamp); 58 | } 59 | 60 | if (options.notBefore !== undefined) { 61 | if (payload.nbf !== undefined) { 62 | throw new InvalidSignOptionsError( 63 | 'Bad "options.notBefore" option the payload already has an "nbf" property.' 64 | ); 65 | } else payload.nbf = timespan(options.notBefore, timestamp); 66 | } 67 | 68 | return signJws(header, payload, signer, sigEncoding as BufferEncoding); 69 | } 70 | -------------------------------------------------------------------------------- /test/integration/sign.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | web3Sign, 3 | signEthers, 4 | signPolkadot, 5 | signBitcoin, 6 | metamaskSign, 7 | } from "../sharedFixtures"; 8 | import { signJWT } from "../../src"; 9 | import { expect, describe, it } from "@jest/globals"; 10 | 11 | import type { Payload, Signer, SignOptions } from "../../src"; 12 | 13 | describe("Test for signing: sign()", () => { 14 | it.each([ 15 | [web3Sign, "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD", "ES256k"], 16 | [ 17 | signPolkadot, 18 | "5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E", 19 | "SR25519", 20 | ], 21 | [metamaskSign, "0x29c76e6ad8f28bb1004902578fb108c507be341b", "ES256k"], 22 | ])( 23 | "sign with %p", 24 | async (signFunc: Signer, address: string, algorithm: string) => { 25 | const token = await signJWT( 26 | { 27 | nonce: 654321, 28 | iat: 1582062696, 29 | exp: 1782098690, 30 | iss: address, 31 | }, 32 | signFunc, 33 | { algorithm } 34 | ); 35 | 36 | expect(token).not.toBe(void 0); 37 | expect(typeof token).toBe("string"); 38 | expect(token.split(".").length).toBe(3); 39 | } 40 | ); 41 | 42 | it.each([ 43 | [signEthers, "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0", "ES256k"], 44 | [ 45 | signPolkadot, 46 | "5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E", 47 | "SR25519", 48 | ], 49 | [signBitcoin, "1HZwtseQ9YoRteyAxzt6Zq43u3Re5JKPbk", "ES256k", false], 50 | ])( 51 | "sign with %p with header in options instead of algorithm", 52 | async (signFunc: Signer, address: string, algorithm: string, isHexSig: boolean = true) => { 53 | 54 | const payload: Payload = { 55 | nonce: 654321, 56 | iat: 1582062696, 57 | exp: 1782098690, 58 | iss: address, 59 | } 60 | const signOptions: Partial & 61 | (Pick | Pick) = { header: { alg: algorithm } } 62 | 63 | if (!isHexSig) 64 | signOptions.sigEncoding = "utf8"; 65 | 66 | const token = await signJWT( 67 | payload, 68 | signFunc, 69 | signOptions 70 | ); 71 | 72 | expect(token).not.toBe(void 0); 73 | expect(typeof token).toBe("string"); 74 | expect(token.split(".").length).toBe(3); 75 | } 76 | ); 77 | }); 78 | -------------------------------------------------------------------------------- /src/schemas/verifySchemas.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const verifyOptionsSchema = z 4 | .object({ 5 | audience: z.union([ 6 | z.string().min(1,), 7 | z 8 | .array(z.string()) 9 | .min(1, 10 | "options.audience, if provided, has to be a non-empty string or an array of string." 11 | ), 12 | ]), 13 | issuer: z.union([ 14 | z.string().min(1,), 15 | z 16 | .array(z.string()) 17 | .min(1, 18 | "options.issuer, if provided, has to be a non-empty string or an array of string." 19 | ), 20 | ]), 21 | subject: z 22 | .string() 23 | .min(1, "options.subject, if provided, has to be a non-empty string."), 24 | jwtid: z 25 | .string() 26 | .min(1, "options.jwtid, if provided, has to be a non-empty string."), 27 | clockTimestamp: z 28 | .number() 29 | .positive( 30 | "options.clockTimestamp, if provided, has to be a positive number." 31 | ), 32 | nonce: z 33 | .number({ 34 | invalid_type_error: "options.nonce, if provided, has to be a number", 35 | }) 36 | .positive("options.nonce, if provided, has to be a positive integer"), 37 | ignoreNotBefore: z.boolean({ 38 | invalid_type_error: 39 | "options.ignoreNotBefore, if provided, has to be a boolean", 40 | }), 41 | clockTolerance: z 42 | .number() 43 | .positive( 44 | "options.clockTolerance, if provided, has to be a positive number." 45 | ), 46 | ignoreExpiration: z.boolean({ 47 | invalid_type_error: 48 | "options.ignoreExpiration, if provided, has to be a boolean.", 49 | }), 50 | maxAge: z 51 | .number() 52 | .positive("options.maxAge, if provided, has to be a positive number."), 53 | complete: z.boolean({ 54 | invalid_type_error: "options.complete, if provided, has to be a boolean" 55 | }), 56 | sigEncoding: z 57 | .string() 58 | .min(1, "sigEncoding, if provided, has to be a non-empty string."), 59 | algorithm: z 60 | .string() 61 | .min(1, 62 | "options.algorithm, if provided, has to be a non-empty string." 63 | ), 64 | }) 65 | .partial() 66 | .strict(); 67 | 68 | 69 | export const decodeOptionsSchema = z 70 | .object({ 71 | complete: z.boolean({ 72 | invalid_type_error: "options.complete, if provided, has to be a boolean" 73 | }), 74 | sigEncoding: z 75 | .string() 76 | .min(1, "sigEncoding, if provided, has to be a non-empty string.") 77 | }) 78 | .partial() 79 | .strict(); -------------------------------------------------------------------------------- /src/schemas/signSchemas.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const payloadSchema = z.object({ 4 | iss: z 5 | .string({ invalid_type_error: "Issuer (iss) claim has to be a string" }) 6 | .min(1, "Issuer (iss) claim has to be non-empty."), 7 | nonce: z 8 | .number({ invalid_type_error: "nonce has to be a number" }) 9 | .positive("A nonce > 0 has to be provided."), 10 | exp: z 11 | .number({ 12 | invalid_type_error: "Expiration Time (exp) claim has to be a number", 13 | }) 14 | .positive( 15 | "A positive number as Expiration Time (exp) claim has to be provided." 16 | ), 17 | iat: z 18 | .number() 19 | .positive( 20 | "Issued At (iat) claim, if provided, has to be a positive number." 21 | ) 22 | .optional(), 23 | nbf: z 24 | .number() 25 | .positive( 26 | "Not Before (nbf) claim, if provided, has to be a positive number." 27 | ) 28 | .optional(), 29 | sub: z 30 | .string() 31 | .min(1, "Subject (sub) claim, if provided, has to be a non-empty string.") 32 | .optional(), 33 | jti: z 34 | .string() 35 | .min(1, "JWT ID (jti) claim, if provided, has to be a non-empty string.") 36 | .optional(), 37 | aud: z 38 | .union([ 39 | z.string().min(1,), 40 | z 41 | .array(z.string()) 42 | .min(1, 43 | "Audience (aud) claim, if provided, has to be a non-empty string or an array of string." 44 | ), 45 | ]) 46 | .optional(), 47 | }).strict(); 48 | 49 | export const headerSchema = z.object({ 50 | alg: z.string().min(1, "header.alg has to be provided.") 51 | }).strict(); 52 | 53 | export const signOptionsSchema = z.object({ 54 | algorithm: z 55 | .string({ invalid_type_error: "Algorithm has to be a string" }) 56 | .min(1, "options.algorithm has to be provided."), 57 | header: headerSchema, 58 | sigEncoding: z 59 | .string() 60 | .min(1, "sigEncoding, if provided, has to be a non-empty string."), 61 | noTimestamp: z.boolean(), 62 | expiresIn: z 63 | .union([ 64 | z.number().positive(), 65 | z 66 | .string() 67 | .min(1, 68 | 'options.expiresIn, if provided, has to be a number of seconds or string representing a timespan eg: "1d", "20h", 60' 69 | ), 70 | ]), 71 | notBefore: z 72 | .union([ 73 | z.number().positive(), 74 | z 75 | .string() 76 | .min(1, 77 | 'options.notBefore, if provided, has to be a number of seconds or string representing a timespan eg: "1d", "20h", 60' 78 | ), 79 | ]), 80 | }).partial().strict(); 81 | -------------------------------------------------------------------------------- /src/jws/verify.ts: -------------------------------------------------------------------------------- 1 | import { Buffer } from "buffer"; 2 | import { jwaVerify } from "../jwa"; 3 | import { JwsDecodingError } from "../errors"; 4 | 5 | import type { Token, Verifier, Header, Payload } from "../types"; 6 | 7 | const JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/; 8 | 9 | function isObject(thing: any): boolean { 10 | return Object.prototype.toString.call(thing) === "[object Object]"; 11 | } 12 | 13 | function safeJsonParse(thing: any) { 14 | if (isObject(thing)) return thing; 15 | try { 16 | return JSON.parse(thing); 17 | } catch (e) { 18 | return undefined; 19 | } 20 | } 21 | 22 | function headerFromJWS(jwsSig: string): Header { 23 | let encodedHeader = jwsSig.split(".", 1)[0]; 24 | 25 | if (encodedHeader) 26 | return safeJsonParse( 27 | Buffer.from(encodedHeader, "base64").toString("binary") 28 | ); 29 | else throw new JwsDecodingError("Error decoding jws from this jwt", jwsSig); 30 | } 31 | 32 | function securedInputFromJWS(jwsSig: string): string { 33 | return jwsSig.split(".", 2).join("."); 34 | } 35 | 36 | function signatureFromJWS(jwsSig: string, sigEncoding: BufferEncoding = 'hex'): string { 37 | const sig = jwsSig.split(".")[2]; 38 | 39 | if (sig){ 40 | const signature = Buffer.from(sig, "base64").toString(sigEncoding); 41 | const returnValue = sigEncoding === 'hex' ? '0x' + signature : signature; 42 | 43 | return returnValue; 44 | } 45 | throw new JwsDecodingError("Signature not present in token", jwsSig); 46 | } 47 | 48 | function payloadFromJWS(jwsSig: string): string { 49 | let payload = jwsSig.split(".")[1]; 50 | 51 | if (payload) return Buffer.from(payload, "base64").toString('utf-8'); 52 | else throw new JwsDecodingError("Error decoding jws", jwsSig); 53 | } 54 | 55 | export function isValidJws(string: string): boolean { 56 | return JWS_REGEX.test(string) && !!headerFromJWS(string); 57 | } 58 | 59 | export async function jwsVerify( 60 | verifier: Verifier, 61 | jwsSig: string, 62 | address: string, 63 | sigEncoding?: BufferEncoding 64 | ): Promise { 65 | let signature = signatureFromJWS(jwsSig, sigEncoding); 66 | let securedInput = securedInputFromJWS(jwsSig); 67 | return jwaVerify(verifier, securedInput, signature, address); 68 | } 69 | 70 | export function decodeJws( 71 | jwsSig: string, 72 | sigEncoding?: BufferEncoding 73 | ): Token { 74 | if (!isValidJws(jwsSig)) 75 | throw new JwsDecodingError("JWT doesn't pass regex", jwsSig); 76 | 77 | let header = headerFromJWS(jwsSig); 78 | if (!header) throw new JwsDecodingError("JWT doesn't contain header", jwsSig); 79 | 80 | let payload = JSON.parse(payloadFromJWS(jwsSig)) as Payload; 81 | return { 82 | header: header, 83 | payload: payload, 84 | signature: signatureFromJWS(jwsSig, sigEncoding), 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | package-lock.json 133 | 134 | # For macOS 135 | .DS_Store 136 | 137 | # build 138 | lib -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 22 | 23 | --- 24 | 25 | ![Logo](./img/djwt-logo.jpeg) 26 | 27 |
28 | 29 | # dJWT 30 | 31 | A general purpose, Digital Signature-agnostic JWT implementation. You can plug in any Digital Signature Algorithm (DSA) using the correct [Signer](https://github.com/amany9000/dJWT/blob/28a64bd25247d4a37eb116208b31be7253a90df2/src/types.ts#L52) and [Verifier](https://github.com/amany9000/dJWT/blob/28a64bd25247d4a37eb116208b31be7253a90df2/src/types.ts#L56) interfaces. 32 | 33 | Compatibile with the current (ECDSA/RSA) and [post-quantum](https://github.com/amany9000/post-quantum-jwt) Digital Signature Schemes (Dilithium/SPHINCS+). Tested for several [major blockchains](./examples): EVM-based(ethers/metamask), Polkadot and Bitcoin. 34 | 35 | ## Installation 36 | 37 | ```sh 38 | npm install djwt 39 | ``` 40 | 41 | ## Tested Libraries/Wallets 42 | 43 | These DLT Packages/Wallets have been tested with djwt->>>> 44 | 45 | - [Ethers.js](https://github.com/ethers-io/ethers.js) 46 | - [Web3.js](https://github.com/ethereum/web3.js) 47 | - [Polkadot.js](https://github.com/polkadot-js) 48 | - [Bitcoinjs](https://github.com/bitcoinjs/bitcoinjs-lib) 49 | - [Metamask](https://github.com/metamask) 50 | 51 | ## Post-Quantum DSA 52 | 53 | As post-quantum algorithms are in infancy right now, their landscape is constantly changing with new implementations coming in every year. Hence, djwt's Pluggable DSA is very useful in implementing these future-proof JWTs. 54 | 55 | The [post-quantum-jwt](https://github.com/amany9000/post-quantum-jwt) POC showcases djwt's compatibility with Post-Quantum DSAs. It utilises [noble-post-quantum](https://github.com/paulmillr/noble-post-quantum) in the backgroud to implement the following DSA Schemes: 56 | 57 | - [DILITHIUM](https://pq-crystals.org/dilithium/index.shtml) 58 | - [SPHINCS](https://sphincs.org/index.html) 59 | 60 | ## How to use 🛠️ 61 | 62 | - For function specification refer to [Documentation](./DOCS.md). 63 | - Check out the [examples](./examples/) section for small working projects on the above mentioned signer libraries/wallets. 64 | - You can look at the types in [types.ts](./src/types.ts). 65 | 66 | --- 67 | 68 | ## ✍️ Contributing 69 | 70 | Found a bug? Create an [issue](https://github.com/amany9000/djwt/issues). 71 | 72 | If you have a contribution in mind, please check out our [Contribution Guide](https://github.com/amany9000/dJWT/blob/main/CONTRIBUTING.md) for information on how to do so. 73 | 74 | --- 75 | 76 | ## 🌟 Spread the word! 77 | 78 | If you want to say thank you and/or show support for dJWT: 79 | 80 | - Star to the project! 81 | - Tweet about the project on Twitter and tag me: [@amany_9000](https://twitter.com/amany_9000) 82 | 83 | --- 84 | 85 | ### Developer 🧑🏻‍💻 86 | 87 | - [Myself](https://github.com/amany9000) 88 | -------------------------------------------------------------------------------- /test/integration/decode.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | web3Sign, 3 | signEthers, 4 | signPolkadot, 5 | signBitcoin, 6 | metamaskSign, 7 | } from "../sharedFixtures"; 8 | import { signJWT, decodeJWT } from "../../src"; 9 | import { expect, describe, it } from "@jest/globals"; 10 | 11 | import type { Signer, SignOptions } from "../../src"; 12 | 13 | describe("Test for decoding: decode()", () => { 14 | it.each([ 15 | [web3Sign, "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD", "ES256k"], 16 | [signEthers, "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0", "ES256k"], 17 | [ 18 | signPolkadot, 19 | "5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E", 20 | "SR25519", 21 | ], 22 | [metamaskSign, "0x29c76e6ad8f28bb1004902578fb108c507be341b", "ES256k"] 23 | ])( 24 | "Complete decode with %p", 25 | async (signFunc: Signer, address: string, algorithm: string) => { 26 | const payload = { 27 | nonce: 654321, 28 | iat: 1582062696, 29 | exp: 1782098690, 30 | iss: address, 31 | nbf: 100000000, 32 | sub: address, 33 | jti: "324221", 34 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"] 35 | }; 36 | 37 | const token = await signJWT( 38 | payload, 39 | signFunc, 40 | { algorithm } 41 | ); 42 | 43 | expect(token).not.toBe(void 0); 44 | expect(typeof token).toBe("string"); 45 | expect(token.split(".").length).toBe(3); 46 | 47 | const decodedToken = decodeJWT(token, { 48 | complete: true 49 | }); 50 | 51 | expect(decodedToken).toBeDefined(); 52 | expect(typeof decodedToken).toBe("object"); 53 | 54 | expect(decodedToken!.signature).toBeDefined(); 55 | expect(decodedToken!.payload).toMatchObject(payload); 56 | expect(typeof decodedToken!.signature).toBe("string"); 57 | } 58 | ); 59 | 60 | it.each([ 61 | [web3Sign, "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD", "ES256k"], 62 | [signEthers, "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0", "ES256k"], 63 | [ 64 | signPolkadot, 65 | "5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E", 66 | "SR25519", 67 | ], 68 | [signBitcoin, "1HZwtseQ9YoRteyAxzt6Zq43u3Re5JKPbk", "ES256k", false] 69 | ])( 70 | "Partial decode with %p.3", 71 | async (signFunc: Signer, address: string, algorithm: string, isHexSig: boolean = true) => { 72 | const payload = { 73 | nonce: 654321, 74 | iat: 1582062696, 75 | exp: 1782098690, 76 | iss: address, 77 | nbf: 100000000, 78 | sub: address, 79 | jti: "324221", 80 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"] 81 | }; 82 | 83 | const signOptions: Partial & 84 | (Pick | Pick) = { header: { alg: algorithm } } 85 | 86 | if (!isHexSig) 87 | signOptions.sigEncoding = "utf8"; 88 | 89 | const token = await signJWT( 90 | payload, 91 | signFunc, 92 | signOptions 93 | ); 94 | 95 | expect(token).not.toBe(void 0); 96 | expect(typeof token).toBe("string"); 97 | expect(token.split(".").length).toBe(3); 98 | 99 | const payloadDecoded = decodeJWT(token); 100 | 101 | expect(payloadDecoded).toBeDefined(); 102 | expect(typeof payloadDecoded).toBe("object"); 103 | expect(payloadDecoded).toMatchObject(payload); 104 | } 105 | ); 106 | }); 107 | -------------------------------------------------------------------------------- /test/error/errorTests.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | web3Sign, 3 | signEthers, 4 | signPolkadot, 5 | signBitcoin, 6 | metamaskSign, 7 | verifyBitcoin, 8 | metamaskVerify, 9 | } from "../sharedFixtures"; 10 | import { expect, describe, it } from "@jest/globals"; 11 | 12 | import type { Signer, Verifier, SignOptions } from "../../src"; 13 | import { 14 | signJWT, 15 | verifyJWT, 16 | InvalidSignOptionsError, 17 | OptionsVerificationError, 18 | } from "../../src"; 19 | 20 | describe("Test for error: sign()", () => { 21 | it.each([ 22 | [web3Sign, "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD", "ES256k"], 23 | [ 24 | signPolkadot, 25 | "5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E", 26 | "SR25519", 27 | ], 28 | ])( 29 | "Fails due to expiresIn provided in signOption along with payload.exp, Signer: %p", 30 | async (signFunc: Signer, address: string, algorithm: string) => { 31 | expect( 32 | signJWT( 33 | { 34 | nonce: 654321, 35 | iat: 1582062696, 36 | exp: 1782098690, 37 | iss: address, 38 | nbf: 1682098690, 39 | }, 40 | signFunc, 41 | { algorithm, notBefore: "1d" } 42 | ) 43 | ).rejects.toThrow(InvalidSignOptionsError); 44 | } 45 | ); 46 | 47 | it.each([ 48 | [signEthers, "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0", "ES256k"], 49 | [signBitcoin, "1HZwtseQ9YoRteyAxzt6Zq43u3Re5JKPbk", "ES256k"], 50 | ])( 51 | "Fails due to expiresIn provided in signOption along with payload.exp, Signer: %p", 52 | async (signFunc: Signer, address: string, algorithm: string) => { 53 | expect( 54 | signJWT( 55 | { 56 | nonce: 654321, 57 | iat: 1582062696, 58 | exp: 1782098690, 59 | iss: address, 60 | }, 61 | signFunc, 62 | { algorithm, expiresIn: 12231132112 } 63 | ) 64 | ).rejects.toThrow(InvalidSignOptionsError); 65 | } 66 | ); 67 | }); 68 | 69 | describe("Test errors for for verification: verify()", () => { 70 | it.each([ 71 | [ 72 | metamaskSign, 73 | metamaskVerify as Verifier, 74 | "0x29c76e6ad8f28bb1004902578fb108c507be341b", 75 | "ES256k", 76 | ], 77 | [ 78 | signBitcoin, 79 | verifyBitcoin as Verifier, 80 | "1HZwtseQ9YoRteyAxzt6Zq43u3Re5JKPbk", 81 | "ES256k", 82 | false 83 | ], 84 | ])( 85 | "Verification fails because of incorrect options.algorithm, Signer: %p", 86 | async ( 87 | signFunc: Signer, 88 | verifierFunc: Verifier, 89 | address: string, 90 | algorithm: string, 91 | isHexSig: boolean = true 92 | ) => { 93 | const payload = { 94 | nonce: 654321, 95 | iat: 1582062696, 96 | exp: 1782098690, 97 | iss: address, 98 | }; 99 | 100 | const signOptions: Partial & 101 | (Pick | Pick) = { header: { alg: algorithm } } 102 | 103 | if (!isHexSig) 104 | signOptions.sigEncoding = "utf8"; 105 | 106 | const token = await signJWT(payload, signFunc, signOptions); 107 | expect(token).not.toBe(void 0); 108 | expect(typeof token).toBe("string"); 109 | expect(token.split(".").length).toBe(3); 110 | 111 | expect( 112 | async () => await verifyJWT(token, verifierFunc, { 113 | complete: true, 114 | nonce: 654321, 115 | algorithm: "SR25519", 116 | }) 117 | ).rejects.toThrow(OptionsVerificationError); 118 | } 119 | ); 120 | }); 121 | -------------------------------------------------------------------------------- /src/verify.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | import { decodeJWT } from "./decode"; 4 | import { timespan } from "./utils"; 5 | import { jwsVerify } from "./jws"; 6 | import { 7 | VerificationError, 8 | InvalidPayloadError, 9 | TokenExpiredError, 10 | NotBeforeError, 11 | OptionsVerificationError, 12 | } from "./errors"; 13 | import { verifyOptionsSchema, headerSchema, payloadSchema } from "./schemas"; 14 | 15 | import type { VerifyOptions, Verifier, TokenOrPayload } from "./types"; 16 | 17 | export async function verifyJWT( 18 | jwtString: string, 19 | verifier: Verifier, 20 | options?: Partial 21 | ): Promise { 22 | //clone this object since we are going to mutate it. 23 | options = Object.assign({}, options); 24 | 25 | const optionsParseResult = verifyOptionsSchema.safeParse(options); 26 | if (!optionsParseResult.success) { 27 | throw new VerificationError( 28 | JSON.parse(optionsParseResult.error.message)[0].message 29 | ); 30 | } 31 | 32 | jwtString = z 33 | .string({ 34 | invalid_type_error: "jwtString must be provided", 35 | }) 36 | .min(1, "jwtString must be non-empty") 37 | .parse(jwtString); 38 | 39 | if (jwtString.split(".").length !== 3) { 40 | throw new VerificationError("jwt malformed"); 41 | } 42 | 43 | const clockTimestamp = 44 | options.clockTimestamp || Math.floor(Date.now() / 1000); 45 | 46 | const decodedToken = decodeJWT(jwtString, { 47 | complete: true, 48 | sigEncoding: options.sigEncoding 49 | }); 50 | 51 | if (!decodedToken) { 52 | throw new VerificationError("Invalid token"); 53 | } 54 | 55 | if (!decodedToken.header) { 56 | throw new VerificationError( 57 | "Invalid token decoding, header not present in decoded token" 58 | ); 59 | } 60 | 61 | if (!decodedToken.payload) { 62 | throw new VerificationError( 63 | "Invalid token decoding, payload not present in decoded token" 64 | ); 65 | } 66 | 67 | if (!decodedToken.signature) { 68 | throw new VerificationError( 69 | "Invalid token decoding, signature not present in decoded token" 70 | ); 71 | } 72 | 73 | const header = decodedToken.header; 74 | const headerParseResult = headerSchema.safeParse(header); 75 | if (!headerParseResult.success) { 76 | throw new VerificationError( 77 | JSON.parse(headerParseResult.error.message)[0].message 78 | ); 79 | } 80 | 81 | const payload = decodedToken.payload; 82 | const payloadParseResult = payloadSchema.safeParse(payload); 83 | if (!payloadParseResult.success) { 84 | throw new InvalidPayloadError( 85 | JSON.parse(payloadParseResult.error.message)[0].message 86 | ); 87 | } 88 | 89 | if (options.algorithm) { 90 | if (options.algorithm !== header.alg) 91 | throw new OptionsVerificationError( 92 | "Header.alg is incorrect.", 93 | header.alg, 94 | options.algorithm 95 | ); 96 | } 97 | 98 | if (payload.nbf !== undefined && !options.ignoreNotBefore) { 99 | if (payload.nbf > clockTimestamp + (options.clockTolerance || 0)) { 100 | throw new NotBeforeError("jwt not active", payload.nbf); 101 | } 102 | } 103 | 104 | if (payload.exp !== undefined && !options.ignoreExpiration) { 105 | if (clockTimestamp >= payload.exp + (options.clockTolerance || 0)) { 106 | throw new TokenExpiredError("jwt expired", payload.exp); 107 | } 108 | } 109 | 110 | if (options.audience) { 111 | if (payload.aud === undefined) 112 | throw new VerificationError( 113 | "options.audience is present but payload.aud is not" 114 | ); 115 | 116 | const audiences = Array.isArray(options.audience) 117 | ? options.audience 118 | : [options.audience]; 119 | const target = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; 120 | 121 | const match = target.some(function (targetAudience) { 122 | return audiences.some(function (audience: any) { 123 | return audience instanceof RegExp 124 | ? audience.test(targetAudience) 125 | : audience === targetAudience; 126 | }); 127 | }); 128 | 129 | if (!match) { 130 | throw new OptionsVerificationError( 131 | "JWT audience is incorrect.", 132 | target.join(" or "), 133 | audiences.join(" or ") 134 | ); 135 | } 136 | } 137 | 138 | if (options.issuer) { 139 | const invalid_issuer = 140 | payload.iss !== options.issuer || 141 | (Array.isArray(options.issuer) && 142 | options.issuer.indexOf(payload.iss) === -1); 143 | 144 | if (invalid_issuer) { 145 | throw new OptionsVerificationError( 146 | "JWT issuer invalid.", 147 | payload.iss, 148 | Array.isArray(options.issuer) 149 | ? options.issuer.join(" or ") 150 | : options.issuer 151 | ); 152 | } 153 | } 154 | 155 | if (options.subject) { 156 | if (payload.sub !== options.subject) { 157 | throw new OptionsVerificationError( 158 | "JWT subject invalid.", 159 | payload.sub ? payload.sub : "undefined", 160 | options.subject 161 | ); 162 | } 163 | } 164 | 165 | if (options.jwtid) { 166 | if (payload.jti !== options.jwtid) { 167 | throw new OptionsVerificationError( 168 | "JWT ID invalid.", 169 | payload.jti ? payload.jti : "undefined", 170 | options.jwtid 171 | ); 172 | } 173 | } 174 | 175 | if (options.nonce) { 176 | if (payload.nonce !== options.nonce) { 177 | throw new OptionsVerificationError( 178 | "Signature nonce invalid.", 179 | payload.nonce, 180 | options.nonce 181 | ); 182 | } 183 | } 184 | 185 | if (options.maxAge) { 186 | if (typeof payload.iat !== "number") { 187 | throw new VerificationError("iat required when maxAge is specified"); 188 | } 189 | 190 | const maxAgeTimestamp = timespan(options.maxAge, payload.iat); 191 | if (maxAgeTimestamp === undefined) { 192 | throw new VerificationError( 193 | '"maxAge" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60' 194 | ); 195 | } 196 | if (clockTimestamp >= maxAgeTimestamp + (options.clockTolerance || 0)) { 197 | throw new TokenExpiredError("maxAge exceeded", maxAgeTimestamp); 198 | } 199 | } 200 | 201 | const valid = await jwsVerify(verifier, jwtString, decodedToken.payload.iss, options.sigEncoding); 202 | 203 | if (!valid) { 204 | throw new VerificationError("Invalid signature"); 205 | } 206 | 207 | const signature = decodedToken.signature; 208 | if (options.complete) 209 | return { 210 | header: header, 211 | payload: payload, 212 | signature: signature, 213 | }; 214 | else return payload; 215 | } 216 | -------------------------------------------------------------------------------- /test/integration/verify.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | web3Sign, 3 | web3Verify, 4 | signEthers, 5 | signPolkadot, 6 | validateSigPolkadot, 7 | signBitcoin, 8 | verifyBitcoin, 9 | metamaskSign, 10 | metamaskVerify, 11 | } from "../sharedFixtures"; 12 | import { signJWT, verifyJWT } from "../../src"; 13 | import { expect, describe, it } from "@jest/globals"; 14 | import * as ethers from "ethers"; 15 | import { Signer, Verifier } from "../../src"; 16 | import type { SignOptions, VerifyOptions } from "../../src"; 17 | 18 | describe("Test for verification: verify()", () => { 19 | it.each([ 20 | [ 21 | web3Sign, 22 | web3Verify as Verifier, 23 | "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD", 24 | "ES256k", 25 | ], 26 | [ 27 | signEthers, 28 | ethers.verifyMessage as Verifier, 29 | "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0", 30 | "ES256k", 31 | ], 32 | [ 33 | signPolkadot, 34 | validateSigPolkadot as Verifier, 35 | "5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E", 36 | "SR25519", 37 | ], 38 | [ 39 | signBitcoin, 40 | verifyBitcoin as Verifier, 41 | "1HZwtseQ9YoRteyAxzt6Zq43u3Re5JKPbk", 42 | "ES256k", 43 | false 44 | ], 45 | [ 46 | metamaskSign, 47 | metamaskVerify as Verifier, 48 | "0x29c76e6ad8f28bb1004902578fb108c507be341b", 49 | "ES256k", 50 | ], 51 | ])( 52 | "sign the payload with %p, get the token and verify it with %p", 53 | async ( 54 | signFunc: Signer, 55 | verifierFunc: Verifier, 56 | address: string, 57 | algorithm: string, 58 | isHexSig: boolean = true 59 | ) => { 60 | const payload = { 61 | nonce: 654321, 62 | iat: 1582062696, 63 | exp: 1782098690, 64 | iss: address, 65 | nbf: 100000000, 66 | sub: address, 67 | jti: "324221", 68 | }; 69 | 70 | const signOptions: Partial & 71 | (Pick | Pick) = { header: { alg: algorithm } } 72 | 73 | const verifyOption : Partial = { 74 | complete: true, 75 | nonce: 654321, 76 | maxAge: 10000000000, 77 | issuer: address, 78 | jwtid: "324221", 79 | subject: address, 80 | algorithm, 81 | } 82 | 83 | if (!isHexSig){ 84 | signOptions.sigEncoding = "utf8"; 85 | verifyOption.sigEncoding = "utf8"; 86 | } 87 | 88 | const token = await signJWT(payload, signFunc, signOptions); 89 | expect(token).not.toBe(void 0); 90 | expect(typeof token).toBe("string"); 91 | expect(token.split(".").length).toBe(3); 92 | 93 | const receivedToken = await verifyJWT(token, verifierFunc, verifyOption); 94 | expect(receivedToken.payload).toMatchObject(payload); 95 | expect(receivedToken.signature).toBeDefined(); 96 | expect(typeof receivedToken.signature).toBe("string"); 97 | } 98 | ); 99 | 100 | it.each([ 101 | [ 102 | web3Sign, 103 | web3Verify as Verifier, 104 | "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD", 105 | "ES256k", 106 | ], 107 | [ 108 | signEthers, 109 | ethers.verifyMessage as Verifier, 110 | "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0", 111 | "ES256k", 112 | ], 113 | [ 114 | metamaskSign, 115 | metamaskVerify as Verifier, 116 | "0x29c76e6ad8f28bb1004902578fb108c507be341b", 117 | "ES256k", 118 | ], 119 | ])( 120 | "Check the audience during signing with %p and verifying with %p", 121 | async ( 122 | signFunc: Signer, 123 | verifierFunc: Verifier, 124 | address: string, 125 | algorithm: string 126 | ) => { 127 | // Audience in this case is an EVM-chain address of the verifier of the JWT. 128 | const payload = { 129 | nonce: 654321, 130 | iat: 1582062696, 131 | exp: 1782098690, 132 | iss: address, 133 | nbf: 100000000, 134 | sub: address, 135 | jti: "324221", 136 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"], 137 | }; 138 | const token = await signJWT(payload, signFunc, { algorithm }); 139 | expect(token).not.toBe(void 0); 140 | expect(typeof token).toBe("string"); 141 | expect(token.split(".").length).toBe(3); 142 | 143 | const receivedToken = await verifyJWT( token, verifierFunc, { 144 | complete: true, 145 | nonce: 654321, 146 | maxAge: 10000000000, 147 | issuer: address, 148 | jwtid: "324221", 149 | subject: address, 150 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 151 | algorithm, 152 | }); 153 | expect(receivedToken.payload).toMatchObject(payload); 154 | expect(receivedToken.signature).toBeDefined(); 155 | expect(typeof receivedToken.signature).toBe("string"); 156 | } 157 | ); 158 | 159 | it.each([ 160 | [ 161 | web3Sign, 162 | web3Verify as Verifier, 163 | "0x231a5147b7c2bDF1dc8449Da0DeF741077447bCD", 164 | "ES256k", 165 | ], 166 | [ 167 | signEthers, 168 | ethers.verifyMessage as Verifier, 169 | "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0", 170 | "ES256k", 171 | ], 172 | [ 173 | signPolkadot, 174 | validateSigPolkadot as Verifier, 175 | "5F7MBfGdyTg5th5gzsWMUyaVBRUkhEZw5Q82rPrtSP1q9F3E", 176 | "SR25519", 177 | ], 178 | ])( 179 | "Signing with %p and verifying with %p without `complete:false`", 180 | async ( 181 | signFunc: Signer, 182 | verifierFunc: Verifier, 183 | address: string, 184 | algorithm: string 185 | ) => { 186 | // Audience in this case is an EVM-chain address of the verifier of the JWT. 187 | const payload = { 188 | nonce: 654321, 189 | iat: 1582062696, 190 | exp: 1782098690, 191 | iss: address, 192 | nbf: 100000000, 193 | sub: address, 194 | jti: "324221", 195 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"], 196 | }; 197 | const token = await signJWT(payload, signFunc, { algorithm }); 198 | expect(token).not.toBe(void 0); 199 | expect(typeof token).toBe("string"); 200 | expect(token.split(".").length).toBe(3); 201 | 202 | const receivedPayload = await verifyJWT(token, verifierFunc, { 203 | nonce: 654321, 204 | maxAge: 10000000000, 205 | issuer: address, 206 | jwtid: "324221", 207 | subject: address, 208 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 209 | algorithm, 210 | }); 211 | 212 | expect(receivedPayload).toBeDefined(); 213 | expect(typeof receivedPayload).toBe("object"); 214 | expect(receivedPayload).toMatchObject(payload); 215 | } 216 | ); 217 | }); 218 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | /** @type {import('jest').Config} */ 7 | const config = { 8 | // All imported modules in your tests should be mocked automatically 9 | // automock: false, 10 | 11 | // Stop running tests after `n` failures 12 | // bail: 0, 13 | 14 | // The directory where Jest should store its cached dependency information 15 | // cacheDirectory: "/private/var/folders/4s/7ssv6ww50zz7xwjytp0_j7f80000gn/T/jest_dx", 16 | 17 | // Automatically clear mock calls, instances, contexts and results before every test 18 | clearMocks: true, 19 | 20 | // Indicates whether the coverage information should be collected while executing the test 21 | collectCoverage: true, 22 | 23 | // An array of glob patterns indicating a set of files for which coverage information should be collected 24 | // collectCoverageFrom: undefined, 25 | 26 | // The directory where Jest should output its coverage files 27 | coverageDirectory: "coverage", 28 | 29 | // An array of regexp pattern strings used to skip coverage collection 30 | coveragePathIgnorePatterns: [ 31 | "/node_modules/", 32 | "sharedFixtures" 33 | ], 34 | 35 | // Indicates which provider should be used to instrument code for coverage 36 | coverageProvider: "v8", 37 | 38 | // A list of reporter names that Jest uses when writing coverage reports 39 | // coverageReporters: [ 40 | // "json", 41 | // "text", 42 | // "lcov", 43 | // "clover" 44 | // ], 45 | 46 | // An object that configures minimum threshold enforcement for coverage results 47 | // coverageThreshold: undefined, 48 | 49 | // A path to a custom dependency extractor 50 | // dependencyExtractor: undefined, 51 | 52 | // Make calling deprecated APIs throw helpful error messages 53 | // errorOnDeprecated: false, 54 | 55 | // The default configuration for fake timers 56 | // fakeTimers: { 57 | // "enableGlobally": false 58 | // }, 59 | 60 | // Force coverage collection from ignored files using an array of glob patterns 61 | // forceCoverageMatch: [], 62 | 63 | // A path to a module which exports an async function that is triggered once before all test suites 64 | // globalSetup: undefined, 65 | 66 | // A path to a module which exports an async function that is triggered once after all test suites 67 | // globalTeardown: undefined, 68 | 69 | // A set of global variables that need to be available in all test environments 70 | // globals: {}, 71 | 72 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 73 | // maxWorkers: "50%", 74 | 75 | // An array of directory names to be searched recursively up from the requiring module's location 76 | // moduleDirectories: [ 77 | // "node_modules" 78 | // ], 79 | 80 | // An array of file extensions your modules use 81 | // moduleFileExtensions: [ 82 | // "js", 83 | // "mjs", 84 | // "cjs", 85 | // "jsx", 86 | // "ts", 87 | // "tsx", 88 | // "json", 89 | // "node" 90 | // ], 91 | 92 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 93 | // moduleNameMapper: {}, 94 | 95 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 96 | // modulePathIgnorePatterns: [], 97 | 98 | // Activates notifications for test results 99 | // notify: false, 100 | 101 | // An enum that specifies notification mode. Requires { notify: true } 102 | // notifyMode: "failure-change", 103 | 104 | // A preset that is used as a base for Jest's configuration 105 | preset: "ts-jest", 106 | 107 | // Run tests from one or more projects 108 | // projects: undefined, 109 | 110 | // Use this configuration option to add custom reporters to Jest 111 | // reporters: undefined, 112 | 113 | // Automatically reset mock state before every test 114 | // resetMocks: false, 115 | 116 | // Reset the module registry before running each individual test 117 | // resetModules: false, 118 | 119 | // A path to a custom resolver 120 | // resolver: undefined, 121 | 122 | // Automatically restore mock state and implementation before every test 123 | // restoreMocks: false, 124 | 125 | // The root directory that Jest should scan for tests and modules within 126 | // rootDir: undefined, 127 | 128 | // A list of paths to directories that Jest should use to search for files in 129 | // roots: [ 130 | // "" 131 | // ], 132 | 133 | // Allows you to use a custom runner instead of Jest's default test runner 134 | // runner: "jest-runner", 135 | 136 | // The paths to modules that run some code to configure or set up the testing environment before each test 137 | // setupFiles: [], 138 | 139 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 140 | // setupFilesAfterEnv: [], 141 | 142 | // The number of seconds after which a test is considered as slow and reported as such in the results. 143 | // slowTestThreshold: 5, 144 | 145 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 146 | // snapshotSerializers: [], 147 | 148 | // The test environment that will be used for testing 149 | // testEnvironment: "jest-environment-node", 150 | 151 | // Options that will be passed to the testEnvironment 152 | // testEnvironmentOptions: {}, 153 | 154 | // Adds a location field to test results 155 | // testLocationInResults: false, 156 | 157 | // The glob patterns Jest uses to detect test files 158 | testMatch: [ 159 | "**/test/integration/**/*.[jt]s?(x)", 160 | "**/test/error/**/*.[jt]s?(x)" 161 | ], 162 | 163 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 164 | // testPathIgnorePatterns: [ 165 | // "/node_modules/" 166 | // ], 167 | 168 | // The regexp pattern or array of patterns that Jest uses to detect test files 169 | // testRegex: [], 170 | 171 | // This option allows the use of a custom results processor 172 | // testResultsProcessor: undefined, 173 | 174 | // This option allows use of a custom test runner 175 | // testRunner: "jest-circus/runner", 176 | 177 | // A map from regular expressions to paths to transformers 178 | // transform: undefined, 179 | 180 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 181 | // transformIgnorePatterns: [ 182 | // "/node_modules/", 183 | // "\\.pnp\\.[^\\/]+$" 184 | // ], 185 | 186 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 187 | // unmockedModulePathPatterns: undefined, 188 | 189 | // Indicates whether each individual test should be reported during the run 190 | verbose: true, 191 | 192 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 193 | // watchPathIgnorePatterns: [], 194 | 195 | // Whether to use watchman for file crawling 196 | // watchman: true, 197 | }; 198 | 199 | module.exports = config; 200 | -------------------------------------------------------------------------------- /DOCS.md: -------------------------------------------------------------------------------- 1 | # `Documentation` 2 | 3 | ## **Installation**: 4 | 5 | ```console 6 | npm install djwt 7 | ``` 8 | 9 | ## **Functions**: 10 | 11 | ### **`async function signJWT(payload: Payload, signer: Signer, options Partial) : Promise`** 12 | 13 | (Asynchronous) Return a JSON Web Token (string) after signing the `payload.header` with the signer(). 14 | 15 | **Parameters** 16 | 17 | | parameter | required/optional | type | description | 18 | | :---: | :---: | :---: | :---: | 19 | | payload | required | Payload | JWT Payload | 20 | | signer | required | Signer | Function that will sign the `payload.header` (see function signature below) | 21 | | options | required | SignOptions | JWT Creation Options (must contain header or algorithm) | 22 | 23 | `Type Payload fields:` 24 | * `iss` (string) - Issuer claim, address of the issuer of the JWT. 25 | * `nonce` (number) - Random signature nonce. 26 | * `exp` (string) - Expiration claim, expiration timestamp of the JWT. 27 | * `iat` (number) (optional) - Issued at claim, issuance timestamp of the JWT. 28 | * `nbf` (number) (optional) - Not before claim, the time span before which the JWT 29 | is invalid. 30 | * `sub` (string) (optional) - Subject claim, the subject of the JWT. 31 | * `jti` (string) (optional) - The JWT ID claim, unique identifier for the JWT. 32 | * `aud` (string | string[]) (optional) - The Audience claim, recipient or group of recipients of the JWT. 33 | 34 | `Interface Signer:` 35 | * Signer is your JWT (`payload.header`) signing function which must satisfy this call signature : 36 | ```js 37 | (payload: string): Promise | string; 38 | ``` 39 | For example, [signBitcoin](./examples/bitcoinjs/signBitcoin.ts) is a valid Signer. 40 | 41 | `Type SignOptions fields:` 42 | * `algorithm` (string) - The algorithm used for signing `payload.header` by `signer()`. 43 | * `header` (Header) - The Header object of the JWT. 44 | * `sigEncoding` (BufferEncoding) (optional) - The encoding of the JWT Signature. 45 | * `noTimestamp` (boolean) (optional): `iat` (issued at) field is not included in the payload if noTimestamp is set `true`. 46 | * `expiresIn` (number | string) (optional): The time span of JWT expiration. All inputs for [ms](https://github.com/vercel/ms) are valid. 47 | * `notBefore` (number | string) (optional): The time span before which the JWT is invalid. All inputs for [ms](https://github.com/vercel/ms) are valid. 48 | 49 | **Return Type** 50 | * Returns the JWT string as `Promise`. 51 | 52 | **Example** 53 | ```js 54 | async function test() { 55 | const address = "0x29c76e6ad8f28bb1004902578fb108c507be341b"; 56 | const algorithm = "ES256k"; 57 | 58 | const payload = { 59 | nonce: 654321, 60 | iat: 1582062696, 61 | exp: 1782098690, 62 | iss: address, 63 | nbf: 100000000, 64 | sub: address, 65 | jti: "324221", 66 | aud: ["0x75FaBc80c774614C424ffC1d7017b4a534607935"] 67 | }; 68 | const token = await signJWT(payload, metamaskSign, { algorithm }); 69 | } 70 | 71 | // metamaskSign.ts 72 | import {personalSign} from "@metamask/eth-sig-util"; 73 | 74 | function metamaskSign(message: string): string{ 75 | const key = Buffer.from("4af1bceebf7f3634ec3cff8a2c38e51178d5d4ce585c52d6043e5e2cc3418bb0", 'hex'); 76 | const signature = personalSign({ privateKey: key, data: message }); 77 | return signature; 78 | } 79 | 80 | ``` 81 | 82 | ### **`function verifyJWT(jwtString: string, verifier: Verifier, options?: Partial): TokenOrPayload `** 83 | 84 | Verifies the JWT string for claims and signature and returns the entire token object or just the payload object. Signature verification is done using the `verifier` function. 85 | 86 | **Parameters** 87 | 88 | | parameter | required/optional | type | description | 89 | | :---: | :---: | :---: | :---: | 90 | | jwtString | required | string | The JWT | 91 | | verifier | required | Verifier | Fuction that will verify the signature in the JWT. | 92 | | options | optional | `Partial` | JWT Verification Options | 93 | 94 | `Interface Verifier:` 95 | * Verifier is your JWT signature verification function which must satisfy this call signature : 96 | ```js 97 | (payload: string, signature: string, address: string): boolean | string; 98 | ``` 99 | * If verifier returns a string then that string will be matched with `address`(`payload.iss`), you can pass in a function with signature call `(payload: string, signature: string): string` in case you are using some address recovery function (used in ECDSA in the EVM-based chains), for e.g. [web3Verify](./examples/web3/web3Verify.ts) and [metamaskVerify](./examples/metamask/metamaskVerify.ts). 100 | * [verifyPolkadot](./examples/polkadot.js/verifyPolkadot.ts) is a also valid Verifier. 101 | 102 | `Type VerifyOptions fields:` 103 | * `algorithm` (string) - To be matched with `header.alg` during verification. 104 | * `complete` (boolean) - `verify` returns the`Token` object if `options.complete=true` otherwise it returns just the `Payload` object. 105 | * `issuer` (string | string[]) - Will be matched with the Issuer claim: `token.payload.iss`. 106 | * `subject` (string) - Will be matched with the Subject claim: `token.payload.sub`. 107 | * `jwtid` (string) - Will be matched with the JWT ID claim: `token.payload.jti`. 108 | * `audience` (string | string[]) - Will be matched with the Audience claim: `token.payload.aud`. 109 | * `nonce` (number) - Will be matched with the signature nonce: `token.payload.nonce`. 110 | * `maxage` (number | string) - The timestamp till which the token is valid. All inputs for [ms](https://github.com/vercel/ms) are valid. 111 | * `clockTimestamp` (number): The time in seconds, to be used as the current time. 112 | * `clockTolerance` (number): The number of seconds to tolerate when checking for all time-based claims: nbf, exp and maxage. 113 | * `ignoreExpiration` (boolean): If true, ignore the Expiration claim: `token.payload.exp`. 114 | * `ignoreNotBefore` (boolean): If true, ignore the Not Before claim: `token.payload.nbf`. 115 | 116 | Note: Since `options` are of the type `Partial`, all these fields are optional. 117 | 118 | **Return Type** 119 | Returns the either the `Token` object if `options.complete=true` otherwise just returns the `Payload` object. 120 | 121 | `Type Token fields:` 122 | * `header` (Header) - The Header object of the JWT. 123 | * `payload` (Payload) - The Payload object of the (see above for Payload type fields). 124 | * `signature` (String) - The signature string of the JWT. 125 | 126 | 127 | **Example** 128 | ```js 129 | import {verifyMessage} from "ethers"; 130 | 131 | const address = "0x145831eba8085d78c1d30A9C108aAD8A1501d6e0"; 132 | const algorithm = "ES256k"; 133 | 134 | const receivedToken = verifyJWT(tokenString, verifyMessage, { 135 | complete: true, 136 | nonce: 654321, 137 | maxAge: 10000000000, 138 | issuer: address, 139 | jwtid: "324221", 140 | subject: address, 141 | audience: "0x75FaBc80c774614C424ffC1d7017b4a534607935", 142 | algorithm, 143 | }); 144 | ``` 145 | 146 | 147 | ### **`function decodeJWT(jwtString: string, options?: Partial): TokenOrPayload`** 148 | 149 | Decodes the JWT string. 150 | 151 | **Parameters** 152 | 153 | | parameter | required/optional | type | description | 154 | | :---: | :---: | :---: | :---: | 155 | | jwtString | required | string | The JWT | 156 | | options | optional | `Partial` | JWT Decoding Options | 157 | 158 | `Type DecodeOptions fields:` 159 | * `complete` (boolean) - `decode` returns the`Token` object if `options.complete=true` otherwise it returns just the `Payload` object. 160 | * `sigEncoding` (BufferEncoding) (optional) - The encoding of the JWT Signature. 161 | 162 | Note: Since `options` are of the type `Partial`, both of these fields are optional. 163 | 164 | 165 | **Return Type** 166 | Returns the either the `Token` object if `options.complete=true` otherwise just returns the `Payload` object. 167 | 168 | 169 | **Example** 170 | ```js 171 | const payloadDecoded = decodeJWT(tokenString); 172 | ``` -------------------------------------------------------------------------------- /examples/ethers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/web3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/bitcoinjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/metamask/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/polkadot.js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | --------------------------------------------------------------------------------