Sebastian Rask
10/31/2024, 7:44 PMDavid Herman
10/31/2024, 7:45 PMSebastian Rask
10/31/2024, 8:00 PM<script src="<https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key>"></script>
<script>
function onClick(e) {
e.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute('reCAPTCHA_site_key', {action: 'submit'}).then(function(token) {
// Add your logic to submit to your backend server here.
});
});
}
</script>
David Herman
10/31/2024, 8:01 PMDavid Herman
10/31/2024, 8:01 PMkobweb create examples/opengl
somewhereDavid Herman
10/31/2024, 8:02 PMDavid Herman
10/31/2024, 8:07 PMDavid Herman
10/31/2024, 8:07 PMDavid Herman
10/31/2024, 8:11 PMkobweb {
app {
index {
head.add {
script(src = "<https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key>") {}
script {
unsafe {
raw(
"""
function onClick(e) {
e.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute('reCAPTCHA_site_key', {action: 'submit'}).then(function(token) {
// Add your logic to submit to your backend server here.
});
});
}
""".trimIndent()
)
}
}
}
}
}
}
Sebastian Rask
10/31/2024, 8:12 PMDavid Herman
10/31/2024, 8:13 PMDavid Herman
10/31/2024, 8:14 PM// Add your logic to submit to your backend server here.
-- that is probably easier to put into your Kotlin code, if I had to guess. But we'll try one thing at a time for now.David Herman
10/31/2024, 8:20 PMbutton
approach could be worth trying to, from the docs. That would look something like:
Button(
Modifier
.classNames("g-recaptcha")
.toAttrs {
attr("data-sitekey", "your_site_key")
attr("data-callback", "onSubmit")
attr("data-action", "submit")
}
) { Text("Submit") }
and then
kobweb {
app {
index {
head.add {
script(src = "<https://www.google.com/recaptcha/api.js>") {}
script {
unsafe {
raw(
"""
function onSubmit(token) {
document.getElementById("demo-form").submit();
}
""".trimIndent()
)
}
}
}
}
}
}
and I guess you'd need to have some form element with ID "demo-form" in your code somewhere.David Herman
10/31/2024, 8:21 PMSebastian Rask
10/31/2024, 8:25 PMDavid Herman
10/31/2024, 8:27 PMDavid Herman
10/31/2024, 8:29 PMSebastian Rask
11/01/2024, 4:33 PMhead.add {
script(src = "<https://www.google.com/recaptcha/api.js>") {}
script {
unsafe {
raw(
"""
function onSubmit(token) {
document.getElementById("form").submit();
}
""".trimIndent()
)
}
}
}
And adding a form with a submit button:
Form(
action = "/",
attrs = Modifier
.id("form")
.toAttrs {
this.attr("method", "GET")
}
) {
Button(
Modifier
.classNames("g-recaptcha")
.attrsModifier {
attr("data-sitekey", "6Le5PnIqAAAAABbNCnbw4rYNCBBBA03tap29U8s5")
attr("data-callback", "onSubmit")
attr("data-action", "submit")
}
.toAttrs()
) { Text("Submit") }
}
This will submit the captcha and navigate the site to the current path, but with a g-recaptcha-response
query parameter appended.
The token value in this parameter can then be send to my backend which will post it to https://www.google.com/recaptcha/api/siteverify with a secret token in order to determine if the user is credible.David Herman
11/01/2024, 4:34 PMSebastian Rask
11/01/2024, 4:35 PMg-recaptcha-response
, so I want to see if I can get the other manual trigger approach to work - I will let you know how that goesDavid Herman
11/01/2024, 5:09 PMAppEntry
block. At that point, you'll be figuring out how to dance the line between Kotlin and JS. I'm assuming you've seen this but check it out if not: https://kotlinlang.org/docs/js-interop.htmlSebastian Rask
11/01/2024, 7:09 PMscript {
unsafe {
raw(
"""
function validateUser() {
grecaptcha.ready(function () {
grecaptcha.execute('6Le5PnIqAAAAABbNCnbw4rYNCBBBA03tap29U8s5', { action: 'submit' }).then(function (token) {
console.log("Token in JavaScript:", token);
if (window.handleCaptchaToken) {
window.handleCaptchaToken(token); // Call the Kotlin function
} else {
console.error("handleCaptchaToken is not defined.");
}
});
});
}
""".trimIndent()
)
}
}
I can then invoke this function with a simple button:
Button(
modifier
.attrsModifier {
attr("onClick", "validateUser()")
}
.toAttrs()
) { Text("Submit Custom") }
And I have registered a Kotlin JS function like this:
@JsName("handleCaptchaToken")
fun handleCaptchaToken(token: String) {
println("Received token in Kotlin: $token")
}
This is where I am at:
I am able to invoke the function and retrieve the token. It is succesfully printed to the console.
However the JS in the build script is unable to invoke handleCaptchaToken (undefined), even though I can see it is generated and available in the generated site js script:
function handleCaptchaToken(token) {\n (0,_kotlin_kotlin_stdlib_mjs__WEBPACK_IMPORTED_MODULE_0__.println2shhhgwwt4c61)('Received token in Kotlin: ' + token);\n}
Any ideas on how to target this function from the custom head script?Sebastian Rask
11/01/2024, 7:10 PMDavid Herman
11/01/2024, 7:21 PMexternal
keyword as a way to wrap and expose whatever the grecaptcha
object is.Sebastian Rask
11/01/2024, 8:47 PMexternal object grecaptcha {
fun ready(callback: () -> Unit)
fun execute(siteKey: String, options: dynamic): dynamic
}
Button(
content = { Text("External Submit") },
onClick = {
grecaptcha.ready {
val options = js("{ action: 'submit' }")
grecaptcha.execute("6Le5PnIqAAAAABbNCnbw4rYNCBBBA03tap29U8s5", options)
.then { token: String ->
println("Captcha token : $token")
}
}
})
Really learned a lot about Kotlin JS today. The interop seem really powerful!David Herman
11/01/2024, 9:56 PMDavid Herman
11/01/2024, 9:56 PM