Create your own CAPTCHA - part 2 - Setup TypeScript, Webpack and React

meatboy

Meat Boy

Posted on May 18, 2020

Create your own CAPTCHA - part 2 - Setup TypeScript, Webpack and React

Welcome in the second part of the series about creating own, custom captcha mechanism. In this article, we are going to prepare an environment for further work. As I mentioned in the previous post, the entire captcha will be written with TypeScript and React on the client-side.

If you want to skip reading, just download source code from git repository. Leave a star if you like the project. ⭐

GitHub logo pilotpirxie / devcaptcha

πŸ€– Open source captcha made with React, Node and TypeScript for DEV.to community

devcaptcha

Open source captcha made with React, Node and TypeScript for DEV.to community

Features

  • Fast and efficient, uses Redis as temp storage,
  • Implements leading zero challenge,
  • Requires image recognition to find coordinates on a background,
  • Customizable, you can easily tailor to your needs,
  • Simple integration in just few minutes,
  • Written with Typescript, React, Node and Express,

Screenshot 1

Screenshot 2

Screenshot 3

Getting started

git clone https://github.com/pilotpirxie/devcaptcha.git
cd devcaptcha/devcaptcha-server
yarn install
yarn start
Enter fullscreen mode Exit fullscreen mode

Integration

Captcha should be configured equally on the client and backend side to works correctly.

const devcaptcha = new DevCaptcha({
  appendSelector: '#captcha',
  promptText: 'Move the puzzle to the correct position to solve captcha',
  lockedText: 'Locked',
  savingText: 'Wait',
  privacyUrl: 'https://example.com',
  termsUrl: 'https://example.com',
  baseUrl: 'http://localhost:8081',
  puzzleAlpha: 0.9,
  canvasContainerId: 'devcaptcha-container',
  leadingZerosLength: 3,
  workerPath: './worker.js'
});
Enter fullscreen mode Exit fullscreen mode

Client Config Definition:

export type CaptchaConfig
…

Installation

So let's initialize the project with installing libraries. Install react, react-dom, styled-components, webpack and typescript. Then install types, eslint and utils plugins.

To install libs faster, just copy them from package.json below that I prepared. Then run yarn and yarn upgrade --latest to upgrade to the newest version.



{
  "name": "devcaptcha",
  "version": "1.0.0",
  "main": "dist/devcaptcha.dist.js",
  "devDependencies": {
    "@types/react": "^16.9.35",
    "@types/react-dom": "^16.9.8",
    "@types/styled-components": "^5.1.0",
    "@typescript-eslint/eslint-plugin": "^2.33.0",
    "@typescript-eslint/parser": "^2.33.0",
    "eslint": "^7.0.0",
    "eslint-plugin-react": "^7.20.0",
    "source-map-loader": "^0.2.4",
    "ts-loader": "^7.0.4",
    "typescript": "^3.9.2",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.11.0"
  },
  "dependencies": {
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "styled-components": "^5.1.0"
  },
  "scripts": {
    "start": "webpack-dev-server --open --config webpack.development.config.js",
    "build": "webpack --config webpack.production.config.js",
    "eslint": "./node_modules/.bin/eslint .",
    "fix": "./node_modules/.bin/eslint --fix ."
  }
}


Enter fullscreen mode Exit fullscreen mode

Dev server for hot reload

After installation, create directory public and index.html file and put inside:



<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
</head>
<body>
<div id="captcha"></div>

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="main.js"></script>
</body>
</html>


Enter fullscreen mode Exit fullscreen mode

This file will be served on the dev server. Before closing body tag it contains links to React on CDN, just for the development process. We want to have an independent file similar to Web Components (if you want, you can wrap this project and create custom element) to work in different situations.

Webpack

Create webpack.development.config.js file for development like below. Configure port and public directory. This file also contains information about bindings in source maps between original and minified files. Make sure to install ts-loader to be able to resolve and load typescript files.



module.exports = {
  mode: "development",
  devtool: "source-map",
  devServer: {
    contentBase: './public',
    compress: false,
    port: 8080,
  },
  resolve: {
    extensions: [".ts", ".tsx", '.js', '.json']
  },
  module: {
    rules: [{
      test: /\.ts(x?)$/,
      exclude: /node_modules/,
      use: [{
        loader: "ts-loader"
      }]
    }, {
      enforce: "pre",
      test: /\.js$/,
      loader: "source-map-loader"
    }]
  },
  externals: {
    react: "React",
    "react-dom": "ReactDOM",
  }
};


Enter fullscreen mode Exit fullscreen mode

Similarly, create production config for builds webpack.production.config.js. It's very close to the previous, however it doesn't contain dev server configuration, different mode and externals. Externals are used to skip and create globals. In the dev mode, we are using CDN links to make hot reload faster. In the prod we want to bundle everything together.



