Skip to content

Commit

Permalink
feat(stream): add verify signature function
Browse files Browse the repository at this point in the history
  • Loading branch information
sogunshola committed Sep 15, 2022
1 parent 7a8d21d commit c719cc2
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 32 deletions.
6 changes: 6 additions & 0 deletions .changeset/fuzzy-tomatoes-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@moralisweb3/core': minor
'@moralisweb3/streams': minor
---

Added verifySignature utility function
27 changes: 10 additions & 17 deletions demos/moralis-stream/src/hook.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
/* eslint-disable no-console */
import express, { NextFunction, Request, Response } from 'express';
import config from './config';
import fs from 'fs';
import Web3Utils from 'web3-utils';
import Moralis from 'moralis';

async function webHook(req: Request, res: Response, next: NextFunction) {
try {
verifySignature(req);
// Verify request is from Moralis
const providedSignature = req.headers['x-signature'];
if (!providedSignature) {
throw new Error('No signature provided');
}
Moralis.Streams.verifySignature({
body: req.body,
signature: providedSignature as string,
});
// Handle data
writeTofile(req.body);

Expand Down Expand Up @@ -37,20 +44,6 @@ function writeTofile(data: any) {
});
}

function verifySignature(req: Request) {
const providedSignature = req.headers['x-signature'];
if (!providedSignature) {
throw new Error('No signature provided');
}
const generatedSignature = Web3Utils.sha3(JSON.stringify(req.body) + config.STREAM_SECRET);
if (providedSignature !== generatedSignature) {
throw new Error('Invalid signature');
}

// eslint-disable-next-line no-console
console.log('Signature verified');
}

