Skip to content

Commit

Permalink
🔒 Add rate limiter on email signin endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Jun 20, 2023
1 parent 3b52363 commit 7c2e574
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 13 deletions.
2 changes: 2 additions & 0 deletions apps/builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"@uiw/codemirror-theme-github": "^4.20.2",
"@uiw/codemirror-theme-tokyo-night": "^4.20.2",
"@uiw/react-codemirror": "^4.20.2",
"@upstash/ratelimit": "^0.4.3",
"@upstash/redis": "^1.21.0",
"@use-gesture/react": "^10.2.27",
"aws-sdk": "2.1384.0",
"browser-image-compression": "2.0.2",
Expand Down
25 changes: 16 additions & 9 deletions apps/builder/src/features/auth/components/SignInForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,24 @@ export const SignInForm = ({
e.preventDefault()
if (isMagicLinkSent) return
setAuthLoading(true)
const response = await signIn('email', {
email: emailValue,
redirect: false,
})
if (response?.error) {
try {
const response = await signIn('email', {
email: emailValue,
redirect: false,
})
if (response?.error) {
showToast({
title: scopedT('signinErrorToast.title'),
description: scopedT('signinErrorToast.description'),
})
} else {
setIsMagicLinkSent(true)
}
} catch {
showToast({
title: scopedT('signinErrorToast.title'),
description: scopedT('signinErrorToast.description'),
status: 'info',
description: scopedT('signinErrorToast.tooManyRequests'),
})
} else {
setIsMagicLinkSent(true)
}
setAuthLoading(false)
}
Expand Down
8 changes: 4 additions & 4 deletions apps/builder/src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ export default {
'auth.error.unknown': 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.',
'auth.signinErrorToast.title': 'Nicht autorisiert',
'auth.signinErrorToast.description': 'Anmeldungen sind deaktiviert.',
'auth.signinErrorToast.tooManyRequests':
'Zu viele Anfragen. Versuche es später erneut.',
'auth.noProvider.preLink': 'Du musst',
'auth.noProvider.link':
'mindestens einen Authentifizierungsanbieter konfigurieren (E-Mail, Google, GitHub, Facebook oder Azure AD).',
Expand All @@ -111,12 +113,10 @@ export default {
'billing.upgradeLimitLabel':
'Um {type} hinzuzufügen, musst du deinen Tarif aktualisieren',
'billing.currentSubscription.heading': 'Abonnement',
'billing.currentSubscription.subheading':
'Aktuelles Workspace-Abonnement:',
'billing.currentSubscription.subheading': 'Aktuelles Workspace-Abonnement:',
'billing.currentSubscription.cancelLink': 'Mein Abonnement kündigen',
'billing.invoices.heading': 'Rechnungen',
'billing.invoices.empty':
'Keine Rechnungen für diesen Workspace gefunden.',
'billing.invoices.empty': 'Keine Rechnungen für diesen Workspace gefunden.',
'billing.invoices.paidAt': 'Bezahlt am',
'billing.invoices.subtotal': 'Zwischensumme',
'billing.preCheckoutModal.companyInput.label': 'Firmenname:',
Expand Down
2 changes: 2 additions & 0 deletions apps/builder/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export default {
'auth.error.unknown': 'An error occurred. Please try again.',
'auth.signinErrorToast.title': 'Unauthorized',
'auth.signinErrorToast.description': 'Sign ups are disabled.',
'auth.signinErrorToast.tooManyRequests':
'Too many requests. Try again later.',
'auth.noProvider.preLink': 'You need to',
'auth.noProvider.link':
'configure at least one auth provider (Email, Google, GitHub, Facebook or Azure AD).',
Expand Down
1 change: 1 addition & 0 deletions apps/builder/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default {
'auth.error.unknown': 'Une erreur est survenue. Essaye à nouveau.',
'auth.signinErrorToast.title': 'Non autorisé',
'auth.signinErrorToast.description': 'Les inscriptions sont désactivées.',
'auth.signinErrorToast.tooManyRequests': 'Trop de tentatives de connexion.',
'auth.noProvider.preLink': 'Tu as besoin de',
'auth.noProvider.link':
"configurer au moins un fournisseur d'authentification (E-mail, Google, GitHub, Facebook ou Azure AD).",
Expand Down
2 changes: 2 additions & 0 deletions apps/builder/src/locales/pt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ export default {
'auth.error.unknown': 'Ocorreu um erro. Tente novamente.',
'auth.signinErrorToast.title': 'Não autorizado',
'auth.signinErrorToast.description': 'As inscrições estão desativadas.',
'auth.signinErrorToast.tooManyRequests':
'Muitas tentativas. Tente novamente mais tarde.',
'auth.noProvider.preLink': 'Você precisa',
'auth.noProvider.link':
'configurar pelo menos um provedor de autenticação (E-mail, Google, GitHub, Facebook ou Azure AD).',
Expand Down
32 changes: 32 additions & 0 deletions apps/builder/src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,23 @@ import { env, getAtPath, isDefined, isNotEmpty } from '@typebot.io/lib'
import { mockedUser } from '@/features/auth/mockedUser'
import { getNewUserInvitations } from '@/features/auth/helpers/getNewUserInvitations'
import { sendVerificationRequest } from '@/features/auth/helpers/sendVerificationRequest'
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis/nodejs'

const providers: Provider[] = []

let rateLimit: Ratelimit | undefined

if (
process.env.UPSTASH_REDIS_REST_URL &&
process.env.UPSTASH_REDIS_REST_TOKEN
) {
rateLimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(1, '60 s'),
})
}

if (
isNotEmpty(process.env.GITHUB_CLIENT_ID) &&
isNotEmpty(process.env.GITHUB_CLIENT_SECRET)
Expand Down Expand Up @@ -174,6 +188,24 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (isMockingSession) return res.send({ user: mockedUser })
const requestIsFromCompanyFirewall = req.method === 'HEAD'
if (requestIsFromCompanyFirewall) return res.status(200).end()

if (
rateLimit &&
req.url === '/api/auth/signin/email' &&
req.method === 'POST'
) {
let ip = req.headers['x-real-ip'] as string | undefined
if (!ip) {
const forwardedFor = req.headers['x-forwarded-for']
if (Array.isArray(forwardedFor)) {
ip = forwardedFor.at(0)
} else {
ip = forwardedFor?.split(',').at(0) ?? 'Unknown'
}
}
const { success } = await rateLimit.limit(ip as string)
if (!success) return res.status(429).json({ error: 'Too many requests' })
}
return await NextAuth(req, res, authOptions)
}

Expand Down
44 changes: 44 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 comments on commit 7c2e574

@vercel
Copy link

@vercel vercel bot commented on 7c2e574 Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

app.typebot.io
builder-v2-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 7c2e574 Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 7c2e574 Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs.typebot.io
docs-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 7c2e574 Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

apo.nigerias.io
app.blogely.com
apr.nigerias.io
aso.nigerias.io
blackcan.cr8.ai
blackvip.online
bot.4display.nl
bot.artiweb.app
bot.devitus.com
bot.jesopizz.it
bot.reeplai.com
bot.renovato.it
bot.scayver.com
bot.tc-mail.com
carspecs.lam.ee
chat.lalmon.com
chat.sureb4.com
eventhub.com.au
fitness.riku.ai
games.klujo.com
proscale.com.br
sakuranembro.it
sellmycarbr.com
typebot.aloe.do
bot.contakit.com
bot.piccinato.co
bot.sv-energy.it
botc.ceox.com.br
clo.closeer.work
cockroach.cr8.ai
desafioem21d.com
faqs.nigerias.io
form.syncwin.com
go.chatbotcv.com
haymanevents.com
kw.wpwakanda.com
myrentalhost.com
stan.vselise.com
start.taxtree.io
typebot.aloe.bot
voicehelp.cr8.ai
zap.fundviser.in
app.bouclidom.com
app.chatforms.net
bot.aldoemaria.it
bot.chatbotcv.com
positivobra.com.br
rollingball.cr8.ai
sub.yolozeeeer.com
survey.digienge.io
zap.techadviser.in
ai.digitaldaftar.in
bot.boston-voip.com
bot.cabinpromos.com
bot.carnaval.studio
bot.digitalbled.com
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
open.campus.aalen.university
piazzatorre.barrettamario.it
poll.mosaicohairboutique.com
type.cookieacademyonline.com
upload.atlasoutfittersk9.com
bot.brigadeirosemdrama.com.br
tuttirecepcao.fratucci.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
recepcao.tutti.fratucci.com.br
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
baleia.testeeventos.progenbr.com
bot.cabin-rentals-of-georgia.net
chat.portaloficialautorizado.com
open.campus.bot.aalen.university
sondaggio.mosaicohairboutique.it
baleia.testegabinete.progenbr.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
sondaggio.mosaicohairboutique.com
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
gabinete.baleia.formulario.progenbr.com
chrome-os-inquiry-system.itschromeos.com
viewer-v2-git-main-typebot-io.vercel.app
main-menu-for-itschromeos.itschromeos.com

Please sign in to comment.