@prosopo/client-bundle-example 2.11.3 → 2.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$cjs.log +3 -6
- package/.turbo/turbo-build$colon$tsc.log +11 -14
- package/.turbo/turbo-build.log +4 -10
- package/CHANGELOG.md +32 -0
- package/LICENSE +201 -0
- package/README.md +6 -6
- package/dist/cjs/plugins/pages.cjs +7 -0
- package/dist/plugins/pages.js +7 -0
- package/env.staging +5 -4
- package/package.json +53 -53
- package/vite.config.ts +52 -1
- package/env.development +0 -7
- package/env.production +0 -6
- package/src/assets/dummy.txt +0 -0
- package/src/frictionless-explicit-web3.html +0 -237
- package/src/frictionless-explicit.html +0 -205
- package/src/frictionless-implicit.html +0 -192
- package/src/frictionless-manual-start.html +0 -204
- package/src/image-explicit-web3.html +0 -182
- package/src/image-explicit.html +0 -151
- package/src/image-implicit.html +0 -192
- package/src/index.html +0 -192
- package/src/index.ts +0 -15
- package/src/invisible-frictionless-explicit.html +0 -123
- package/src/invisible-frictionless-implicit.html +0 -187
- package/src/invisible-image-explicit.html +0 -146
- package/src/invisible-image-implicit.html +0 -187
- package/src/invisible-pow-explicit.html +0 -146
- package/src/invisible-pow-implicit.html +0 -94
- package/src/invisible-puzzle-explicit.html +0 -145
- package/src/invisible-puzzle-implicit.html +0 -94
- package/src/plugins/code-snippet-injector.ts +0 -179
- package/src/plugins/form-filler-injector.ts +0 -148
- package/src/plugins/layout-injector.ts +0 -256
- package/src/plugins/logo.ts +0 -15
- package/src/plugins/pages.ts +0 -219
- package/src/plugins/placement-injector.ts +0 -114
- package/src/plugins/slots.ts +0 -32
- package/src/plugins/status-log-injector.ts +0 -143
- package/src/pow-explicit-web3.html +0 -179
- package/src/pow-explicit.html +0 -143
- package/src/pow-implicit-sessionid.html +0 -209
- package/src/pow-implicit.html +0 -192
- package/src/puzzle-bind-explicit.html +0 -83
- package/src/puzzle-explicit.html +0 -151
- package/src/puzzle-implicit.html +0 -192
- package/src/styles/demo.css +0 -803
- package/src/tests/explicitRender.unit.test.ts +0 -49
- package/src/tests/plugins.unit.test.ts +0 -490
- package/tsconfig.cjs.json +0 -25
- package/tsconfig.json +0 -31
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.types.json +0 -9
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
// Vite plugin that adds the event log. Demo pages report what the widget is
|
|
16
|
-
// doing through window.updateCaptchaStatus.
|
|
17
|
-
import type { IndexHtmlTransformContext, Plugin } from "vite";
|
|
18
|
-
import { fillSlot } from "./slots.js";
|
|
19
|
-
|
|
20
|
-
// Opening tag of <head>, attributes and all.
|
|
21
|
-
const HEAD_OPEN = /<head(\s[^>]*)?>/i;
|
|
22
|
-
|
|
23
|
-
export default function statusLogInjector(): Plugin {
|
|
24
|
-
// A classic script at the very start of <head>, so it runs during parsing,
|
|
25
|
-
// before any module script. By the time this plugin runs ("post"), Vite has
|
|
26
|
-
// already hoisted the page's own entry module into <head>, and module
|
|
27
|
-
// scripts run in document order: defined in the module below, the helper
|
|
28
|
-
// did not exist yet when the explicit-render pages called it at top level,
|
|
29
|
-
// so they threw before render() and never showed a widget.
|
|
30
|
-
const updateStatusJs = `
|
|
31
|
-
<script>
|
|
32
|
-
function updateCaptchaStatus(message, type = 'info') {
|
|
33
|
-
const statusContainer = document.getElementById('captcha-status');
|
|
34
|
-
if (!statusContainer) return;
|
|
35
|
-
|
|
36
|
-
const time = document.createElement('span');
|
|
37
|
-
time.className = 'status-item__time';
|
|
38
|
-
time.textContent = new Date().toLocaleTimeString();
|
|
39
|
-
const dot = document.createElement('span');
|
|
40
|
-
dot.className = 'status-item__dot';
|
|
41
|
-
const text = document.createElement('span');
|
|
42
|
-
text.className = 'status-item__message';
|
|
43
|
-
text.textContent = message;
|
|
44
|
-
|
|
45
|
-
const statusItem = document.createElement('div');
|
|
46
|
-
statusItem.className = \`status-item status-\${type}\`;
|
|
47
|
-
statusItem.append(time, dot, text);
|
|
48
|
-
statusContainer.appendChild(statusItem);
|
|
49
|
-
statusContainer.scrollTop = statusContainer.scrollHeight;
|
|
50
|
-
|
|
51
|
-
console.log(\`CAPTCHA Status: \${message}\`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
window.updateCaptchaStatus = updateCaptchaStatus;
|
|
55
|
-
</script>
|
|
56
|
-
`;
|
|
57
|
-
|
|
58
|
-
// A module, after the page's entry module, because it wraps the callbacks
|
|
59
|
-
// that module defines.
|
|
60
|
-
const statusLogJs = `
|
|
61
|
-
<script type="module">
|
|
62
|
-
const updateCaptchaStatus = window.updateCaptchaStatus;
|
|
63
|
-
|
|
64
|
-
// Wrap the page's own callbacks so every page logs the same events.
|
|
65
|
-
const originalOnCaptchaFailed = window.onCaptchaFailed;
|
|
66
|
-
window.onCaptchaFailed = function() {
|
|
67
|
-
updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
|
|
68
|
-
if (originalOnCaptchaFailed) originalOnCaptchaFailed();
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const originalOnCaptchaVerified = window.onCaptchaVerified;
|
|
72
|
-
window.onCaptchaVerified = function(output) {
|
|
73
|
-
updateCaptchaStatus('Challenge passed successfully!', 'success');
|
|
74
|
-
updateCaptchaStatus(\`Token generated: \${output.substring(0, 15)}...\`, 'success');
|
|
75
|
-
if (originalOnCaptchaVerified) originalOnCaptchaVerified(output);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const originalOnActionHandler = window.onActionHandler;
|
|
79
|
-
window.onActionHandler = function(token) {
|
|
80
|
-
const procaptchaElements = document.getElementsByName('procaptcha-response');
|
|
81
|
-
|
|
82
|
-
if (!procaptchaElements.length) {
|
|
83
|
-
updateCaptchaStatus('Error: No CAPTCHA response token found', 'error');
|
|
84
|
-
alert("Must complete captcha");
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
updateCaptchaStatus('Form submission initiated with valid CAPTCHA token', 'info');
|
|
89
|
-
if (originalOnActionHandler) originalOnActionHandler(token);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
document.addEventListener('procaptcha:execute', function(e) {
|
|
93
|
-
const url = window.location.pathname;
|
|
94
|
-
const isFrictionless = url.includes('frictionless');
|
|
95
|
-
const isImage = url.includes('image');
|
|
96
|
-
const isPow = url.includes('pow');
|
|
97
|
-
|
|
98
|
-
let captchaType = 'unknown';
|
|
99
|
-
if (isFrictionless) captchaType = 'frictionless';
|
|
100
|
-
else if (isImage) captchaType = 'image';
|
|
101
|
-
else if (isPow) captchaType = 'pow';
|
|
102
|
-
|
|
103
|
-
updateCaptchaStatus(\`CAPTCHA execution started: type=\${captchaType}\`, 'info');
|
|
104
|
-
updateCaptchaStatus(\`Container: \${e.detail.containerId}, timestamp: \${new Date(e.detail.timestamp).toLocaleTimeString()}\`, 'info');
|
|
105
|
-
});
|
|
106
|
-
</script>
|
|
107
|
-
`;
|
|
108
|
-
|
|
109
|
-
const statusLogHtml = `<div id="captcha-status" class="captcha-status" aria-live="polite"></div>`;
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
name: "status-log-injector",
|
|
113
|
-
transformIndexHtml: {
|
|
114
|
-
order: "post",
|
|
115
|
-
handler(html: string, _ctx: IndexHtmlTransformContext): string {
|
|
116
|
-
if (!html.includes("<body") || !html.includes("</body>")) {
|
|
117
|
-
return html;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (html.includes('id="captcha-status"')) {
|
|
121
|
-
return html;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const withHelper = HEAD_OPEN.test(html)
|
|
125
|
-
? html.replace(HEAD_OPEN, (open) => `${open}${updateStatusJs}`)
|
|
126
|
-
: html;
|
|
127
|
-
const withJs = withHelper.replace(
|
|
128
|
-
"</head>",
|
|
129
|
-
() => `${statusLogJs}</head>`,
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
const inSlot = fillSlot(withJs, "events", statusLogHtml);
|
|
133
|
-
if (inSlot) {
|
|
134
|
-
return inSlot;
|
|
135
|
-
}
|
|
136
|
-
if (withJs.includes("</form>")) {
|
|
137
|
-
return withJs.replace("</form>", () => `</form>${statusLogHtml}`);
|
|
138
|
-
}
|
|
139
|
-
return withJs.replace("</body>", () => `${statusLogHtml}</body>`);
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
<title>Procaptcha demo: Proof of Work, explicit rendering, web3</title>
|
|
6
|
-
<script type="module">
|
|
7
|
-
|
|
8
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
9
|
-
updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
|
|
10
|
-
|
|
11
|
-
// Monitor DOM for CAPTCHA initialization
|
|
12
|
-
const observer = new MutationObserver((mutations) => {
|
|
13
|
-
mutations.forEach((mutation) => {
|
|
14
|
-
if (mutation.addedNodes.length) {
|
|
15
|
-
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
|
16
|
-
const node = mutation.addedNodes[i];
|
|
17
|
-
if (node.classList && (node.classList.contains('procaptcha') ||
|
|
18
|
-
node.querySelector && node.querySelector('.procaptcha'))) {
|
|
19
|
-
updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
|
|
20
|
-
observer.disconnect();
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
</script>
|
|
32
|
-
</head>
|
|
33
|
-
<body>
|
|
34
|
-
<div class="demo-card">
|
|
35
|
-
|
|
36
|
-
<form id="demo-form" class="demo-form">
|
|
37
|
-
<h2>Create an account</h2>
|
|
38
|
-
|
|
39
|
-
<div class="demo-field">
|
|
40
|
-
<label for="name">Name</label>
|
|
41
|
-
<input type="text" id="name" name="name" required />
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
<div class="demo-field">
|
|
45
|
-
<label for="email">Email</label>
|
|
46
|
-
<input type="email" id="email" name="email" required />
|
|
47
|
-
</div>
|
|
48
|
-
|
|
49
|
-
<div id="procaptcha-container"></div>
|
|
50
|
-
|
|
51
|
-
<button type="submit" class="demo-button">Submit</button>
|
|
52
|
-
|
|
53
|
-
<div id="error"></div>
|
|
54
|
-
</form>
|
|
55
|
-
|
|
56
|
-
<div id="result" class="demo-result" style="display: none;"></div>
|
|
57
|
-
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
<script type="module">
|
|
61
|
-
import { render } from "%VITE_BUNDLE_URL%"
|
|
62
|
-
import { web3AccountsSubscribe, web3Enable } from "@polkadot/extension-dapp";
|
|
63
|
-
|
|
64
|
-
// Form validation function
|
|
65
|
-
function validateForm() {
|
|
66
|
-
const nameInput = document.getElementById('name');
|
|
67
|
-
const emailInput = document.getElementById('email');
|
|
68
|
-
const errorDisplay = document.getElementById('error');
|
|
69
|
-
|
|
70
|
-
if (!nameInput.value || !emailInput.value) {
|
|
71
|
-
errorDisplay.textContent = 'Please fill out all fields';
|
|
72
|
-
errorDisplay.style.display = 'block';
|
|
73
|
-
window.updateCaptchaStatus('Form validation failed: Empty fields', 'error');
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Basic email validation
|
|
78
|
-
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
79
|
-
if (!emailPattern.test(emailInput.value)) {
|
|
80
|
-
errorDisplay.textContent = 'Please enter a valid email address';
|
|
81
|
-
errorDisplay.style.display = 'block';
|
|
82
|
-
window.updateCaptchaStatus('Form validation failed: Invalid email format', 'error');
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
errorDisplay.style.display = 'none';
|
|
87
|
-
window.updateCaptchaStatus('Form validation passed', 'info');
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Callback for successful CAPTCHA verification
|
|
92
|
-
function handleCaptchaResponse(token) {
|
|
93
|
-
console.log('Procaptcha verified, token:', token);
|
|
94
|
-
window.updateCaptchaStatus('Challenge passed successfully!', 'success');
|
|
95
|
-
window.updateCaptchaStatus(`Token generated: ${token.substring(0, 15)}...`, 'success');
|
|
96
|
-
|
|
97
|
-
const name = document.getElementById('name').value;
|
|
98
|
-
const email = document.getElementById('email').value;
|
|
99
|
-
|
|
100
|
-
// Display result
|
|
101
|
-
const resultElement = document.getElementById('result');
|
|
102
|
-
resultElement.innerHTML = `<strong>Form submitted with:</strong><br>
|
|
103
|
-
- Name: ${name}<br>
|
|
104
|
-
- Email: ${email}<br>
|
|
105
|
-
- Procaptcha verified: Yes`;
|
|
106
|
-
resultElement.style.display = 'block';
|
|
107
|
-
|
|
108
|
-
window.updateCaptchaStatus('Form submission completed', 'success');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Callback for failed CAPTCHA verification
|
|
112
|
-
function handleCaptchaFailed() {
|
|
113
|
-
console.log('Captcha verification failed');
|
|
114
|
-
window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
|
|
115
|
-
document.getElementById('error').textContent = 'CAPTCHA verification failed. Please try again.';
|
|
116
|
-
document.getElementById('error').style.display = 'block';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Get web3 accounts and create select element
|
|
120
|
-
async function getWeb3Accounts() {
|
|
121
|
-
return new Promise((resolve) =>{ web3AccountsSubscribe((accounts) => {
|
|
122
|
-
if (accounts.length === 0) {
|
|
123
|
-
window.updateCaptchaStatus('No accounts found. Please connect your wallet.', 'error');
|
|
124
|
-
} else {
|
|
125
|
-
window.updateCaptchaStatus(`Connected to ${accounts.length} account(s).`, 'info');
|
|
126
|
-
// render a modal and get the user to select 1 account
|
|
127
|
-
const accountSelect = document.createElement('select');
|
|
128
|
-
accountSelect.id = 'account-select';
|
|
129
|
-
accountSelect.classList.add('account-select');
|
|
130
|
-
accounts.forEach((account) => {
|
|
131
|
-
const option = document.createElement('option');
|
|
132
|
-
option.value = account.address;
|
|
133
|
-
option.textContent = `${account.meta.name} (${account.address})`;
|
|
134
|
-
accountSelect.appendChild(option);
|
|
135
|
-
});
|
|
136
|
-
const accountContainer = document.createElement('div');
|
|
137
|
-
accountContainer.innerHTML = '<label for="account-select"><h2>Select Account:</h2></label>';
|
|
138
|
-
accountContainer.appendChild(accountSelect);
|
|
139
|
-
document.getElementById('demo-form').prepend(accountContainer);
|
|
140
|
-
window.updateCaptchaStatus('Accounts loaded successfully', 'info');
|
|
141
|
-
}
|
|
142
|
-
resolve(accounts);
|
|
143
|
-
})});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Wait for DOM content to be loaded
|
|
147
|
-
document.addEventListener('DOMContentLoaded', async function() {
|
|
148
|
-
window.updateCaptchaStatus('Initializing CAPTCHA with explicit render call', 'info');
|
|
149
|
-
|
|
150
|
-
await web3Enable("Procaptcha Demo");
|
|
151
|
-
|
|
152
|
-
await getWeb3Accounts();
|
|
153
|
-
|
|
154
|
-
// Render the CAPTCHA
|
|
155
|
-
const account = document.getElementById('account-select') ? document.getElementById('account-select').value : null;
|
|
156
|
-
window.updateCaptchaStatus(`Rendering the CAPTCHA for account ${account}`, 'info');
|
|
157
|
-
const widgetId = await render(document.getElementById('procaptcha-container'), {
|
|
158
|
-
siteKey: import.meta.env.PROSOPO_SITE_KEY_POW,
|
|
159
|
-
callback: handleCaptchaResponse,
|
|
160
|
-
"failed-callback": handleCaptchaFailed,
|
|
161
|
-
web3:true,
|
|
162
|
-
userAccountAddress: account
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
window.updateCaptchaStatus('CAPTCHA render function called with widget ID: ' + widgetId, 'info');
|
|
166
|
-
|
|
167
|
-
// Add form submit handler
|
|
168
|
-
document.getElementById('demo-form').addEventListener('submit', function(e) {
|
|
169
|
-
e.preventDefault();
|
|
170
|
-
window.updateCaptchaStatus('Form submission attempted', 'info');
|
|
171
|
-
if (validateForm()) {
|
|
172
|
-
// Form is valid, CAPTCHA verification will be handled by the callback
|
|
173
|
-
window.updateCaptchaStatus('Waiting for CAPTCHA verification', 'info');
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
</script>
|
|
178
|
-
</body>
|
|
179
|
-
</html>
|
package/src/pow-explicit.html
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
<title>Procaptcha demo: Proof of Work, explicit rendering</title>
|
|
6
|
-
<script type="module">
|
|
7
|
-
|
|
8
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
9
|
-
updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
|
|
10
|
-
|
|
11
|
-
// Monitor DOM for CAPTCHA initialization
|
|
12
|
-
const observer = new MutationObserver((mutations) => {
|
|
13
|
-
mutations.forEach((mutation) => {
|
|
14
|
-
if (mutation.addedNodes.length) {
|
|
15
|
-
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
|
16
|
-
const node = mutation.addedNodes[i];
|
|
17
|
-
if (node.classList && (node.classList.contains('procaptcha') ||
|
|
18
|
-
node.querySelector && node.querySelector('.procaptcha'))) {
|
|
19
|
-
updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
|
|
20
|
-
observer.disconnect();
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
</script>
|
|
32
|
-
</head>
|
|
33
|
-
<body>
|
|
34
|
-
<div class="demo-card">
|
|
35
|
-
|
|
36
|
-
<form id="demo-form" class="demo-form">
|
|
37
|
-
<h2>Create an account</h2>
|
|
38
|
-
|
|
39
|
-
<div class="demo-field">
|
|
40
|
-
<label for="name">Name</label>
|
|
41
|
-
<input type="text" id="name" name="name" required />
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
<div class="demo-field">
|
|
45
|
-
<label for="email">Email</label>
|
|
46
|
-
<input type="email" id="email" name="email" required />
|
|
47
|
-
</div>
|
|
48
|
-
|
|
49
|
-
<div id="procaptcha-container"></div>
|
|
50
|
-
|
|
51
|
-
<button type="submit" class="demo-button">Submit</button>
|
|
52
|
-
|
|
53
|
-
<div id="error"></div>
|
|
54
|
-
</form>
|
|
55
|
-
|
|
56
|
-
<div id="result" class="demo-result" style="display: none;"></div>
|
|
57
|
-
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
<script type="module">
|
|
61
|
-
import { render } from "%VITE_BUNDLE_URL%"
|
|
62
|
-
|
|
63
|
-
// Form validation function
|
|
64
|
-
function validateForm() {
|
|
65
|
-
const nameInput = document.getElementById('name');
|
|
66
|
-
const emailInput = document.getElementById('email');
|
|
67
|
-
const errorDisplay = document.getElementById('error');
|
|
68
|
-
|
|
69
|
-
if (!nameInput.value || !emailInput.value) {
|
|
70
|
-
errorDisplay.textContent = 'Please fill out all fields';
|
|
71
|
-
errorDisplay.style.display = 'block';
|
|
72
|
-
window.updateCaptchaStatus('Form validation failed: Empty fields', 'error');
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Basic email validation
|
|
77
|
-
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
78
|
-
if (!emailPattern.test(emailInput.value)) {
|
|
79
|
-
errorDisplay.textContent = 'Please enter a valid email address';
|
|
80
|
-
errorDisplay.style.display = 'block';
|
|
81
|
-
window.updateCaptchaStatus('Form validation failed: Invalid email format', 'error');
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
errorDisplay.style.display = 'none';
|
|
86
|
-
window.updateCaptchaStatus('Form validation passed', 'info');
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Callback for successful CAPTCHA verification
|
|
91
|
-
function handleCaptchaResponse(token) {
|
|
92
|
-
console.log('Procaptcha verified, token:', token);
|
|
93
|
-
window.updateCaptchaStatus('Challenge passed successfully!', 'success');
|
|
94
|
-
window.updateCaptchaStatus(`Token generated: ${token.substring(0, 15)}...`, 'success');
|
|
95
|
-
|
|
96
|
-
const name = document.getElementById('name').value;
|
|
97
|
-
const email = document.getElementById('email').value;
|
|
98
|
-
|
|
99
|
-
// Display result
|
|
100
|
-
const resultElement = document.getElementById('result');
|
|
101
|
-
resultElement.innerHTML = `<strong>Form submitted with:</strong><br>
|
|
102
|
-
- Name: ${name}<br>
|
|
103
|
-
- Email: ${email}<br>
|
|
104
|
-
- Procaptcha verified: Yes`;
|
|
105
|
-
resultElement.style.display = 'block';
|
|
106
|
-
|
|
107
|
-
window.updateCaptchaStatus('Form submission completed', 'success');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Callback for failed CAPTCHA verification
|
|
111
|
-
function handleCaptchaFailed() {
|
|
112
|
-
console.log('Captcha verification failed');
|
|
113
|
-
window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
|
|
114
|
-
document.getElementById('error').textContent = 'CAPTCHA verification failed. Please try again.';
|
|
115
|
-
document.getElementById('error').style.display = 'block';
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Wait for DOM content to be loaded
|
|
119
|
-
document.addEventListener('DOMContentLoaded', async function() {
|
|
120
|
-
window.updateCaptchaStatus('Initializing CAPTCHA with explicit render call', 'info');
|
|
121
|
-
|
|
122
|
-
// Render the CAPTCHA
|
|
123
|
-
const widgetId = await render(document.getElementById('procaptcha-container'), {
|
|
124
|
-
siteKey: import.meta.env.PROSOPO_SITE_KEY_POW,
|
|
125
|
-
callback: handleCaptchaResponse,
|
|
126
|
-
"failed-callback": handleCaptchaFailed
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
window.updateCaptchaStatus('CAPTCHA render function called with widget ID: ' + widgetId, 'info');
|
|
130
|
-
|
|
131
|
-
// Add form submit handler
|
|
132
|
-
document.getElementById('demo-form').addEventListener('submit', function(e) {
|
|
133
|
-
e.preventDefault();
|
|
134
|
-
window.updateCaptchaStatus('Form submission attempted', 'info');
|
|
135
|
-
if (validateForm()) {
|
|
136
|
-
// Form is valid, CAPTCHA verification will be handled by the callback
|
|
137
|
-
window.updateCaptchaStatus('Waiting for CAPTCHA verification', 'info');
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
</script>
|
|
142
|
-
</body>
|
|
143
|
-
</html>
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
<title>Procaptcha demo: Proof of Work, client session id</title>
|
|
6
|
-
<script type="module">
|
|
7
|
-
|
|
8
|
-
// The site's own per-user session id. Rendered onto the widget via
|
|
9
|
-
// data-sessionid and sent again at verify as clientSessionId; the
|
|
10
|
-
// provider only accepts the token when the two agree.
|
|
11
|
-
//
|
|
12
|
-
// `?mismatch=1` deliberately verifies with a DIFFERENT id, which must
|
|
13
|
-
// be rejected — that is the half of the contract that proves the
|
|
14
|
-
// correlation is actually enforced rather than silently skipped.
|
|
15
|
-
const CLIENT_SESSION_ID = 'cypress-session-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';
|
|
16
|
-
const MISMATCHED_SESSION_ID = 'cypress-session-99999999-9999-9999-9999-999999999999';
|
|
17
|
-
const verifyingSessionId = () =>
|
|
18
|
-
new URLSearchParams(window.location.search).get('mismatch') === '1'
|
|
19
|
-
? MISMATCHED_SESSION_ID
|
|
20
|
-
: CLIENT_SESSION_ID;
|
|
21
|
-
|
|
22
|
-
window.addEventListener('load', () => {
|
|
23
|
-
|
|
24
|
-
window.setIsError = (isError) => {
|
|
25
|
-
const messageContainer = document.getElementById('messageContainer');
|
|
26
|
-
messageContainer.style.color = isError ? 'red' : 'black';
|
|
27
|
-
messageContainer.style.display = 'block';
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
window.setMessage = (message) => {
|
|
31
|
-
document.getElementById('message').innerText = message;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
window.onLoggedIn = (token) => {
|
|
35
|
-
updateCaptchaStatus('Logged in, fetching private resource', 'info');
|
|
36
|
-
const url = new URL("/private", config.serverUrl).href;
|
|
37
|
-
console.log("getting private resource with token ", token, "at", url);
|
|
38
|
-
fetch(url, {
|
|
39
|
-
method: "GET",
|
|
40
|
-
headers: {
|
|
41
|
-
Origin: "http://localhost:9232", // TODO: change this to env var
|
|
42
|
-
"Content-Type": "application/json",
|
|
43
|
-
Authorization: `Bearer ${token}`,
|
|
44
|
-
},
|
|
45
|
-
})
|
|
46
|
-
.then(async (res) => {
|
|
47
|
-
try {
|
|
48
|
-
const jsonRes = await res.json();
|
|
49
|
-
if (res.status === 200) {
|
|
50
|
-
updateCaptchaStatus(`Successfully accessed private resource`, 'success');
|
|
51
|
-
setMessage(jsonRes.message);
|
|
52
|
-
} else {
|
|
53
|
-
updateCaptchaStatus(`Failed to access private resource: ${res.status}`, 'error');
|
|
54
|
-
}
|
|
55
|
-
} catch (err) {
|
|
56
|
-
console.log(err);
|
|
57
|
-
updateCaptchaStatus(`Error parsing response: ${err}`, 'error');
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
.catch((err) => {
|
|
61
|
-
console.log(err);
|
|
62
|
-
updateCaptchaStatus(`Fetch error: ${err}`, 'error');
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
window.onActionHandler = () => {
|
|
67
|
-
updateCaptchaStatus('Form submission initiated', 'info');
|
|
68
|
-
|
|
69
|
-
const procaptchaElements = document.getElementsByName('procaptcha-response');
|
|
70
|
-
|
|
71
|
-
if (!procaptchaElements.length) {
|
|
72
|
-
updateCaptchaStatus('Error: No CAPTCHA response found', 'error');
|
|
73
|
-
alert("Must complete captcha");
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const procaptchaToken = procaptchaElements[0].value;
|
|
78
|
-
updateCaptchaStatus(`Token received: ${procaptchaToken.substring(0, 15)}...`, 'success');
|
|
79
|
-
|
|
80
|
-
const payload = {
|
|
81
|
-
email: document.getElementById('email').value,
|
|
82
|
-
name: document.getElementById('name').value,
|
|
83
|
-
password: document.getElementById('password').value,
|
|
84
|
-
siteKey: import.meta.env.PROSOPO_SITE_KEY_POW,
|
|
85
|
-
"procaptcha-response": procaptchaToken,
|
|
86
|
-
clientSessionId: verifyingSessionId(),
|
|
87
|
-
};
|
|
88
|
-
const url = new URL('signup', import.meta.env.PROSOPO_SERVER_URL).href;
|
|
89
|
-
console.log("posting to", url, "with payload", payload);
|
|
90
|
-
fetch(url, {
|
|
91
|
-
method: "POST",
|
|
92
|
-
headers: {
|
|
93
|
-
"Content-Type": "application/json",
|
|
94
|
-
},
|
|
95
|
-
body: JSON.stringify(payload),
|
|
96
|
-
contentType: "application/json",
|
|
97
|
-
}).then((response) => {
|
|
98
|
-
return new Promise((resolve) => response.json()
|
|
99
|
-
.then((json) => resolve({
|
|
100
|
-
status: response.status,
|
|
101
|
-
ok: response.ok,
|
|
102
|
-
json,
|
|
103
|
-
})))
|
|
104
|
-
})
|
|
105
|
-
.then(async ({status, json, ok}) => {
|
|
106
|
-
console.log("status", status, "json", json.message, "ok", ok);
|
|
107
|
-
console.log("json", json)
|
|
108
|
-
try {
|
|
109
|
-
if (status !== 200) {
|
|
110
|
-
updateCaptchaStatus(`API Error: ${json.message}`, 'error');
|
|
111
|
-
setIsError(true);
|
|
112
|
-
setMessage(json.message);
|
|
113
|
-
} else {
|
|
114
|
-
updateCaptchaStatus(`API Success: ${json.message}`, 'success');
|
|
115
|
-
setIsError(false);
|
|
116
|
-
setMessage(json.message);
|
|
117
|
-
}
|
|
118
|
-
} catch (err) {
|
|
119
|
-
console.log(err);
|
|
120
|
-
updateCaptchaStatus(`Error processing response: ${err}`, 'error');
|
|
121
|
-
}
|
|
122
|
-
})
|
|
123
|
-
.catch((err) => {
|
|
124
|
-
console.log(err);
|
|
125
|
-
updateCaptchaStatus(`Fetch error: ${err}`, 'error');
|
|
126
|
-
setIsError(true);
|
|
127
|
-
setMessage("Error: " + err);
|
|
128
|
-
});
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
|
|
132
|
-
|
|
133
|
-
// Monitor DOM for CAPTCHA initialization
|
|
134
|
-
const observer = new MutationObserver((mutations) => {
|
|
135
|
-
mutations.forEach((mutation) => {
|
|
136
|
-
if (mutation.addedNodes.length) {
|
|
137
|
-
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
|
138
|
-
const node = mutation.addedNodes[i];
|
|
139
|
-
if (node.classList && (node.classList.contains('procaptcha') ||
|
|
140
|
-
node.querySelector && node.querySelector('.procaptcha'))) {
|
|
141
|
-
updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
|
|
142
|
-
observer.disconnect();
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
window.onCaptchaFailed = function () {
|
|
154
|
-
console.log('Challenge failed');
|
|
155
|
-
updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
window.onCaptchaVerified = (output) => {
|
|
159
|
-
console.log('Challenge passed');
|
|
160
|
-
updateCaptchaStatus('Challenge passed successfully!', 'success');
|
|
161
|
-
updateCaptchaStatus(`Token generated: ${output.substring(0, 15)}...`, 'success');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Add custom event listener for CAPTCHA verification stages
|
|
165
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
166
|
-
setTimeout(() => {
|
|
167
|
-
updateCaptchaStatus('CAPTCHA script loaded and initialized', 'info');
|
|
168
|
-
updateCaptchaStatus('Waiting for user interaction...', 'info');
|
|
169
|
-
}, 500);
|
|
170
|
-
});
|
|
171
|
-
</script>
|
|
172
|
-
<script id="procaptchaScript" type="module" src="%VITE_BUNDLE_URL%" async defer></script>
|
|
173
|
-
</head>
|
|
174
|
-
<body>
|
|
175
|
-
|
|
176
|
-
<div class="demo-card">
|
|
177
|
-
|
|
178
|
-
<form action="%PROSOPO_SERVER_URL%/signup" method="POST" class="demo-form">
|
|
179
|
-
<h2>Create an account</h2>
|
|
180
|
-
<p class="demo-note">Renders with <code>data-sessionid</code> and verifies with the same <code>clientSessionId</code>.</p>
|
|
181
|
-
<div class="demo-field">
|
|
182
|
-
<label for="name">Name</label>
|
|
183
|
-
<input id="name" type="text" name="name" required/>
|
|
184
|
-
</div>
|
|
185
|
-
<div class="demo-field">
|
|
186
|
-
<label for="email">Email Address</label>
|
|
187
|
-
<input id="email" type="email" name="email" required/>
|
|
188
|
-
</div>
|
|
189
|
-
<div class="demo-field">
|
|
190
|
-
<label for="password">Password</label>
|
|
191
|
-
<input id="password" type="password" name="password" required/>
|
|
192
|
-
</div>
|
|
193
|
-
<div class="demo-field">
|
|
194
|
-
<div
|
|
195
|
-
class="procaptcha"
|
|
196
|
-
data-theme="light"
|
|
197
|
-
data-sitekey="%PROSOPO_SITE_KEY_POW%"
|
|
198
|
-
data-sessionid="cypress-session-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
|
199
|
-
data-failed-callback="onCaptchaFailed"
|
|
200
|
-
data-callback="onCaptchaVerified"
|
|
201
|
-
></div>
|
|
202
|
-
</div>
|
|
203
|
-
<div id="messageContainer" style="display: none; color: black;"><span id="message"></span></div>
|
|
204
|
-
<button type="button" class="demo-button" onclick="onActionHandler()" data-cy="submit-button">Submit</button>
|
|
205
|
-
</form>
|
|
206
|
-
|
|
207
|
-
</div>
|
|
208
|
-
</body>
|
|
209
|
-
</html>
|