module.exports = {
  mode: "production",
  devtool: "source-map",
  output: {
    filename: 'devcaptcha.dist.js'
  },
  resolve: {
    extensions: [".ts", ".tsx", '.js', '.json']
  },
  module: {
    rules: [{
      test: /\.ts(x?)$/,
      exclude: /node_modules/,
      use: [{
        loader: "ts-loader"
      }]
    }, {
      enforce: "pre",
      test: /\.js$/,
      loader: "source-map-loader"
    }]
  },
};


Enter fullscreen mode Exit fullscreen mode

Typescript

Create configuration for typescript tsconfig.json. Parameter noImplicitAny set to true disallow compilation when somewhere variable is untyped. Parameter jsxspecifies that we are using tsx files. Library array contains different types of load by default. Entry dom allow accessing to Web API and objects like window.document.



{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "CommonJS",
    "jsx": "react",
    "target": "es5",
    "lib": [
      "es6",
      "dom"
    ]
  }
}


Enter fullscreen mode Exit fullscreen mode

Initial source code

Ok, almost ready. You need to create an entry point for typescript. With React we will be using .tsx extension. It's like typescript with some additional sugar.

Create directory src and index.tsx inside. Inside import entire React and ReactDOM and create a class with a method for rendering/mounting captcha in the right place.

In my case, I am looking for root element by selector passed in the constructor. The class that I created implements interface ICaptcha with common properties for hypothetical, different captchas and DevCaptcha too.

Important is to assign the reference to DevCaptcha on window object to make access possible. However, in TypeScript, you cannot assign directly to a global object. Firstly declare an extended interface to the object.



import * as React from "react";
import * as ReactDOM from "react-dom";

import { App } from "./components/App";

interface ICaptcha {
  _appendSelector: string
}

type CaptchaConfig = {
  appendSelector: string
}

class DevCaptcha implements ICaptcha {
  readonly _appendSelector : string;

  public constructor(config : CaptchaConfig) {
    this._appendSelector = config.appendSelector;
  }

  mount() {
    ReactDOM.render(<App />, document.querySelector(this._appendSelector));
  }
}

declare global {
  interface Window { DevCaptcha: object; }
}

window.DevCaptcha = window.DevCaptcha || {};
window['DevCaptcha'] = DevCaptcha;


Enter fullscreen mode Exit fullscreen mode

ESLint

Finally, configure eslint to quickly look for the code quality problems. You can configure it for you. If you have your own eslint config, just use it.

Create .eslintrc with the following code:



module.exports = {
  "env": {
    "browser": true,
    "commonjs": true,
    "es6": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/eslint-recommended"
  ],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true,
    },
    "ecmaVersion": 2018
  },
  "plugins": [
    "react",
    "@typescript-eslint"
  ],
  "rules": {
    "indent": ["error", 2]
  }
};


Enter fullscreen mode Exit fullscreen mode

and .eslintignore with directories to exclude



node_modules
public
dist


Enter fullscreen mode Exit fullscreen mode

You did it!

If you did everything well, you should be able to run dev server of this app.



yarn start


Enter fullscreen mode Exit fullscreen mode

You did it
Open browser on localhost at the port which you set up previously. In my case, it's 8080, so open http://localhost:8080. You should see Hello World setup for React, widget-based application.

Uff. That's how we prepared environment for future work on client-side of captcha. In the next article, we will start working on a first reverse-turing mechanism.

Current source code is available on GitHub. Please, leave a star ⭐ if you like project.

GitHub logo pilotpirxie / devcaptcha

πŸ€– Open source captcha made with React, Node and TypeScript for DEV.to community

devcaptcha

Open source captcha made with React, Node and TypeScript for DEV.to community

Features

  • Fast and efficient, uses Redis as temp storage,
  • Implements leading zero challenge,
  • Requires image recognition to find coordinates on a background,
  • Customizable, you can easily tailor to your needs,
  • Simple integration in just few minutes,
  • Written with Typescript, React, Node and Express,

Screenshot 1

Screenshot 2

Screenshot 3

Getting started

git clone https://github.com/pilotpirxie/devcaptcha.git
cd devcaptcha/devcaptcha-server
yarn install
yarn start
Enter fullscreen mode Exit fullscreen mode

Integration

Captcha should be configured equally on the client and backend side to works correctly.

const devcaptcha = new DevCaptcha({
  appendSelector: '#captcha',
  promptText: 'Move the puzzle to the correct position to solve captcha',
  lockedText: 'Locked',
  savingText: 'Wait',
  privacyUrl: 'https://example.com',
  termsUrl: 'https://example.com',
  baseUrl: 'http://localhost:8081',
  puzzleAlpha: 0.9,
  canvasContainerId: 'devcaptcha-container',
  leadingZerosLength: 3,
  workerPath: './worker.js'
});
Enter fullscreen mode Exit fullscreen mode

Client Config Definition:

export type CaptchaConfig
…

If you want to be notified about the next part, follow me on DEV.to. πŸ˜‰

πŸ’– πŸ’ͺ πŸ™… 🚩
meatboy
Meat Boy

Posted on May 18, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related