async function getEvents(req: Request, res: Response, next: NextFunction) {
try {
fs.readFile('data.json', 'utf8', (err, result) => {
Expand Down
3 changes: 0 additions & 3 deletions demos/moralis-stream/src/stream/streamService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,11 @@ export async function updateStream(id: string, options: StreamOptions) {

export async function setSettings({
region,
secretKey,
}: {
secretKey: string;
region: 'us-east-1' | 'us-west-2' | 'eu-central-1' | 'ap-southeast-1';
}) {
const result = await Moralis.Streams.setSettings({
region,
secretKey,
});

return result.raw;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/Error/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export enum StreamErrorCode {
GENERIC_STREAM_ERROR = 'S0001',
INCORRECT_NETWORK = 'S0002',
INCORRECT_PARAMETER = 'S0003',
INVALID_SIGNATURE = 'S0004',

NOT_IMPLEMENTED = 'S9000',
}
Expand Down
3 changes: 2 additions & 1 deletion packages/streams/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dependencies": {
"@moralisweb3/api-utils": "^2.4.0",
"@moralisweb3/core": "^2.4.0",
"@moralisweb3/evm-utils": "^2.4.0"
"@moralisweb3/evm-utils": "^2.4.0",
"web3-utils": "^1.7.5"
}
}
17 changes: 15 additions & 2 deletions packages/streams/src/MoralisStreams.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { readSettings, setSettings } from './resolvers';
import { Endpoints } from '@moralisweb3/api-utils';
import { ApiModule, MoralisCore, MoralisCoreProvider } from '@moralisweb3/core';
import { ApiConfig, Endpoints } from '@moralisweb3/api-utils';
import { ApiModule, MoralisCore, MoralisCoreProvider, MoralisStreamError, StreamErrorCode } from '@moralisweb3/core';
import { createStream, CreateStreamOptions } from './methods/create';
import { updateStream, UpdateStreamOptions } from './methods/update';
import { deleteStream, DeleteStreamOptions } from './methods/delete';
import { GetStreamsOptions, getStreams } from './methods/getAll';
import { verifySignature, VerifySignatureOptions } from './utils/verifySignature';

export const BASE_URL = 'https://streams-api.aws-prod-streams-master-1.moralis.io';

Expand Down Expand Up @@ -42,4 +43,16 @@ export class MoralisStreams extends ApiModule {
private readonly _readSettings = this.endpoints.createFetcher(readSettings);

public readonly readSettings = () => this._readSettings({});

public readonly verifySignature = (options: VerifySignatureOptions) => {
const apiKey = this.core.config.get(ApiConfig.apiKey);

if (!apiKey) {
throw new MoralisStreamError({
code: StreamErrorCode.GENERIC_STREAM_ERROR,
message: 'unable to verify signature without an api key',
});
}
return verifySignature(options, apiKey);
};
}
22 changes: 15 additions & 7 deletions packages/streams/src/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface paths {
};
"/settings": {
/** Get the settings for the current project based on the project api-key. */
get: operations["ReadSettings"];
get: operations["GetSettings"];
/** Set the settings for the current project based on the project api-key. */
post: operations["SetSettings"];
};
Expand Down Expand Up @@ -50,17 +50,23 @@ export interface components {
| "eu-central-1"
| "ap-southeast-1";
SettingsModel: {
/** @description A user-provided secret-key, all the webhooks will be signed and include a x-signature header with the following: sha3(JSON.stringify(body)+secretKey) */
secretKey: string;
/** @description The region from where all the webhooks will be posted for this project */
region: components["schemas"]["SettingsRegion"];
region?: components["schemas"]["SettingsRegion"];
};
/**
* Format: uuid
* @description Stringified UUIDv4.
* See [RFC 4112](https://tools.ietf.org/html/rfc4122)
*/
UUID: string;
/**
* @description The stream status:
* [active] The Stream is healthy and processing blocks
* [paused] The Stream is paused and is not processing blocks
* [error] The Stream has encountered an error and is not processing blocks
* @enum {string}
*/
StreamsStatus: "active" | "paused" | "error";
/**
* @description The abi to parse the log object of the contract
* @example {}
Expand Down Expand Up @@ -90,7 +96,7 @@ export interface components {
tokenAddress?: string | null;
/** @description The topic0 of the event in hex, required if the type : log */
topic0?: string | null;
/** @description Include or not native transactions defaults to false */
/** @description Include or not native transactions defaults to false (only applied when type:contract) */
includeNativeTxs?: boolean;
abi?: components["schemas"]["StreamsAbi"] | null;
filter?: components["schemas"]["StreamsFilter"] | null;
Expand All @@ -102,6 +108,8 @@ export interface components {
type: components["schemas"]["StreamsType"];
/** @description The unique uuid of the stream */
id?: components["schemas"]["UUID"];
/** @description The status of the stream. */
status?: components["schemas"]["StreamsStatus"];
};
StreamsResponse: {
/** @description Array of project Streams */
Expand All @@ -125,7 +133,7 @@ export interface components {
tokenAddress?: string | null;
/** @description The topic0 of the event in hex, required if the type : log */
topic0?: string | null;
/** @description Include or not native transactions defaults to false */
/** @description Include or not native transactions defaults to false (only applied when type:contract) */
includeNativeTxs?: boolean;
abi?: components["schemas"]["StreamsAbi"] | null;
filter?: components["schemas"]["StreamsFilter"] | null;
Expand Down Expand Up @@ -158,7 +166,7 @@ export interface operations {
};
};
/** Get the settings for the current project based on the project api-key. */
ReadSettings: {
GetSettings: {
parameters: {};
responses: {
/** Ok */
Expand Down
2 changes: 1 addition & 1 deletion packages/streams/src/resolvers/readSettings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createEndpoint, createEndpointFactory } from '@moralisweb3/api-utils';
import { operations } from '../generated/types';

const name = 'ReadSettings';
const name = 'GetSettings';

type Name = typeof name;
type ApiResult = operations[Name]['responses']['200']['content']['application/json'];
Expand Down
2 changes: 1 addition & 1 deletion packages/streams/src/resolvers/setSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type Name = typeof name;
type BodyParams = operations[Name]['requestBody']['content']['application/json'];
type ApiParams = BodyParams;
const method = 'post';
const bodyParams = ['secretKey', 'region'] as const;
const bodyParams = ['region'] as const;

export const setSettings = createEndpointFactory(() =>
createEndpoint({
Expand Down
18 changes: 18 additions & 0 deletions packages/streams/src/utils/verifySignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { MoralisDataFormatted, MoralisStreamError, StreamErrorCode } from '@moralisweb3/core';
import { sha3 } from 'web3-utils';

export interface VerifySignatureOptions {
body: MoralisDataFormatted;
signature: string;
}

export const verifySignature = ({ body, signature }: VerifySignatureOptions, apiKey: string): boolean => {
const generatedSignature = sha3(JSON.stringify(body) + apiKey);
if(signature !== generatedSignature) {
throw new MoralisStreamError({
code: StreamErrorCode.INVALID_SIGNATURE,
message: 'signature is not valid',
});
}
return true;
};

0 comments on commit c719cc2

Please sign in to comment.