Skip to content

Commit 7c9b968

Browse files
Sita04engelke
andauthored
fix: update demosite to reflect error messages in client (GoogleCloudPlatform#9867)
* fix: update demosite to reflect error messages in client * lint fix * flatten the returned response * fix according to review comments * refactor bad action check to a function * update comments --------- Co-authored-by: Charles Engelke <[email protected]>
1 parent 9b4b465 commit 7c9b968

File tree

3 files changed

+131
-85
lines changed

3 files changed

+131
-85
lines changed

recaptcha_enterprise/demosite/app/static/demo-6df0841a.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,7 +2247,7 @@ var demoCSS = i$3`
22472247
margin-bottom: var(--size-huge);
22482248
}
22492249
#guide .text,
2250-
#guide #verdict + .scoreExample {
2250+
#guide #label + .scoreExample {
22512251
margin-bottom: var(--size-xhuge);
22522252
}
22532253
#guide p,
@@ -2372,7 +2372,7 @@ var demoCSS = i$3`
23722372
animation: .5s ease-out 0s 2 alternate both paused drawerBump;
23732373
}
23742374
#guide .response,
2375-
#verdict p,
2375+
#label p,
23762376
.scoreExample {
23772377
transition: max-height var(--full-lapse) ease-out var(--half-lapse),
23782378
opacity var(--full-lapse) ease-out var(--half-lapse);
@@ -2383,7 +2383,7 @@ var demoCSS = i$3`
23832383
opacity: 0;
23842384
}
23852385
.scored #guide .response,
2386-
.scored #verdict p,
2386+
.scored #label p,
23872387
.scored .scoreExample {
23882388
opacity: 1;
23892389
}
@@ -3700,7 +3700,8 @@ class RecaptchaDemo extends s {
37003700
step: { type: String },
37013701
/* Result */
37023702
score: { type: String },
3703-
verdict: { type: String },
3703+
label: { type: String },
3704+
reason: { type: String },
37043705
};
37053706

37063707
constructor() {
@@ -3714,7 +3715,8 @@ class RecaptchaDemo extends s {
37143715
/* Result */
37153716
this._score = undefined;
37163717
this.score = this._score;
3717-
this.verdict = undefined;
3718+
this.label = undefined;
3719+
this.reason = undefined;
37183720
/* Other */
37193721
this.cleanupGame = () => {};
37203722
/* In the year of our lord 2023 */
@@ -3850,7 +3852,7 @@ class RecaptchaDemo extends s {
38503852
}
38513853

38523854
handleSubmit() {
3853-
if (this.score && this.verdict) {
3855+
if (this.score && this.label) {
38543856
this.goToNextStep();
38553857
return;
38563858
}
@@ -4092,7 +4094,7 @@ class RecaptchaDemo extends s {
40924094
"tokenProperties": {
40934095
"action": "${ACTIONS[this.step]}",
40944096
...
4095-
"valid": true
4097+
"valid": ${this.reason !== 'Invalid token'}
40964098
},
40974099
}`
40984100
.replace(/^([ ]+)[}](?!,)/m, "}")
@@ -4177,7 +4179,7 @@ class RecaptchaDemo extends s {
41774179
const score = this.score && this.score.slice(0, 3);
41784180
const percentage = score && Number(score) * 100;
41794181
let card = null;
4180-
switch (this.verdict) {
4182+
switch (this.label) {
41814183
case "Not Bad":
41824184
card = x`
41834185
<p>reCAPTCHA is ${percentage || "???"}% confident you're not bad.</p>
@@ -4186,7 +4188,7 @@ class RecaptchaDemo extends s {
41864188
break;
41874189
case "Bad":
41884190
card = x`
4189-
<p>reCAPTCHA is ${percentage || "???"}% confident you're not bad.</p>
4191+
<p>Suspicious request. Reason: "${this.reason}".</p>
41904192
<img alt="Bad" src="${badbad}" />
41914193
`;
41924194
break;
@@ -4200,7 +4202,7 @@ class RecaptchaDemo extends s {
42004202
`;
42014203
}
42024204
return x`
4203-
<div id="verdict">
4205+
<div id="label">
42044206
<div id="score">
42054207
<div class="score">${score || "–"}</div>
42064208
${card}
@@ -4448,10 +4450,10 @@ class RecaptchaDemo extends s {
44484450
animating: this.animating,
44494451
drawerOpen: this.step !== "game" && this.drawerOpen,
44504452
drawerClosed: this.step === "game" || !this.drawerOpen,
4451-
scored: this.score && this.verdict,
4453+
scored: this.score && this.label,
44524454
sitemapOpen: this.sitemapOpen,
44534455
sitemapClosed: !this.sitemapOpen,
4454-
unscored: !this.score || !this.verdict,
4456+
unscored: !this.score || !this.label,
44554457
})}"
44564458
id="demo"
44574459
>

recaptcha_enterprise/demosite/app/static/global-e680a49614fd8ff8.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ function fetchServerResponse({ body, url }) {
4141
// This code is internal to the demo.
4242
// It passes the score to the demo to display it.
4343
function useAssessmentInClient(score) {
44-
if (score?.data?.score && score?.data?.verdict) {
44+
if (score?.data?.score && score?.data?.label) {
4545
const demoElement = document.querySelector("recaptcha-demo");
4646
demoElement.setAttribute("score", score?.data?.score);
47-
demoElement.setAttribute("verdict", score?.data?.verdict);
47+
demoElement.setAttribute("label", score?.data?.label);
48+
demoElement.setAttribute("reason", score?.data?.reason);
4849
}
4950
}

recaptcha_enterprise/demosite/app/urls.py

Lines changed: 114 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import configparser
15+
import enum
1516
import json
1617
import os
18+
from typing import Tuple
1719

1820
from flask import jsonify, render_template, request, Response
21+
from google.cloud.recaptchaenterprise_v1 import Assessment
1922

2023
from backend.create_recaptcha_assessment import create_assessment
2124

@@ -36,6 +39,17 @@
3639
assert "recaptcha_actions" in config
3740

3841

42+
class Error(enum.Enum):
43+
INVALID_TOKEN = "Invalid token"
44+
ACTION_MISMATCH = "Action mismatch"
45+
SCORE_LESS_THAN_THRESHOLD = "Returned score less than threshold set"
46+
47+
48+
class Label(enum.Enum):
49+
NOT_BAD = "Not Bad"
50+
BAD = "Bad"
51+
52+
3953
# Return homepage template.
4054
def home() -> str:
4155
return render_template(template_name_or_list="home.html", context=context)
@@ -44,6 +58,7 @@ def home() -> str:
4458
# On homepage load, execute reCAPTCHA Enterprise assessment and take action according to the score.
4559
def on_homepage_load() -> Response:
4660
try:
61+
reason = ""
4762
recaptcha_action = config["recaptcha_actions"]["home"]
4863
json_data = json.loads(request.data)
4964

@@ -55,25 +70,26 @@ def on_homepage_load() -> Response:
5570
)
5671

5772
# Check if the token is valid, score is above threshold score and the action equals expected.
58-
if assessment_response.token_properties.valid and \
59-
assessment_response.risk_analysis.score > SAMPLE_THRESHOLD_SCORE and \
60-
assessment_response.token_properties.action == recaptcha_action:
61-
# Load the home page.
62-
# Business logic.
63-
# Classify the action as not bad.
64-
verdict = "Not Bad"
65-
else:
66-
# If any of the above condition fails, trigger email/ phone verification flow.
67-
# Classify the action as bad.
68-
verdict = "Bad"
73+
# Take action based on the result (BAD/ NOT_BAD).
74+
#
75+
# If 'label' is NOT_BAD:
76+
# Load the home page.
77+
# Business logic.
78+
#
79+
# If 'label' is BAD:
80+
# Trigger email/ phone verification flow.
81+
label, reason = check_for_bad_action(assessment_response, recaptcha_action)
6982
# <!-- ATTENTION: reCAPTCHA Example (Server Part 1/2) Ends -->
7083

71-
# Return the risk score.
84+
# Below code is only used to send response to the client for demo purposes.
85+
# DO NOT send scores or other assessment response to the client.
86+
# Return the response.
7287
return jsonify(
7388
{
7489
"data": {
7590
"score": "{:.1f}".format(assessment_response.risk_analysis.score),
76-
"verdict": verdict,
91+
"label": label,
92+
"reason": reason,
7793
}
7894
}
7995
)
@@ -100,27 +116,28 @@ def on_signup() -> Response:
100116
)
101117

102118
# Check if the token is valid, score is above threshold score and the action equals expected.
103-
if assessment_response.token_properties.valid and \
104-
assessment_response.risk_analysis.score > SAMPLE_THRESHOLD_SCORE and \
105-
assessment_response.token_properties.action == recaptcha_action:
106-
# Write new username and password to users database.
107-
# username = json_data["username"]
108-
# password = json_data["password"]
109-
# Business logic.
110-
# Classify the action as not bad.
111-
verdict = "Not Bad"
112-
else:
113-
# If any of the above condition fails, trigger email/ phone verification flow.
114-
# Classify the action as bad.
115-
verdict = "Bad"
119+
# Take action based on the result (BAD/ NOT_BAD).
120+
#
121+
# If 'label' is NOT_BAD:
122+
# Write new username and password to users database.
123+
# username = json_data["username"]
124+
# password = json_data["password"]
125+
# Business logic.
126+
#
127+
# If 'label' is BAD:
128+
# Trigger email/ phone verification flow.
129+
label, reason = check_for_bad_action(assessment_response, recaptcha_action)
116130
# <!-- ATTENTION: reCAPTCHA Example (Server Part 1/2) Ends -->
117131

118-
# Return the risk score.
132+
# Below code is only used to send response to the client for demo purposes.
133+
# DO NOT send scores or other assessment response to the client.
134+
# Return the response.
119135
return jsonify(
120136
{
121137
"data": {
122138
"score": "{:.1f}".format(assessment_response.risk_analysis.score),
123-
"verdict": verdict,
139+
"label": label,
140+
"reason": reason,
124141
}
125142
}
126143
)
@@ -147,27 +164,28 @@ def on_login() -> Response:
147164
)
148165

149166
# Check if the token is valid, score is above threshold score and the action equals expected.
150-
if assessment_response.token_properties.valid and \
151-
assessment_response.risk_analysis.score > SAMPLE_THRESHOLD_SCORE and \
152-
assessment_response.token_properties.action == recaptcha_action:
153-
# Check if the login credentials exist and match.
154-
# username = json_data["username"]
155-
# password = json_data["password"]
156-
# Business logic.
157-
# Classify the action as not bad.
158-
verdict = "Not Bad"
159-
else:
160-
# If any of the above condition fails, trigger email/phone verification flow.
161-
# Classify the action as bad.
162-
verdict = "Bad"
167+
# Take action based on the result (BAD/ NOT_BAD).
168+
#
169+
# If 'label' is NOT_BAD:
170+
# Check if the login credentials exist and match.
171+
# username = json_data["username"]
172+
# password = json_data["password"]
173+
# Business logic.
174+
#
175+
# If 'label' is BAD:
176+
# Trigger email/phone verification flow.
177+
label, reason = check_for_bad_action(assessment_response, recaptcha_action)
163178
# <!-- ATTENTION: reCAPTCHA Example (Server Part 1/2) Ends -->
164179

165-
# Return the risk score.
180+
# Below code is only used to send response to the client for demo purposes.
181+
# DO NOT send scores or other assessment response to the client.
182+
# Return the response.
166183
return jsonify(
167184
{
168185
"data": {
169186
"score": "{:.1f}".format(assessment_response.risk_analysis.score),
170-
"verdict": verdict,
187+
"label": label,
188+
"reason": reason,
171189
}
172190
}
173191
)
@@ -194,26 +212,27 @@ def on_store_checkout() -> Response:
194212
)
195213

196214
# Check if the token is valid, score is above threshold score and the action equals expected.
197-
if assessment_response.token_properties.valid and \
198-
assessment_response.risk_analysis.score > SAMPLE_THRESHOLD_SCORE and \
199-
assessment_response.token_properties.action == recaptcha_action:
200-
# Check if the cart contains items and proceed to checkout and payment.
201-
# items = json_data["items"]
202-
# Business logic.
203-
# Classify the action as not bad.
204-
verdict = "Not Bad"
205-
else:
206-
# If any of the above condition fails, trigger email/phone verification flow.
207-
# Classify the action as bad.
208-
verdict = "Bad"
215+
# Take action based on the result (BAD/ NOT_BAD).
216+
#
217+
# If 'label' is NOT_BAD:
218+
# Check if the cart contains items and proceed to checkout and payment.
219+
# items = json_data["items"]
220+
# Business logic.
221+
#
222+
# If 'label' is BAD:
223+
# Trigger email/phone verification flow.
224+
label, reason = check_for_bad_action(assessment_response, recaptcha_action)
209225
# <!-- ATTENTION: reCAPTCHA Example (Server Part 1/2) Ends -->
210226

211-
# Return the risk score.
227+
# Below code is only used to send response to the client for demo purposes.
228+
# DO NOT send scores or other assessment response to the client.
229+
# Return the response.
212230
return jsonify(
213231
{
214232
"data": {
215233
"score": "{:.1f}".format(assessment_response.risk_analysis.score),
216-
"verdict": verdict,
234+
"label": label,
235+
"reason": reason,
217236
}
218237
}
219238
)
@@ -240,28 +259,52 @@ def on_comment_submit() -> Response:
240259
)
241260

242261
# Check if the token is valid, score is above threshold score and the action equals expected.
243-
if assessment_response.token_properties.valid and \
244-
assessment_response.risk_analysis.score > SAMPLE_THRESHOLD_SCORE and \
245-
assessment_response.token_properties.action == recaptcha_action:
246-
# Check if comment has safe language and proceed to store in database.
247-
# comment = json_data["comment"]
248-
# Business logic.
249-
# Classify the action as not bad.
250-
verdict = "Not Bad"
251-
else:
252-
# If any of the above condition fails, trigger email/phone verification flow.
253-
# Classify the action as bad.
254-
verdict = "Bad"
262+
# Take action based on the result (BAD/ NOT_BAD).
263+
#
264+
# If 'label' is NOT_BAD:
265+
# Check if comment has safe language and proceed to store in database.
266+
# comment = json_data["comment"]
267+
# Business logic.
268+
#
269+
# If 'label' is BAD:
270+
# Trigger email/phone verification flow.
271+
label, reason = check_for_bad_action(assessment_response, recaptcha_action)
255272
# <!-- ATTENTION: reCAPTCHA Example (Server Part 1/2) Ends -->
256273

257-
# Return the risk score.
274+
# Below code is only used to send response to the client for demo purposes.
275+
# DO NOT send scores or other assessment response to the client.
276+
# Return the response.
258277
return jsonify(
259278
{
260279
"data": {
261280
"score": "{:.1f}".format(assessment_response.risk_analysis.score),
262-
"verdict": verdict,
281+
"label": label,
282+
"reason": reason,
263283
}
264284
}
265285
)
266286
except ValueError or Exception as e:
267287
return jsonify({"data": {"error_msg": str(e.__dict__)}})
288+
289+
290+
# Classify the action as BAD/ NOT_BAD based on conditions specified.
291+
def check_for_bad_action(assessment_response: Assessment, recaptcha_action: str) -> Tuple[str, str]:
292+
reason = ""
293+
label = Label.NOT_BAD.value
294+
295+
# Classify the action as BAD if the token obtained from client is not valid.
296+
if not assessment_response.token_properties.valid:
297+
reason = Error.INVALID_TOKEN.value
298+
label = Label.BAD.value
299+
300+
# Classify the action as BAD if the returned recaptcha action doesn't match the expected.
301+
elif assessment_response.token_properties.action != recaptcha_action:
302+
reason = Error.ACTION_MISMATCH.value
303+
label = Label.BAD.value
304+
305+
# Classify the action as BAD if the returned score is less than or equal to the threshold set.
306+
elif assessment_response.risk_analysis.score <= SAMPLE_THRESHOLD_SCORE:
307+
reason = Error.SCORE_LESS_THAN_THRESHOLD.value
308+
label = Label.BAD.value
309+
310+
return label, reason

0 commit comments

Comments
 (0)