Permalink
Browse files

Implement reCAPTCHA for abuse prevention (#311)

* Implement reCAPTCHA for abuse prevention

* Update the README
  • Loading branch information...
chadwhitacre authored and rauchg committed Jun 1, 2017
1 parent 3ff77b9 commit 1dedb2e5dce7ef80ffa4fdbf3b2481d09f0b7aa7
Showing with 105 additions and 21 deletions.
  1. +1 −1 Dockerfile
  2. +1 −1 Procfile
  3. +9 −1 README.md
  4. +8 −0 app.json
  5. +9 −2 bin/slackin
  6. +4 −2 lib/assets/client.js
  7. +70 −13 lib/index.js
  8. +3 −1 lib/splash.js
@@ -10,4 +10,4 @@ RUN npm install --unsafe-perm
EXPOSE 3000
CMD ./bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN
CMD ./bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN $GOOGLE_CAPTCHA_SECRET $GOOGLE_CAPTCHA_SITEKEY
@@ -1 +1 @@
web: bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN
web: bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN $GOOGLE_CAPTCHA_SECRET $GOOGLE_CAPTCHA_SITEKEY
@@ -5,6 +5,7 @@
- A landing page you can point users to fill in their emails and receive an invite (`https://slack.yourdomain.com`)
- An `<iframe>` badge to embed on any website that shows connected users in *realtime* with socket.io.
- A SVG badge that works well from static mediums (like GitHub README pages)
- Abuse prevention via [Google reCAPTCHA](https://www.google.com/recaptcha/intro/)
Check out the [Demo](https://slackin.now.sh/) or read more about the [motivations and history](http://rauchg.com/slackin) behind Slackin.
@@ -13,7 +14,11 @@ Check out the [Demo](https://slackin.now.sh/) or read more about the [motivation
Set up [Now](https://zeit.co/now) on your device and run this command:
```bash
$ now -e SLACK_API_TOKEN="<token>" -e SLACK_SUBDOMAIN="<team-name>" now-examples/slackin
$ now -e SLACK_API_TOKEN="<token>" \
-e SLACK_SUBDOMAIN="<team-name>" \
-e GOOGLE_CAPTCHA_SECRET="<secret>" \
-e GOOGLE_CAPTCHA_SITEKEY="<sitekey>" \
now-examples/slackin
```
Other platforms:
@@ -35,6 +40,9 @@ times 5. If you are not getting invite emails, this might be the reason.
Workaround: sign up for a free org, and set up Slackin to point to it
(all channels will be visible).
Here is where to [generate a secret and
sitekey](https://www.google.com/recaptcha/admin) for Google reCAPTCHA.
### Badges
#### Realtime ([demo](https://cldup.com/IaiPnDEAA6.gif))
@@ -13,6 +13,14 @@
"description": "A Slack API token (find it on https://api.slack.com/web)",
"required": true
},
"GOOGLE_CAPTCHA_SECRET": {
"description": "Google captcha secret key",
"required": true
},
"GOOGLE_CAPTCHA_SITEKEY": {
"description": "Google captcha site key",
"required": true
},
"SLACK_COC": {
"description": "A URL to a Code of Conduct people must agree on before joining.",
"required": false
@@ -17,24 +17,31 @@ args
.option(['?', 'help'], 'Show the usage information')
var flags = args.parse(process.argv, {
value: '<team-id> <api-token>',
value: '<team-id> <api-token> <google-captcha-secret> <google-captcha-sitekey>',
help: false
})
var org = args.sub[0] || process.env.SLACK_SUBDOMAIN
var token = args.sub[1] || process.env.SLACK_API_TOKEN
var emails = process.env.EMAIL_SLACK_LIST || ''
var gcaptcha_secret = args.sub[2] || process.env.GOOGLE_CAPTCHA_SECRET
var gcaptcha_sitekey = args.sub[3] || process.env.GOOGLE_CAPTCHA_SITEKEY
if (flags.help) {
args.showHelp()
}
if (!org || !token) {
if (!org || !token || !gcaptcha_sitekey || !gcaptcha_secret) {
args.showHelp()
} else {
flags.org = org
flags.token = token
flags.emails = emails
flags.gcaptcha_secret = gcaptcha_secret
flags.gcaptcha_sitekey = gcaptcha_sitekey
}
var port = flags.port
@@ -19,7 +19,8 @@ body.addEventListener('submit', function (ev){
button.disabled = true
button.className = ''
button.innerHTML = 'Please Wait'
invite(channel ? channel.value : null, coc && coc.checked ? 1 : 0, email.value, function (err, msg){
var gcaptcha_response = form.elements['g-recaptcha-response']
invite(channel ? channel.value : null, coc && coc.checked ? 1 : 0, email.value, gcaptcha_response.value, function (err, msg){
if (err) {
button.removeAttribute('disabled')
button.className = 'error'
@@ -31,10 +32,11 @@ body.addEventListener('submit', function (ev){
})
})
function invite (channel, coc, email, fn){
function invite (channel, coc, email, gcaptcha_response_value, fn){
request
.post(data.path + 'invite')
.send({
"g-recaptcha-response": gcaptcha_response_value,
coc: coc,
channel: channel,
email: email
@@ -9,6 +9,7 @@ import { Server as http } from 'http'
import remail from 'email-regex'
import dom from 'vd'
import cors from 'cors'
import request from 'superagent';
// our code
import Slack from './slack'
@@ -22,6 +23,8 @@ export default function slackin ({
token,
interval = 5000, // jshint ignore:line
org,
gcaptcha_secret,
gcaptcha_sitekey,
css,
coc,
cors: useCors = false,
@@ -33,6 +36,8 @@ export default function slackin ({
// must haves
if (!token) throw new Error('Must provide a `token`.')
if (!org) throw new Error('Must provide an `org`.')
if (!gcaptcha_secret) throw new Error('Must provide a `gcaptcha_secret`.')
if (!gcaptcha_sitekey) throw new Error('Must provide an `gcaptcha_sitekey`.')
if (channels) {
// convert to an array
@@ -84,11 +89,12 @@ export default function slackin ({
dom('title',
'Join ', name, ' on Slack!'
),
dom("script src=https://www.google.com/recaptcha/api.js"),
dom('meta name=viewport content="width=device-width,initial-scale=1.0,minimum-scale=1.0,user-scalable=no"'),
dom('link rel="shortcut icon" href=https://slack.global.ssl.fastly.net/272a/img/icons/favicon-32.png'),
css && dom('link rel=stylesheet', { href: css })
),
splash({ coc, path, css, name, org, logo, channels, active, total })
splash({ coc, path, css, name, org, logo, channels, active, total, gcaptcha_sitekey})
)
res.type('html')
res.send(page.toHTML())
@@ -130,13 +136,20 @@ export default function slackin ({
}
let email = req.body.email
let captcha_response = req.body['g-recaptcha-response'];
if (!email) {
return res
.status(400)
.json({ msg: 'No email provided' })
}
if(captcha_response == undefined || !captcha_response.length){
return res
.status(400)
.send({ msg: 'Invalid captcha' });
}
if (!remail().test(email)) {
return res
.status(400)
@@ -156,23 +169,67 @@ export default function slackin ({
.json({ msg: 'Agreement to CoC is mandatory' })
}
invite({ token, org, email, channel: chanId }, err => {
if (err) {
if (err.message === `Sending you to Slack...`) {
return res
.status(303)
.json({ msg: err.message, redirectUrl: `https://${org}.slack.com` })
}
/////////////////////////////////////////////////////////////////////////
const captcha_data = {
secret: gcaptcha_secret,
response: captcha_response,
remoteip: req.connection.remoteAddress
}
const captcha_callback = (err, resp) => {
if (err) {
return res
.status(400)
.json({ msg: err.message })
.send({ msg: err });
}else{
if(resp.body.success){
let chanId = slack.channel ? slack.channel.id : null;
invite({ token, org, email, channel: chanId }, err => {
if (err) {
if (err.message === `Sending you to Slack...`) {
return res
.status(303)
.json({ msg: err.message, redirectUrl: `https://${org}.slack.com` })
}
return res
.status(400)
.json({ msg: err.message })
}
res
.status(200)
.json({ msg: 'WOOT. Check your email!' })
});
}else{
if (err) {
return res
.status(400)
.send({ msg: "Captcha check failed" });
}
}
}
res
.status(200)
.json({ msg: 'WOOT. Check your email!' })
})
}
request.post('https://www.google.com/recaptcha/api/siteverify')
.type('form')
.send(captcha_data)
.end(captcha_callback);
})
// iframe
@@ -1,6 +1,6 @@
import dom from 'vd'
export default function splash ({ path, name, org, coc, logo, active, total, channels, large, iframe }){
export default function splash ({ path, name, org, coc, logo, active, total, channels, large, iframe, gcaptcha_sitekey }){
let div = dom('.splash',
!iframe && dom('.logos',
logo && dom('.logo.org'),
@@ -34,6 +34,8 @@ export default function splash ({ path, name, org, coc, logo, active, total, cha
),
dom('input.form-item type=email name=email placeholder=you@yourdomain.com '
+ (!iframe ? 'autofocus' : '')),
dom('br'),
dom(`div class="g-recaptcha" data-sitekey="${gcaptcha_sitekey}"`),
coc && dom('.coc',
dom('label',
dom('input type=checkbox name=coc value=1'),

0 comments on commit 1dedb2e

Please sign in to comment.