aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGusted <williamzijl7@hotmail.com>2022-08-10 13:20:10 +0000
committerGitHub <noreply@github.com>2022-08-10 15:20:10 +0200
commit58de07e5fd14c23dd4c4a315b8c5b3fb3219f66a (patch)
treecec2e02c494e19a42bc6095c480d93354259b669
parent452272c0384d7033ffde18bcb1167e8e49ff598c (diff)
downloadgitea-58de07e5fd14c23dd4c4a315b8c5b3fb3219f66a.tar.gz
gitea-58de07e5fd14c23dd4c4a315b8c5b3fb3219f66a.zip
Add support mCaptcha as captcha provider (#20458)
https://mcaptcha.org/ Co-authored-by: Felipe Leopoldo Sologuren GutiƩrrez <fsologureng@users.noreply.github.com>
-rw-r--r--custom/conf/app.example.ini13
-rw-r--r--docs/content/doc/advanced/config-cheat-sheet.en-us.md7
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--modules/mcaptcha/mcaptcha.go27
-rw-r--r--modules/setting/service.go6
-rw-r--r--modules/setting/setting.go1
-rw-r--r--package-lock.json63
-rw-r--r--package.json1
-rw-r--r--routers/web/auth/auth.go7
-rw-r--r--routers/web/auth/linkaccount.go9
-rw-r--r--routers/web/auth/openid.go11
-rw-r--r--services/forms/user_form.go1
-rw-r--r--services/forms/user_form_auth_openid.go1
-rw-r--r--templates/user/auth/signup_inner.tmpl8
-rw-r--r--templates/user/auth/signup_openid_register.tmpl5
-rw-r--r--web_src/js/features/mcaptcha.js16
-rw-r--r--web_src/js/index.js2
-rw-r--r--web_src/less/_form.less3
-rw-r--r--web_src/less/helpers.less5
20 files changed, 183 insertions, 6 deletions
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 83b3048bc6..ab22899955 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -698,9 +698,11 @@ ROUTER = console
;; Enable captcha validation for registration
;ENABLE_CAPTCHA = false
;;
-;; Type of captcha you want to use. Options: image, recaptcha, hcaptcha
+;; Type of captcha you want to use. Options: image, recaptcha, hcaptcha, mcaptcha.
;CAPTCHA_TYPE = image
;;
+;; Change this to use recaptcha.net or other recaptcha service
+;RECAPTCHA_URL = https://www.google.com/recaptcha/
;; Enable recaptcha to use Google's recaptcha service
;; Go to https://www.google.com/recaptcha/admin to sign up for a key
;RECAPTCHA_SECRET =
@@ -710,8 +712,13 @@ ROUTER = console
;HCAPTCHA_SECRET =
;HCAPTCHA_SITEKEY =
;;
-;; Change this to use recaptcha.net or other recaptcha service
-;RECAPTCHA_URL = https://www.google.com/recaptcha/
+;; Change this to use demo.mcaptcha.org or your self-hosted mcaptcha.org instance.
+;MCAPTCHA_URL = https://demo.mcaptcha.org
+;;
+;; Go to your configured mCaptcha instance and register a sitekey
+;; and use your account's secret.
+;MCAPTCHA_SECRET =
+;MCAPTCHA_SITEKEY =
;;
;; Default value for KeepEmailPrivate
;; Each new user will get the value of this setting copied into their profile
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 6b050e12b8..ad971e866f 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -579,13 +579,16 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o
provided email rather than a generated email.
- `ENABLE_CAPTCHA`: **false**: Enable this to use captcha validation for registration.
- `REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA`: **false**: Enable this to force captcha validation
- even for External Accounts (i.e. GitHub, OpenID Connect, etc). You must `ENABLE_CAPTCHA` also.
-- `CAPTCHA_TYPE`: **image**: \[image, recaptcha, hcaptcha\]
+ even for External Accounts (i.e. GitHub, OpenID Connect, etc). You also must enable `ENABLE_CAPTCHA`.
+- `CAPTCHA_TYPE`: **image**: \[image, recaptcha, hcaptcha, mcaptcha\]
- `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha.
- `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha.
- `RECAPTCHA_URL`: **https://www.google.com/recaptcha/**: Set the recaptcha url - allows the use of recaptcha net.
- `HCAPTCHA_SECRET`: **""**: Sign up at https://www.hcaptcha.com/ to get a secret for hcaptcha.
- `HCAPTCHA_SITEKEY`: **""**: Sign up at https://www.hcaptcha.com/ to get a sitekey for hcaptcha.
+- `MCAPTCHA_SECRET`: **""**: Go to your mCaptcha instance to get a secret for mCaptcha.
+- `MCAPTCHA_SITEKEY`: **""**: Go to your mCaptcha instance to get a sitekey for mCaptcha.
+- `MCAPTCHA_URL` **https://demo.mcaptcha.org/**: Set the mCaptcha URL.
- `DEFAULT_KEEP_EMAIL_PRIVATE`: **false**: By default set users to keep their email address private.
- `DEFAULT_ALLOW_CREATE_ORGANIZATION`: **true**: Allow new users to create organizations by default.
- `DEFAULT_USER_IS_RESTRICTED`: **false**: Give new users restricted permissions by default
diff --git a/go.mod b/go.mod
index 6d41af507d..fa6fb911db 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.18
require (
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b
code.gitea.io/sdk/gitea v0.15.1
+ codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
gitea.com/go-chi/cache v0.2.0
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
diff --git a/go.sum b/go.sum
index 124e65a727..7f7ed7fe2d 100644
--- a/go.sum
+++ b/go.sum
@@ -62,6 +62,8 @@ code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b/go.mod h1:zcNbT/aJE
code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M=
code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
+codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222 h1:PCW4i+gnQ9XxF8V+nBch3KWdGe4MiP3xXUCA/z0jhHk=
+codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0=
contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
diff --git a/modules/mcaptcha/mcaptcha.go b/modules/mcaptcha/mcaptcha.go
new file mode 100644
index 0000000000..b889cf423b
--- /dev/null
+++ b/modules/mcaptcha/mcaptcha.go
@@ -0,0 +1,27 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package mcaptcha
+
+import (
+ "context"
+ "fmt"
+
+ "code.gitea.io/gitea/modules/setting"
+
+ "codeberg.org/gusted/mcaptcha"
+)
+
+func Verify(ctx context.Context, token string) (bool, error) {
+ valid, err := mcaptcha.Verify(ctx, &mcaptcha.VerifyOpts{
+ InstanceURL: setting.Service.McaptchaURL,
+ Sitekey: setting.Service.McaptchaSitekey,
+ Secret: setting.Service.McaptchaSecret,
+ Token: token,
+ })
+ if err != nil {
+ return false, fmt.Errorf("wasn't able to verify mCaptcha: %v", err)
+ }
+ return valid, nil
+}
diff --git a/modules/setting/service.go b/modules/setting/service.go
index bd97e10b0f..af8a72cc6d 100644
--- a/modules/setting/service.go
+++ b/modules/setting/service.go
@@ -47,6 +47,9 @@ var Service = struct {
RecaptchaURL string
HcaptchaSecret string
HcaptchaSitekey string
+ McaptchaSecret string
+ McaptchaSitekey string
+ McaptchaURL string
DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool
DefaultUserIsRestricted bool
@@ -133,6 +136,9 @@ func newService() {
Service.RecaptchaURL = sec.Key("RECAPTCHA_URL").MustString("https://www.google.com/recaptcha/")
Service.HcaptchaSecret = sec.Key("HCAPTCHA_SECRET").MustString("")
Service.HcaptchaSitekey = sec.Key("HCAPTCHA_SITEKEY").MustString("")
+ Service.McaptchaURL = sec.Key("MCAPTCHA_URL").MustString("https://demo.mcaptcha.org/")
+ Service.McaptchaSecret = sec.Key("MCAPTCHA_SECRET").MustString("")
+ Service.McaptchaSitekey = sec.Key("MCAPTCHA_SITEKEY").MustString("")
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false)
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 465dc75cad..0af743dd97 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -59,6 +59,7 @@ const (
ImageCaptcha = "image"
ReCaptcha = "recaptcha"
HCaptcha = "hcaptcha"
+ MCaptcha = "mcaptcha"
)
// settings
diff --git a/package-lock.json b/package-lock.json
index 9451935385..aabbd84fd9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"license": "MIT",
"dependencies": {
"@claviska/jquery-minicolors": "2.3.6",
+ "@mcaptcha/vanilla-glue": "0.1.0-alpha-2",
"@primer/octicons": "17.4.0",
"add-asset-webpack-plugin": "2.0.1",
"css-loader": "6.7.1",
@@ -1662,6 +1663,55 @@
"jsep": "^0.4.0||^1.0.0"
}
},
+ "node_modules/@mcaptcha/core-glue": {
+ "version": "0.1.0-alpha-3",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-3.tgz",
+ "integrity": "sha512-avphBVgf3PPDWuUoDsB2qiXAss2pc00lUILswJaMQofr8FQyflzkhha8H2Z+qGFiX0Iib/yyP2TOtBDbHqE9Tg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "http://mcaptcha.org/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/mcaptcha"
+ },
+ {
+ "type": "individual",
+ "url": "http://batsense.net/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/realaravinth"
+ }
+ ]
+ },
+ "node_modules/@mcaptcha/vanilla-glue": {
+ "version": "0.1.0-alpha-2",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-2.tgz",
+ "integrity": "sha512-cQOg3EIhdjk1xoZtjD9SVPwQAnd49FCvHKchwFZZuhdNTeFs7SUHynOCekuGow2Ip0RJZuMZGcRxvWMgd0ogng==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "http://mcaptcha.org/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/mcaptcha"
+ },
+ {
+ "type": "individual",
+ "url": "http://batsense.net/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/realaravinth"
+ }
+ ],
+ "dependencies": {
+ "@mcaptcha/core-glue": "^0.1.0-alpha-3"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -14144,6 +14194,19 @@
"dev": true,
"requires": {}
},
+ "@mcaptcha/core-glue": {
+ "version": "0.1.0-alpha-3",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-3.tgz",
+ "integrity": "sha512-avphBVgf3PPDWuUoDsB2qiXAss2pc00lUILswJaMQofr8FQyflzkhha8H2Z+qGFiX0Iib/yyP2TOtBDbHqE9Tg=="
+ },
+ "@mcaptcha/vanilla-glue": {
+ "version": "0.1.0-alpha-2",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-2.tgz",
+ "integrity": "sha512-cQOg3EIhdjk1xoZtjD9SVPwQAnd49FCvHKchwFZZuhdNTeFs7SUHynOCekuGow2Ip0RJZuMZGcRxvWMgd0ogng==",
+ "requires": {
+ "@mcaptcha/core-glue": "^0.1.0-alpha-3"
+ }
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
diff --git a/package.json b/package.json
index a3e13eb609..42cba24f85 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
},
"dependencies": {
"@claviska/jquery-minicolors": "2.3.6",
+ "@mcaptcha/vanilla-glue": "0.1.0-alpha-2",
"@primer/octicons": "17.4.0",
"add-asset-webpack-plugin": "2.0.1",
"css-loader": "6.7.1",
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 610e4d2904..8a4c12d57b 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/hcaptcha"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/mcaptcha"
"code.gitea.io/gitea/modules/password"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/session"
@@ -414,6 +415,8 @@ func SignUp(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["PageIsSignUp"] = true
// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
@@ -435,6 +438,8 @@ func SignUpPost(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["PageIsSignUp"] = true
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
@@ -458,6 +463,8 @@ func SignUpPost(ctx *context.Context) {
valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse)
case setting.HCaptcha:
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse)
+ case setting.MCaptcha:
+ valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse)
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
return
diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go
index a2d76e9c5a..4f3f2062b6 100644
--- a/routers/web/auth/linkaccount.go
+++ b/routers/web/auth/linkaccount.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/hcaptcha"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/mcaptcha"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
@@ -40,6 +41,8 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["ShowRegistrationButton"] = false
@@ -96,6 +99,8 @@ func LinkAccountPostSignIn(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false
@@ -195,6 +200,8 @@ func LinkAccountPostRegister(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false
@@ -233,6 +240,8 @@ func LinkAccountPostRegister(ctx *context.Context) {
valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse)
case setting.HCaptcha:
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse)
+ case setting.MCaptcha:
+ valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse)
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
return
diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go
index 32ae91da47..3b1065189d 100644
--- a/routers/web/auth/openid.go
+++ b/routers/web/auth/openid.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/hcaptcha"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/mcaptcha"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
@@ -341,6 +342,8 @@ func RegisterOpenID(ctx *context.Context) {
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["OpenID"] = oid
userName, _ := ctx.Session.Get("openid_determined_username").(string)
if userName != "" {
@@ -372,6 +375,8 @@ func RegisterOpenIDPost(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["OpenID"] = oid
if setting.Service.AllowOnlyInternalRegistration {
@@ -397,6 +402,12 @@ func RegisterOpenIDPost(ctx *context.Context) {
return
}
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse)
+ case setting.MCaptcha:
+ if err := ctx.Req.ParseForm(); err != nil {
+ ctx.ServerError("", err)
+ return
+ }
+ valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse)
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
return
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index c8f2b02d8c..8ce1d85c57 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -96,6 +96,7 @@ type RegisterForm struct {
Retype string
GRecaptchaResponse string `form:"g-recaptcha-response"`
HcaptchaResponse string `form:"h-captcha-response"`
+ McaptchaResponse string `form:"m-captcha-response"`
}
// Validate validates the fields
diff --git a/services/forms/user_form_auth_openid.go b/services/forms/user_form_auth_openid.go
index fd3368d303..992517f34f 100644
--- a/services/forms/user_form_auth_openid.go
+++ b/services/forms/user_form_auth_openid.go
@@ -31,6 +31,7 @@ type SignUpOpenIDForm struct {
Email string `binding:"Required;Email;MaxSize(254)"`
GRecaptchaResponse string `form:"g-recaptcha-response"`
HcaptchaResponse string `form:"h-captcha-response"`
+ McaptchaResponse string `form:"m-captcha-response"`
}
// Validate validates the fields
diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl
index 356c3e1cdc..58380f57d8 100644
--- a/templates/user/auth/signup_inner.tmpl
+++ b/templates/user/auth/signup_inner.tmpl
@@ -54,6 +54,14 @@
<div class="h-captcha" data-sitekey="{{ .HcaptchaSitekey }}"></div>
</div>
{{end}}
+ {{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}}
+ <div class="inline field df ac db-small">
+ <span>{{.locale.Tr "captcha"}}</span>
+ <div class="border-secondary w-100-small" id="mcaptcha__widget-container" style="width: 50%; height: 5em"></div>
+ <div class="m-captcha" data-sitekey="{{ .McaptchaSitekey }}" data-instance-url="{{ .McaptchaURL }}"></div>
+ </div>
+ {{end}}
+
<div class="inline field">
<label></label>
diff --git a/templates/user/auth/signup_openid_register.tmpl b/templates/user/auth/signup_openid_register.tmpl
index 5edd7966ff..9fe0a9de1b 100644
--- a/templates/user/auth/signup_openid_register.tmpl
+++ b/templates/user/auth/signup_openid_register.tmpl
@@ -40,6 +40,11 @@
<div class="h-captcha" data-sitekey="{{ .HcaptchaSitekey }}"></div>
</div>
{{end}}
+ {{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}}
+ <div class="inline field required">
+ <div class="m-captcha" data-sitekey="{{ .McaptchaSitekey }}" data-instance-url="{{ .McaptchaURL }}"></div>
+ </div>
+ {{end}}
<div class="inline field">
<label for="openid">OpenID URI</label>
<input id="openid" value="{{ .OpenID }}" readonly>
diff --git a/web_src/js/features/mcaptcha.js b/web_src/js/features/mcaptcha.js
new file mode 100644
index 0000000000..725e2e28ac
--- /dev/null
+++ b/web_src/js/features/mcaptcha.js
@@ -0,0 +1,16 @@
+export async function initMcaptcha() {
+ const mCaptchaEl = document.querySelector('.m-captcha');
+ if (!mCaptchaEl) return;
+
+ const {default: mCaptcha} = await import(/* webpackChunkName: "mcaptcha-vanilla-glue" */'@mcaptcha/vanilla-glue');
+ mCaptcha.INPUT_NAME = 'm-captcha-response';
+ const siteKey = mCaptchaEl.getAttribute('data-sitekey');
+ const instanceURL = mCaptchaEl.getAttribute('data-instance-url');
+
+ mCaptcha.default({
+ siteKey: {
+ instanceUrl: new URL(instanceURL),
+ key: siteKey,
+ }
+ });
+}
diff --git a/web_src/js/index.js b/web_src/js/index.js
index b96e79c3c8..bd56fff773 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -86,6 +86,7 @@ import {initCommonOrganization} from './features/common-organization.js';
import {initRepoWikiForm} from './features/repo-wiki.js';
import {initRepoCommentForm, initRepository} from './features/repo-legacy.js';
import {initFormattingReplacements} from './features/formatting.js';
+import {initMcaptcha} from './features/mcaptcha.js';
// Run time-critical code as soon as possible. This is safe to do because this
// script appears at the end of <body> and rendered HTML is accessible at that point.
@@ -182,6 +183,7 @@ $(document).ready(() => {
initRepository();
initCommitStatuses();
+ initMcaptcha();
initUserAuthLinkAccountView();
initUserAuthOauth2();
diff --git a/web_src/less/_form.less b/web_src/less/_form.less
index eeab07c475..c958763216 100644
--- a/web_src/less/_form.less
+++ b/web_src/less/_form.less
@@ -156,7 +156,8 @@ textarea:focus,
padding-left: @create-page-form-input-padding+30px;
}
- .inline.field > label {
+ .inline.field > label,
+ .inline.field > span {
text-align: right;
width: @create-page-form-input-padding;
word-wrap: break-word;
diff --git a/web_src/less/helpers.less b/web_src/less/helpers.less
index cacf7d9c8e..5510ee5b23 100644
--- a/web_src/less/helpers.less
+++ b/web_src/less/helpers.less
@@ -168,3 +168,8 @@
.py-3 { padding-top: .5rem !important; padding-bottom: .5rem !important; }
.py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
.py-5 { padding-top: 2rem !important; padding-bottom: 2rem !important; }
+
+@media @mediaSm {
+ .db-small { display: block !important; }
+ .w-100-small { width: 100% !important; }
+}