Skip to content

Commit

Permalink
feat: added reCAPCHA verification tool (#39)
Browse files Browse the repository at this point in the history
* feat: added google-recaptcha verification

* fix: updated isStagingUrl var
  • Loading branch information
impelcrypto authored May 30, 2022
1 parent 1d43953 commit 5ae170c
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 12 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ DISCORD_FAUCET_CHANNEL_ID=<channel id>
# Secret phrase (mnemonic) for the faucet account
FAUCET_SECRET_PHRASE=<secret phrase>
# Secret key for RECAPTCHA
GOOGLE_RECAPTCHA_SECRET = <secret key>
# Private key for the EVM faucet account
EVM_FAUCET_PRIVATE_KEY=<private key>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"dedent": "^0.7.0",
"discord-api-types": "^0.30.0",
"discord.js": "^13.6.0",
"express": "^4.17.3"
"express": "^4.17.3",
"google-recaptcha": "^1.1.0"
}
}
1 change: 1 addition & 0 deletions src/@types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'google-recaptcha';
15 changes: 12 additions & 3 deletions src/clients/express.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import cors from 'cors';
import express from 'express';
import { appOauthInstallUrl } from './discord';
import { verifyRecaptcha } from '../helpers';
import { NetworkApis, Network, FaucetInfo } from '../types';

const whitelist = ['http://localhost:8080', 'http://localhost:8081', 'https://portal.astar.network'];

const recaptchaSecret = process.env.GOOGLE_RECAPTCHA_SECRET;
if (!recaptchaSecret) {
throw Error('Secret key for recaptcha is not defined');
}

/**
* Handles client request via Express.js. These are usually for custom endpoints or OAuth and app installation.
* We didn't hook this up to any database, so for out-of-the-box usage, you can hard-code the guild ID and other credentials in a .env file
Expand Down Expand Up @@ -43,11 +48,15 @@ export const expressApp = async (apis: NetworkApis) => {
const listedOrigin = whitelist.find((it) => it === origin);

// for portal staging environments
const isHeroku = origin.includes('https://deploy-preview-pr-');
const isStagingUrl = origin.includes('https://astar-apps--pr');

if (!listedOrigin && !isHeroku) {
if (!listedOrigin && !isStagingUrl) {
throw Error('invalid request');
}

const recaptchaResponse: string = req.body.recaptchaResponse || '';
await verifyRecaptcha({ recaptchaResponse, recaptchaSecret });

// parse the name of the network
const network: Network = req.params.network as Network;
// parse the faucet drip destination
Expand Down
1 change: 1 addition & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './addNumber';
export * from './calculation';
export * from './recaptcha';
23 changes: 23 additions & 0 deletions src/helpers/recaptcha.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import GoogleRecaptcha from 'google-recaptcha';

export const verifyRecaptcha = async ({
recaptchaResponse,
recaptchaSecret,
}: {
recaptchaResponse: string;
recaptchaSecret: string;
}): Promise<boolean> => {
const googleRecaptcha = new GoogleRecaptcha({ secret: recaptchaSecret });
if (!recaptchaResponse) throw Error('invalid recaptcha');
return await new Promise<boolean>(async (resolve) => {
googleRecaptcha.verify({ response: recaptchaResponse }, (error: Error) => {
if (error) {
throw Error('invalid recaptcha');
} else {
resolve(true);
}
});
});
};
Loading

0 comments on commit 5ae170c

Please sign in to comment.