Skip to content

Commit 5170475

Browse files
committed
Add implementation plan for cookie-based captcha bypass
https://claude.ai/code/session_01PF65GfYeDZP2TroMbmfG6b
1 parent de0458a commit 5170475

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed

plan.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Plan: Add Cookie-Based Captcha Bypass
2+
3+
## Goal
4+
When a user solves the captcha, set a browser cookie (`captcha_passed=true`) that expires after 1 day. On subsequent visits, check for this cookie and skip the captcha if it exists.
5+
6+
## Approach
7+
Use `streamlit_js_eval` (already declared as a dependency in `requirements.txt`) to read/write browser cookies via JavaScript. No new dependencies needed.
8+
9+
### Key consideration: async behavior
10+
`streamlit_js_eval` is a Streamlit component — it returns `None` (or `0`) on the first render, then returns the actual JS result on rerun. The cookie check must handle this:
11+
- `None`/`0` → JS hasn't executed yet, don't decide yet (fall through to captcha)
12+
- `"true"` → cookie found, skip captcha
13+
- Any other string → no valid cookie, show captcha
14+
15+
---
16+
17+
## Changes
18+
19+
### File 1: `src/common/captcha_.py`
20+
21+
**Change 1a — Add import (line 1 area)**
22+
23+
Add `from streamlit_js_eval import streamlit_js_eval` to the imports section.
24+
25+
**Change 1b — Add cookie expiry constant (after line 185)**
26+
27+
Add:
28+
```python
29+
CAPTCHA_COOKIE_MAX_AGE = 86400 # 1 day in seconds
30+
```
31+
32+
**Change 1c — Cookie check at start of `captcha_control()` (lines 206–207)**
33+
34+
Before the existing `if "controllo" not in st.session_state ...` check, insert a cookie check block:
35+
36+
```python
37+
def captcha_control():
38+
# ... (docstring unchanged) ...
39+
40+
# Check if user already passed captcha via browser cookie
41+
cookie_val = streamlit_js_eval(
42+
js_expressions=(
43+
"document.cookie.split('; ')"
44+
".find(c => c.startsWith('captcha_passed='))"
45+
"?.split('=')[1] || ''"
46+
),
47+
key="captcha_cookie_check",
48+
)
49+
# cookie_val is None on first render (JS hasn't executed yet),
50+
# then the actual string value on rerun
51+
if cookie_val == "true":
52+
st.session_state["controllo"] = True
53+
return
54+
55+
# existing code continues: if "controllo" not in st.session_state or ...
56+
```
57+
58+
This will:
59+
- On first render: `cookie_val` is `None` → falls through to the existing captcha logic
60+
- On rerun (JS has executed): `cookie_val` is `"true"` → sets `controllo=True` and returns, skipping captcha entirely
61+
- If cookie doesn't exist: `cookie_val` is `""` → falls through to captcha
62+
63+
**Change 1d — Set cookie after successful captcha verification (line 257 area)**
64+
65+
After `st.session_state["controllo"] = True` and before `st.rerun()`, set the cookie:
66+
67+
```python
68+
if st.session_state["Captcha"].lower() == capta2_text.lower().strip():
69+
del st.session_state["Captcha"]
70+
if "captcha_input" in st.session_state:
71+
del st.session_state["captcha_input"]
72+
col1.empty()
73+
st.session_state["controllo"] = True
74+
# Set browser cookie to remember captcha was passed
75+
streamlit_js_eval(
76+
js_expressions=f"document.cookie = 'captcha_passed=true; max-age={CAPTCHA_COOKIE_MAX_AGE}; path=/; SameSite=Strict'",
77+
key="captcha_cookie_set",
78+
)
79+
st.rerun()
80+
```
81+
82+
---
83+
84+
## Summary of touched files
85+
86+
| File | What changes |
87+
|------|-------------|
88+
| `src/common/captcha_.py` | Add import, add constant, add cookie check at top of `captcha_control()`, set cookie on successful verification |
89+
90+
No other files need changes. No new dependencies.
91+
92+
## Testing notes
93+
- The cookie check adds one extra Streamlit rerun on the very first page load (JS executes, returns value, triggers rerun). This is imperceptible to users.
94+
- The cookie is scoped to `path=/` so it works across all pages of the app.
95+
- `SameSite=Strict` prevents the cookie from being sent in cross-site requests.
96+
- The cookie is only checked in online mode — in local mode, `controllo` is already set to `True` in `common.py:485` before `captcha_control()` is called, so the function returns immediately at the existing check on line 206.

0 commit comments

Comments
 (0)