Skip to content

Commit df2ae95

Browse files
authored
Move recaptcha demosite to nodejs-docs-samples (#2968)
* Move recaptcha demosite to nodejs-docs-samples * add package.json * fix linting * add codeowners
1 parent 7ed4181 commit df2ae95

18 files changed

+608
-0
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ test/fixtures
44
build/
55
docs/
66
protos/
7+
static/

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ compute @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-revie
2828
iam @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
2929
kms @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
3030
orgpolicy @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
31+
recaptcha @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
3132
secret-manager @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
3233
security-center @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
3334
service-directory @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/nodejs-samples-reviewers
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: "3"
2+
services:
3+
livereload:
4+
image: demosite-livereload
5+
build:
6+
context: "app"
7+
dockerfile: Dockerfile
8+
args:
9+
- "GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT}"
10+
- "SITE_KEY=${SITE_KEY}"
11+
- "CHECKBOX_SITE_KEY=${CHECKBOX_SITE_KEY}"
12+
command: node index.js
13+
ports: ["8000:8000"]
14+
volumes:
15+
- "./app:/app"
16+
restart: always

recaptcha/demosite/README.md

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Google Cloud reCAPTCHA Enterprise
2+
3+
Google [Cloud reCAPTCHA Enterprise](https://cloud.google.com/recaptcha-enterprise) helps protect your website from fraudulent activity, spam, and abuse without creating friction.
4+
5+
# Google Cloud reCAPTCHA Enterprise
6+
7+
## Prerequisites
8+
9+
### Google Cloud Project
10+
11+
Set up a Google Cloud project.
12+
Billing information is **not needed** to deploy this application.
13+
14+
# One-click deploy
15+
16+
1. Click the below "Open in Cloud Shell" button.
17+
18+
<a href="https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/googleapis/nodejs-recaptcha-enterprise&cloudshell_git_branch=demosite">
19+
<img alt="Open Cloud Shell" src ="http://gstatic.com/cloudssh/images/open-btn.png"></a>
20+
21+
2. Run
22+
```
23+
cd samples/demosite/app && sh init.sh
24+
```
25+
26+
3. Click on the localhost link in the terminal output. You'll find the deployed application.
27+
28+
29+
# Manual Deploy
30+
31+
### 1. Enable the reCAPTCHA Enterprise API
32+
33+
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.
34+
35+
### 2. Create Score key and Checkbox key
36+
37+
Create a Score key, and a Checkbox key via [Cloud Console.](https://console.cloud.google.com/security/recaptcha)
38+
39+
### 3. Set Environment Variables
40+
41+
Open the CloudShell from Cloud Console.
42+
Set your project ID and site keys.
43+
44+
```angular2html
45+
export GOOGLE_CLOUD_PROJECT="<google-project-id-here>"
46+
export SITE_KEY="<score-key-id-here>"
47+
export CHECKBOX_SITE_KEY="<checkbox-key-id-here>"
48+
```
49+
50+
### 4. Clone, Build and Run
51+
52+
The following instructions will help you prepare your development environment.
53+
54+
55+
1. Clone the nodejs-recaptcha-enterprise repository and navigate to ```samples/demosite``` directory.
56+
57+
```
58+
cloudshell_open --repo_url "https://github.com/googleapis/nodejs-recaptcha-enterprise.git" --dir "samples/demosite" --page "shell" --force_new_clone --git_branch "demosite"
59+
```
60+
61+
2. Run docker-compose
62+
63+
```
64+
/usr/local/bin/docker-compose -f $PWD/docker-compose.yaml up --build
65+
```
66+
67+
3. Click on the localhost link in the terminal output. You'll find the deployed application.
68+
69+
## Authentication
70+
71+
The above _**one-click**_ and _**manual**_ deployment works with the default **compute-engine** service account in the project.
72+
If you want to create a new service account, follow the below steps.
73+
74+
### 1. Create Service account
75+
76+
A service account with private key credentials is required to create signed bearer tokens.
77+
78+
Create
79+
1. [Service account](https://console.cloud.google.com/iam-admin/serviceaccounts/create)
80+
2. [Key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#iam-service-account-keys-create-console) for the service account
81+
3. Download the key credentials file as JSON.
82+
83+
### 2. Grant Permissions
84+
85+
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:
86+
87+
* reCAPTCHA Enterprise Agent
88+
* reCAPTCHA Enterprise Admin
89+
90+
More information can be found in the [Google reCAPTCHA Enterprise Docs](https://cloud.google.com/recaptcha-enterprise/docs/access-control#rbac_iam).
91+
92+
### 3. Export the Service account credentials
93+
94+
```angular2html
95+
export GOOGLE_APPLICATION_CREDENTIALS="<path-to-service-account-credentials-file>"
96+
```

recaptcha/demosite/app/Dockerfile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM node:16-slim
2+
3+
ARG GOOGLE_CLOUD_PROJECT
4+
ENV GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT}
5+
6+
ARG SITE_KEY
7+
ENV SITE_KEY=${SITE_KEY}
8+
9+
ARG CHECKBOX_SITE_KEY
10+
ENV CHECKBOX_SITE_KEY=${CHECKBOX_SITE_KEY}
11+
12+
# Copy local code to the container image.
13+
ENV APP_HOME /app
14+
WORKDIR $APP_HOME
15+
COPY . ./
16+
17+
# Install production dependencies.
18+
RUN npm install
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const {createAssessment} = require('../recaptcha/createAssessment');
2+
3+
const assessmentController = async (req, res) => {
4+
try {
5+
const assessmentData = await createAssessment(
6+
process.env.GOOGLE_CLOUD_PROJECT,
7+
req.body.sitekey,
8+
req.body.token,
9+
req.body.action
10+
);
11+
12+
res.json({
13+
error: null,
14+
data: assessmentData,
15+
});
16+
} catch (e) {
17+
res.json({
18+
error: e.toString(),
19+
data: null,
20+
});
21+
}
22+
};
23+
24+
module.exports = {
25+
assessmentController,
26+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const loginController = (req, res) => {
2+
const context = {
3+
project_id: process.env.GOOGLE_CLOUD_PROJECT,
4+
site_key: process.env.SITE_KEY,
5+
};
6+
7+
res.render('login', context);
8+
};
9+
10+
module.exports = {
11+
loginController,
12+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const signupController = (req, res) => {
2+
const context = {
3+
project_id: process.env.GOOGLE_CLOUD_PROJECT,
4+
checkbox_site_key: process.env.CHECKBOX_SITE_KEY,
5+
};
6+
7+
res.render('signup', context);
8+
};
9+
10+
module.exports = {
11+
signupController,
12+
};

recaptcha/demosite/app/index.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const express = require('express');
2+
const mustacheExpress = require('mustache-express');
3+
const bodyParser = require('body-parser');
4+
5+
const router = require('./routes');
6+
7+
const app = express();
8+
const port = 8000;
9+
10+
app.use(
11+
bodyParser.urlencoded({
12+
extended: true,
13+
})
14+
);
15+
app.use(bodyParser.json());
16+
17+
app.engine('html', mustacheExpress());
18+
app.set('view engine', 'html');
19+
app.set('views', __dirname + '/templates');
20+
21+
app.use('/static', express.static('static'));
22+
app.use('/', router);
23+
24+
app.listen(port, () => {
25+
console.log(`Recaptcha demosite app listening on port ${port}`);
26+
});

recaptcha/demosite/app/init.sh

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
3+
# gcloud command to get the current GOOGLE Project id.
4+
export GOOGLE_CLOUD_PROJECT=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
5+
gcloud config set project "$GOOGLE_CLOUD_PROJECT"
6+
7+
# Enabling the reCAPTCHA Enterprise API
8+
gcloud services enable recaptchaenterprise.googleapis.com
9+
10+
# gcloud command to create reCAPTCHA keys.
11+
gcloud alpha recaptcha keys create --display-name=demo-recaptcha-score-key --web --allow-all-domains --integration-type=SCORE 1>/dev/null 2>recaptchascorekeyfile
12+
export SITE_KEY=$(cat recaptchascorekeyfile | sed -n -e 's/.*Created \[\([0-9a-zA-Z_-]\+\)\].*/\1/p')
13+
gcloud alpha recaptcha keys create --display-name=demo-recaptcha-checkbox-key --web --allow-all-domains --integration-type=CHECKBOX 1>/dev/null 2>recaptchacheckboxkeyfile
14+
export CHECKBOX_SITE_KEY=$(cat recaptchacheckboxkeyfile | sed -n -e 's/.*Created \[\([0-9a-zA-Z_-]\+\)\].*/\1/p')
15+
16+
# Docker compose up
17+
DOCKER_COMPOSE="/usr/local/bin/docker-compose -f $HOME/cloudshell_open/nodejs-recaptcha-enterprise/samples/demosite/docker-compose.yaml up --build"
18+
$DOCKER_COMPOSE
19+
DOCKER_COMPOSE_RESULT=$?
20+
if [[ $DOCKER_COMPOSE_RESULT == *"error"* ]];
21+
then
22+
echo "Deployment error"
23+
fi

recaptcha/demosite/app/package.json

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "recaptcha-demosite",
3+
"description": "",
4+
"version": "1.0.0",
5+
"scripts": {
6+
"test": "echo \"Error: no test specified\" && exit 1"
7+
},
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/googleapis/nodejs-recaptcha-enterprise.git"
11+
},
12+
"license": "MIT",
13+
"dependencies": {
14+
"@google-cloud/recaptcha-enterprise": "^3.0.0",
15+
"body-parser": "^1.20.0",
16+
"express": "^4.18.1",
17+
"mustache-express": "^1.3.2"
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const {RecaptchaEnterpriseServiceClient} =
2+
require('@google-cloud/recaptcha-enterprise').v1;
3+
4+
const THRESHHOLD_SCORE = 0.5;
5+
6+
async function createAssessment(
7+
projectId,
8+
recaptchSiteKey,
9+
token,
10+
recaptchaAction
11+
) {
12+
const client = new RecaptchaEnterpriseServiceClient();
13+
14+
const [response] = await client.createAssessment({
15+
parent: `projects/${projectId}`,
16+
assessment: {
17+
event: {
18+
siteKey: recaptchSiteKey,
19+
token,
20+
},
21+
},
22+
});
23+
24+
// Check if the token is valid.
25+
if (!response.tokenProperties || !response.tokenProperties.valid) {
26+
throw new Error(
27+
`The Create Assessment call failed because the token was invalid for the following reasons: ${response.tokenProperties.invalidReason}`
28+
);
29+
}
30+
31+
// Check if the expected action was executed.
32+
if (response.tokenProperties.action !== recaptchaAction) {
33+
throw new Error(
34+
'The action attribute in your reCAPTCHA tag does not match the action you are expecting to score. Please check your action attribute !'
35+
);
36+
}
37+
38+
// Get the risk score and the reason(s)
39+
// For more information on interpreting the assessment,
40+
// see https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment
41+
for (const reason of response.riskAnalysis.reasons) {
42+
console.log(reason);
43+
}
44+
45+
console.log(
46+
`The reCAPTCHA score for this token is: ${response.riskAnalysis.score}`
47+
);
48+
49+
let verdict = 'Human';
50+
51+
if (response.riskAnalysis.score < THRESHHOLD_SCORE) {
52+
verdict = 'Not a human';
53+
}
54+
55+
return {
56+
score: response.riskAnalysis.score,
57+
verdict,
58+
};
59+
}
60+
61+
module.exports = {
62+
createAssessment,
63+
};

recaptcha/demosite/app/routes.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const express = require('express');
2+
const router = express.Router();
3+
4+
const {loginController} = require('./controllers/loginController');
5+
const {signupController} = require('./controllers/signupController');
6+
const {assessmentController} = require('./controllers/assessmentController');
7+
8+
router.get('/login', loginController);
9+
router.get('/signup', signupController);
10+
router.post('/create_assessment', assessmentController);
11+
12+
module.exports = router;

0 commit comments

Comments
 (0)