Skip to content

Commit

Permalink
Move recaptcha demosite to nodejs-docs-samples (GoogleCloudPlatform#2968
Browse files Browse the repository at this point in the history
)

* Move recaptcha demosite to nodejs-docs-samples

* add package.json

* fix linting

* add codeowners
  • Loading branch information
FrodoTheTrue authored Jan 10, 2023
1 parent 7ed4181 commit df2ae95
Show file tree
Hide file tree
Showing 18 changed files with 608 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ test/fixtures
build/
docs/
protos/
static/
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ compute @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-revie
iam @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
kms @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
orgpolicy @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
recaptcha @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
secret-manager @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
security-center @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
service-directory @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
Expand Down
16 changes: 16 additions & 0 deletions recaptcha/demosite/.docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: "3"
services:
livereload:
image: demosite-livereload
build:
context: "app"
dockerfile: Dockerfile
args:
- "GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT}"
- "SITE_KEY=${SITE_KEY}"
- "CHECKBOX_SITE_KEY=${CHECKBOX_SITE_KEY}"
command: node index.js
ports: ["8000:8000"]
volumes:
- "./app:/app"
restart: always
96 changes: 96 additions & 0 deletions recaptcha/demosite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Google Cloud reCAPTCHA Enterprise

Google [Cloud reCAPTCHA Enterprise](https://cloud.google.com/recaptcha-enterprise) helps protect your website from fraudulent activity, spam, and abuse without creating friction.

# Google Cloud reCAPTCHA Enterprise

## Prerequisites

### Google Cloud Project

Set up a Google Cloud project.
Billing information is **not needed** to deploy this application.

# One-click deploy

1. Click the below "Open in Cloud Shell" button.

<a href="https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/googleapis/nodejs-recaptcha-enterprise&cloudshell_git_branch=demosite">
<img alt="Open Cloud Shell" src ="http://gstatic.com/cloudssh/images/open-btn.png"></a>

2. Run
```
cd samples/demosite/app && sh init.sh
```

3. Click on the localhost link in the terminal output. You'll find the deployed application.


# Manual Deploy

### 1. Enable the reCAPTCHA Enterprise API

You must [enable the Google reCAPTCHA Enterprise API](https://console.cloud.google.com/flows/enableapi?apiid=recaptchaenterprise.googleapis.com) for your project in order to use this application.

### 2. Create Score key and Checkbox key

Create a Score key, and a Checkbox key via [Cloud Console.](https://console.cloud.google.com/security/recaptcha)

### 3. Set Environment Variables

Open the CloudShell from Cloud Console.
Set your project ID and site keys.

```angular2html
export GOOGLE_CLOUD_PROJECT="<google-project-id-here>"
export SITE_KEY="<score-key-id-here>"
export CHECKBOX_SITE_KEY="<checkbox-key-id-here>"
```

### 4. Clone, Build and Run

The following instructions will help you prepare your development environment.


1. Clone the nodejs-recaptcha-enterprise repository and navigate to ```samples/demosite``` directory.

```
cloudshell_open --repo_url "https://github.com/googleapis/nodejs-recaptcha-enterprise.git" --dir "samples/demosite" --page "shell" --force_new_clone --git_branch "demosite"
```

2. Run docker-compose

```
/usr/local/bin/docker-compose -f $PWD/docker-compose.yaml up --build
```

3. Click on the localhost link in the terminal output. You'll find the deployed application.

## Authentication

The above _**one-click**_ and _**manual**_ deployment works with the default **compute-engine** service account in the project.
If you want to create a new service account, follow the below steps.

### 1. Create Service account

A service account with private key credentials is required to create signed bearer tokens.

Create
1. [Service account](https://console.cloud.google.com/iam-admin/serviceaccounts/create)
2. [Key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#iam-service-account-keys-create-console) for the service account
3. Download the key credentials file as JSON.

### 2. Grant Permissions

You must ensure that the [user account or service account](https://cloud.google.com/iam/docs/service-accounts#differences_between_a_service_account_and_a_user_account) you used to authorize your gcloud session has the proper permissions to edit reCAPTCHA Enterprise resources for your project. In the Cloud Console under IAM, add the following roles to the project whose service account you're using to test:

* reCAPTCHA Enterprise Agent
* reCAPTCHA Enterprise Admin

More information can be found in the [Google reCAPTCHA Enterprise Docs](https://cloud.google.com/recaptcha-enterprise/docs/access-control#rbac_iam).

### 3. Export the Service account credentials

```angular2html
export GOOGLE_APPLICATION_CREDENTIALS="<path-to-service-account-credentials-file>"
```
18 changes: 18 additions & 0 deletions recaptcha/demosite/app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:16-slim

ARG GOOGLE_CLOUD_PROJECT
ENV GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT}

ARG SITE_KEY
ENV SITE_KEY=${SITE_KEY}

ARG CHECKBOX_SITE_KEY
ENV CHECKBOX_SITE_KEY=${CHECKBOX_SITE_KEY}

# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./

# Install production dependencies.
RUN npm install
26 changes: 26 additions & 0 deletions recaptcha/demosite/app/controllers/assessmentController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const {createAssessment} = require('../recaptcha/createAssessment');

const assessmentController = async (req, res) => {
try {
const assessmentData = await createAssessment(
process.env.GOOGLE_CLOUD_PROJECT,
req.body.sitekey,
req.body.token,
req.body.action
);

res.json({
error: null,
data: assessmentData,
});
} catch (e) {
res.json({
error: e.toString(),
data: null,
});
}
};

module.exports = {
assessmentController,
};
12 changes: 12 additions & 0 deletions recaptcha/demosite/app/controllers/loginController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const loginController = (req, res) => {
const context = {
project_id: process.env.GOOGLE_CLOUD_PROJECT,
site_key: process.env.SITE_KEY,
};

res.render('login', context);
};

module.exports = {
loginController,
};
12 changes: 12 additions & 0 deletions recaptcha/demosite/app/controllers/signupController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const signupController = (req, res) => {
const context = {
project_id: process.env.GOOGLE_CLOUD_PROJECT,
checkbox_site_key: process.env.CHECKBOX_SITE_KEY,
};

res.render('signup', context);
};

module.exports = {
signupController,
};
26 changes: 26 additions & 0 deletions recaptcha/demosite/app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const express = require('express');
const mustacheExpress = require('mustache-express');
const bodyParser = require('body-parser');

const router = require('./routes');

const app = express();
const port = 8000;

app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use(bodyParser.json());

app.engine('html', mustacheExpress());
app.set('view engine', 'html');
app.set('views', __dirname + '/templates');

app.use('/static', express.static('static'));
app.use('/', router);

app.listen(port, () => {
console.log(`Recaptcha demosite app listening on port ${port}`);
});
23 changes: 23 additions & 0 deletions recaptcha/demosite/app/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

# gcloud command to get the current GOOGLE Project id.
export GOOGLE_CLOUD_PROJECT=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
gcloud config set project "$GOOGLE_CLOUD_PROJECT"

# Enabling the reCAPTCHA Enterprise API
gcloud services enable recaptchaenterprise.googleapis.com

# gcloud command to create reCAPTCHA keys.
gcloud alpha recaptcha keys create --display-name=demo-recaptcha-score-key --web --allow-all-domains --integration-type=SCORE 1>/dev/null 2>recaptchascorekeyfile
export SITE_KEY=$(cat recaptchascorekeyfile | sed -n -e 's/.*Created \[\([0-9a-zA-Z_-]\+\)\].*/\1/p')
gcloud alpha recaptcha keys create --display-name=demo-recaptcha-checkbox-key --web --allow-all-domains --integration-type=CHECKBOX 1>/dev/null 2>recaptchacheckboxkeyfile
export CHECKBOX_SITE_KEY=$(cat recaptchacheckboxkeyfile | sed -n -e 's/.*Created \[\([0-9a-zA-Z_-]\+\)\].*/\1/p')

# Docker compose up
DOCKER_COMPOSE="/usr/local/bin/docker-compose -f $HOME/cloudshell_open/nodejs-recaptcha-enterprise/samples/demosite/docker-compose.yaml up --build"
$DOCKER_COMPOSE
DOCKER_COMPOSE_RESULT=$?
if [[ $DOCKER_COMPOSE_RESULT == *"error"* ]];
then
echo "Deployment error"
fi
19 changes: 19 additions & 0 deletions recaptcha/demosite/app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "recaptcha-demosite",
"description": "",
"version": "1.0.0",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/googleapis/nodejs-recaptcha-enterprise.git"
},
"license": "MIT",
"dependencies": {
"@google-cloud/recaptcha-enterprise": "^3.0.0",
"body-parser": "^1.20.0",
"express": "^4.18.1",
"mustache-express": "^1.3.2"
}
}
63 changes: 63 additions & 0 deletions recaptcha/demosite/app/recaptcha/createAssessment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const {RecaptchaEnterpriseServiceClient} =
require('@google-cloud/recaptcha-enterprise').v1;

const THRESHHOLD_SCORE = 0.5;

async function createAssessment(
projectId,
recaptchSiteKey,
token,
recaptchaAction
) {
const client = new RecaptchaEnterpriseServiceClient();

const [response] = await client.createAssessment({
parent: `projects/${projectId}`,
assessment: {
event: {
siteKey: recaptchSiteKey,
token,
},
},
});

// Check if the token is valid.
if (!response.tokenProperties || !response.tokenProperties.valid) {
throw new Error(
`The Create Assessment call failed because the token was invalid for the following reasons: ${response.tokenProperties.invalidReason}`
);
}

// Check if the expected action was executed.
if (response.tokenProperties.action !== recaptchaAction) {
throw new Error(
'The action attribute in your reCAPTCHA tag does not match the action you are expecting to score. Please check your action attribute !'
);
}

// Get the risk score and the reason(s)
// For more information on interpreting the assessment,
// see https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment
for (const reason of response.riskAnalysis.reasons) {
console.log(reason);
}

console.log(
`The reCAPTCHA score for this token is: ${response.riskAnalysis.score}`
);

let verdict = 'Human';

if (response.riskAnalysis.score < THRESHHOLD_SCORE) {
verdict = 'Not a human';
}

return {
score: response.riskAnalysis.score,
verdict,
};
}

module.exports = {
createAssessment,
};
12 changes: 12 additions & 0 deletions recaptcha/demosite/app/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require('express');
const router = express.Router();

const {loginController} = require('./controllers/loginController');
const {signupController} = require('./controllers/signupController');
const {assessmentController} = require('./controllers/assessmentController');

router.get('/login', loginController);
router.get('/signup', signupController);
router.post('/create_assessment', assessmentController);

module.exports = router;
Loading

0 comments on commit df2ae95

Please sign in to comment.