From db66e0b8966dbe6c43ff9db6dbefb8bad5e176df Mon Sep 17 00:00:00 2001 From: Mohamed EZ-ZARGHILI Date: Sat, 23 Dec 2017 17:48:22 +0000 Subject: [PATCH] First release --- README.md | 56 ++++++++++++++++++++++++++++++++++++- recaptcha.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 recaptcha.go diff --git a/README.md b/README.md index 78bf4db..aec97d0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,56 @@ # recaptcha-go -Google recaptcha implementation in golang + +Google reCAPTCHA v2 form submittion in golang + +## Usage + +Install the package in your environment + +```bash +go get github.com/ezzarghili/recaptcha-go +``` + +To use it within your own code + +```go +import "github.com/ezzarghili/recaptcha-go" +func main(){ + recaptcha.Init (recaptchaSecret) // get your secret from https://www.google.com/recaptcha/admin +} +``` + +Now everytime you need to verify a client request use + +```go +success, err :=recaptcha.Verify(recaptchaResponse, ClientRemoteIP) +if err !=nil { + // do something with err (log?) +} +// proceed with success (true|false) +``` + +or + +```go +recaptcha.VerifyNoRemoteIP(recaptchaResponse) +if err !=nil { + // do something with err (log?) +} +// proceed with success (true|false) +``` + +while `recaptchaResponse` is the form value with name `g-recaptcha-response` sent back by recaptcha server and set for in the form when user answers the challenge + +Both `recaptcha.Verify` and `recaptcha.VerifyNoRemoteIP` return a `bool` and `error` values `(bool, error)` + +Use the `error` to check for issues with the secret and connection in the server, and use the `bool` value to verify if the client answered the challenge correctly + +### Issues with this library + +If you have some problems with using this library, bug reports or enhancement please open an issue in the issues tracker. + +### License + +Let's go with somehting permitive should we ? + +[MIT](https://choosealicense.com/licenses/mit/) \ No newline at end of file diff --git a/recaptcha.go b/recaptcha.go new file mode 100644 index 0000000..09b27be --- /dev/null +++ b/recaptcha.go @@ -0,0 +1,79 @@ +package recaptcha + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" +) + +var recaptchaSecret string + +const reCAPTCHALink = "https://www.google.com/recaptcha/api/siteverify" + +type reCHAPTCHRequest struct { + Secret string `json:"secret"` + Response string `json:"response"` + RemoteIP string `json:"remoteip,omitempty"` +} + +type reCHAPTCHResponse struct { + Success bool `json:"success"` + ChallengeTS time.Time `json:"challenge_ts"` + Hostname string `json:"hostname"` + ErrorCodes []string `json:"error-codes,omitempty"` +} + +// Init initialize with the reCAPTCHA secret optained from https://www.google.com/recaptcha/admin +func Init(ReCAPTCHASecret string) { + recaptchaSecret = ReCAPTCHASecret +} + +// Verify returns true if the client answered the challenge correctly and have correct remoteIP +func Verify(challenResponse string, remoteIP string) (bool, error) { + body := reCHAPTCHRequest{Secret: recaptchaSecret, Response: challenResponse, RemoteIP: remoteIP} + return confirm(body) +} + +// VerifyNoRemoteIP returns true if the client answered the challenge correctly +func VerifyNoRemoteIP(challenResponse string) (bool, error) { + body := reCHAPTCHRequest{Secret: recaptchaSecret, Response: challenResponse} + return confirm(body) +} + +func confirm(recaptach reCHAPTCHRequest) (Ok bool, Err error) { + if recaptach.Secret == "" { + Err = fmt.Errorf("recaptcha secret has not been set, please set recaptcha.Init(secret) before calling verification functions") + return false, Err + } + // Go http client does not set a default timeout for request, so we need + // to set one for worse cases when the server hang, we need to make this available in the API + // to make it possible this library's users to change it, for now a 10s timeout seems resonable + netClient := &http.Client{ + Timeout: 10 * time.Second, + } + jsonValue, _ := json.Marshal(recaptach) + response, err := netClient.Post( + reCAPTCHALink, "application/json", + bytes.NewBuffer(jsonValue), + ) + if err != nil { + Err = fmt.Errorf("error posting to recaptcha endpoint: %s", err) + return false, Err + } + defer response.Body.Close() + resultBody, err := ioutil.ReadAll(response.Body) + if err != nil { + Err = fmt.Errorf("couldn't read response body: %s", err) + return false, Err + } + var result reCHAPTCHResponse + err = json.Unmarshal(resultBody, &result) + if err != nil { + Err = fmt.Errorf("invalid response body json: %s", err) + return false, Err + } + return result.Success, nil +}