-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1071 from bndrmrtn/main
feat: HCaptcha Middleware
- Loading branch information
Showing
12 changed files
with
415 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name-template: 'HCaptcha - v$RESOLVED_VERSION' | ||
tag-template: 'hcaptcha/v$RESOLVED_VERSION' | ||
tag-prefix: hcaptcha/v | ||
include-paths: | ||
- hcaptcha | ||
categories: | ||
- title: '❗ Breaking Changes' | ||
labels: | ||
- '❗ BreakingChange' | ||
- title: '🚀 New' | ||
labels: | ||
- '✏️ Feature' | ||
- title: '🧹 Updates' | ||
labels: | ||
- '🧹 Updates' | ||
- '🤖 Dependencies' | ||
- title: '🐛 Fixes' | ||
labels: | ||
- '☢️ Bug' | ||
- title: '📚 Documentation' | ||
labels: | ||
- '📒 Documentation' | ||
change-template: '- $TITLE (#$NUMBER)' | ||
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. | ||
exclude-contributors: | ||
- dependabot | ||
- dependabot[bot] | ||
version-resolver: | ||
major: | ||
labels: | ||
- 'major' | ||
- '❗ BreakingChange' | ||
minor: | ||
labels: | ||
- 'minor' | ||
- '✏️ Feature' | ||
patch: | ||
labels: | ||
- 'patch' | ||
- '📒 Documentation' | ||
- '☢️ Bug' | ||
- '🤖 Dependencies' | ||
- '🧹 Updates' | ||
default: patch | ||
template: | | ||
$CHANGES | ||
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...hcaptcha/v$RESOLVED_VERSION | ||
Thank you $CONTRIBUTORS for making this update possible. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
name: Release Drafter HCaptcha | ||
on: | ||
push: | ||
# branches to consider in the event; optional, defaults to all | ||
branches: | ||
- master | ||
- main | ||
paths: | ||
- 'hcaptcha/**' | ||
jobs: | ||
draft_release_jwt: | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 30 | ||
steps: | ||
- uses: release-drafter/release-drafter@v6 | ||
with: | ||
config-name: release-drafter-hcaptcha.yml | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
name: "Test hcaptcha" | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
- main | ||
paths: | ||
- 'hcaptcha/**' | ||
pull_request: | ||
paths: | ||
- 'hcaptcha/**' | ||
|
||
jobs: | ||
Tests: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
go-version: | ||
- 1.21.x | ||
- 1.22.x | ||
steps: | ||
- name: Fetch Repository | ||
uses: actions/checkout@v4 | ||
- name: Install Go | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: '${{ matrix.go-version }}' | ||
- name: Run Test | ||
working-directory: ./hcaptcha | ||
run: go test -v -race ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,5 @@ | |
*.workspace | ||
|
||
# Dependencies | ||
/vendor/ | ||
vendor/ | ||
vendor | ||
/Godeps/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
--- | ||
id: hcaptcha | ||
--- | ||
|
||
# HCaptcha | ||
|
||
![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=hcaptcha*) | ||
[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) | ||
![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) | ||
![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) | ||
![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) | ||
|
||
A simple [HCaptcha](https://hcaptcha.com) middleware to prevent bot attacks. | ||
|
||
:::note | ||
|
||
Requires Go **1.21** and above | ||
|
||
::: | ||
|
||
## Install | ||
|
||
:::caution | ||
|
||
This middleware only supports Fiber **v3**. | ||
|
||
::: | ||
|
||
```shell | ||
go get -u github.com/gofiber/fiber/v3 | ||
go get -u github.com/gofiber/contrib/hcaptcha | ||
``` | ||
|
||
## Signature | ||
|
||
```go | ||
hcaptcha.New(config hcaptcha.Config) fiber.Handler | ||
``` | ||
|
||
## Config | ||
|
||
| Property | Type | Description | Default | | ||
|:----------------|:----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------| | ||
| SecretKey | `string` | The secret key you obtained from the HCaptcha admin panel. This field must not be empty. | `""` | | ||
| ResponseKeyFunc | `func(fiber.Ctx) (string, error)` | ResponseKeyFunc should return the token that captcha provides upon successful solving. By default, it gets the token from the body by parsing a JSON request and returns the `hcaptcha_token` field. | `hcaptcha.DefaultResponseKeyFunc` | | ||
| SiteVerifyURL | `string` | This property specifies the API resource used for token authentication. | `https://api.hcaptcha.com/siteverify` | | ||
|
||
## Example | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/gofiber/contrib/hcaptcha" | ||
"github.com/gofiber/fiber/v3" | ||
"log" | ||
) | ||
|
||
const ( | ||
TestSecretKey = "0x0000000000000000000000000000000000000000" | ||
TestSiteKey = "20000000-ffff-ffff-ffff-000000000002" | ||
) | ||
|
||
func main() { | ||
app := fiber.New() | ||
captcha := hcaptcha.New(hcaptcha.Config{ | ||
// Must set the secret key | ||
SecretKey: TestSecretKey, | ||
}) | ||
|
||
app.Get("/api/", func(c fiber.Ctx) error { | ||
return c.JSON(fiber.Map{ | ||
"hcaptcha_site_key": TestSiteKey, | ||
}) | ||
}) | ||
|
||
app.Post("/api/robots-excluded", func(c fiber.Ctx) error { | ||
return c.SendString("You are not a robot") | ||
}, captcha) | ||
|
||
log.Fatal(app.Listen(":3000")) | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package hcaptcha | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/gofiber/fiber/v3" | ||
) | ||
|
||
// DefaultSiteVerifyURL is the default URL for the HCaptcha API | ||
const DefaultSiteVerifyURL = "https://api.hcaptcha.com/siteverify" | ||
|
||
// Config defines the config for HCaptcha middleware. | ||
type Config struct { | ||
// SecretKey is the secret key you get from HCaptcha when you create a new application | ||
SecretKey string | ||
// ResponseKeyFunc should return the generated pass UUID from the ctx, which will be validated | ||
ResponseKeyFunc func(fiber.Ctx) (string, error) | ||
// SiteVerifyURL is the endpoint URL where the program should verify the given token | ||
// default value is: "https://api.hcaptcha.com/siteverify" | ||
SiteVerifyURL string | ||
} | ||
|
||
// DefaultResponseKeyFunc is the default function to get the HCaptcha token from the request body | ||
func DefaultResponseKeyFunc(c fiber.Ctx) (string, error) { | ||
data := struct { | ||
HCaptchaToken string `json:"hcaptcha_token"` | ||
}{} | ||
|
||
err := json.NewDecoder(bytes.NewReader(c.Body())).Decode(&data) | ||
|
||
if err != nil { | ||
return "", fmt.Errorf("failed to decode HCaptcha token: %w", err) | ||
} | ||
|
||
return data.HCaptchaToken, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
module github.com/gofiber/contrib/hcaptcha | ||
|
||
go 1.21 | ||
|
||
require ( | ||
github.com/gofiber/fiber/v3 v3.0.0-beta.2 | ||
github.com/stretchr/testify v1.9.0 | ||
github.com/valyala/fasthttp v1.52.0 | ||
) | ||
|
||
require ( | ||
github.com/andybalholm/brotli v1.1.0 // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect | ||
github.com/google/uuid v1.6.0 // indirect | ||
github.com/klauspost/compress v1.17.6 // indirect | ||
github.com/mattn/go-colorable v0.1.13 // indirect | ||
github.com/mattn/go-isatty v0.0.20 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/valyala/bytebufferpool v1.0.0 // indirect | ||
github.com/valyala/tcplisten v1.0.0 // indirect | ||
golang.org/x/sys v0.17.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= | ||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao= | ||
github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM= | ||
github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= | ||
github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= | ||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= | ||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= | ||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | ||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= | ||
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= | ||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= | ||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= | ||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= | ||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Package hcaptcha is a simple middleware that checks for an HCaptcha UUID | ||
// and then validates it. It returns an error if the UUID is not valid (the request may have been sent by a robot). | ||
package hcaptcha | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"github.com/gofiber/fiber/v3" | ||
"github.com/valyala/fasthttp" | ||
"net/url" | ||
) | ||
|
||
// HCaptcha is a middleware handler that checks for an HCaptcha UUID and then validates it. | ||
type HCaptcha struct { | ||
Config | ||
} | ||
|
||
// New creates a new HCaptcha middleware handler. | ||
func New(config Config) fiber.Handler { | ||
if config.SiteVerifyURL == "" { | ||
config.SiteVerifyURL = DefaultSiteVerifyURL | ||
} | ||
|
||
if config.ResponseKeyFunc == nil { | ||
config.ResponseKeyFunc = DefaultResponseKeyFunc | ||
} | ||
|
||
h := &HCaptcha{ | ||
config, | ||
} | ||
return h.Validate | ||
} | ||
|
||
// Validate checks for an HCaptcha UUID and then validates it. | ||
func (h *HCaptcha) Validate(c fiber.Ctx) error { | ||
token, err := h.ResponseKeyFunc(c) | ||
if err != nil { | ||
c.Status(fiber.StatusBadRequest) | ||
return fmt.Errorf("error retrieving HCaptcha token: %w", err) | ||
} | ||
|
||
req := fasthttp.AcquireRequest() | ||
defer fasthttp.ReleaseRequest(req) | ||
req.SetBody([]byte(url.Values{ | ||
"secret": {h.SecretKey}, | ||
"response": {token}, | ||
}.Encode())) | ||
req.Header.SetMethod("POST") | ||
req.Header.SetContentType("application/x-www-form-urlencoded; charset=UTF-8") | ||
req.Header.Set("Accept", "application/json") | ||
req.SetRequestURI(h.SiteVerifyURL) | ||
res := fasthttp.AcquireResponse() | ||
defer fasthttp.ReleaseResponse(res) | ||
|
||
// Send the request to the HCaptcha API | ||
if err = fasthttp.Do(req, res); err != nil { | ||
c.Status(fiber.StatusBadRequest) | ||
return fmt.Errorf("error sending request to HCaptcha API: %w", err) | ||
} | ||
|
||
o := struct { | ||
Success bool `json:"success"` | ||
}{} | ||
|
||
if err = json.NewDecoder(bytes.NewReader(res.Body())).Decode(&o); err != nil { | ||
c.Status(fiber.StatusInternalServerError) | ||
return fmt.Errorf("error decoding HCaptcha API response: %w", err) | ||
} | ||
|
||
if !o.Success { | ||
c.Status(fiber.StatusForbidden) | ||
return errors.New("unable to check that you are not a robot") | ||
} | ||
|
||
return c.Next() | ||
} |
Oops, something went wrong.