commit c1cc4c60f3e68fa7c64c05627583f79e6e95802e
parent a430e3f5f20a84fb3cbb3c67715fb9f906add0cf
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Wed, 14 Dec 2022 10:18:37 -0500
no longer use cookie for login with 2fa process
Diffstat:
8 files changed, 157 insertions(+), 178 deletions(-)
diff --git a/pkg/web/handlers/data.go b/pkg/web/handlers/data.go
@@ -37,10 +37,12 @@ type loginData struct {
}
type sessionsTwoFactorData struct {
+ Token string
Error string
}
type sessionsGpgTwoFactorData struct {
+ Token string
EncryptedMessage string
Code string
Error string
@@ -48,6 +50,7 @@ type sessionsGpgTwoFactorData struct {
}
type sessionsGpgSignTwoFactorData struct {
+ Token string
ToBeSignedMessage string
SignedMessage string
Error string
@@ -55,6 +58,7 @@ type sessionsGpgSignTwoFactorData struct {
}
type sessionsTwoFactorRecoveryData struct {
+ Token string
Error string
}
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -228,112 +228,117 @@ func LoginAttackHandler(c echo.Context) error {
}
func loginHandler(c echo.Context) error {
- var data loginData
- data.Autofocus = 0
- data.HomeUsersList = config.HomeUsersList.Load()
-
- if data.HomeUsersList {
- data.Online = managers.ActiveUsers.GetActiveUsers()
- }
+ formName := c.Request().PostFormValue("formName")
+ if formName == "" {
+ var data loginData
+ data.Autofocus = 0
+ data.HomeUsersList = config.HomeUsersList.Load()
- actualLogin := func(username, password string, captchaSolved bool) error {
- username = strings.TrimSpace(username)
- user, err := database.GetVerifiedUserByUsername(username)
- if err != nil {
- time.Sleep(utils.RandMs(50, 200))
- data.Error = "Invalid username/password"
- return c.Render(http.StatusOK, "login", data)
+ if data.HomeUsersList {
+ data.Online = managers.ActiveUsers.GetActiveUsers()
}
- user.LoginAttempts++
- user.DoSave()
-
- if user.LoginAttempts > 4 && !captchaSolved {
- data.CaptchaRequired = true
- data.Autofocus = 2
- data.Error = "Captcha required"
- data.CaptchaID, data.CaptchaImg = captcha.New()
- data.Password = password
- captchaID := c.Request().PostFormValue("captcha_id")
- captchaInput := c.Request().PostFormValue("captcha")
- if captchaInput == "" {
+ actualLogin := func(username, password string, captchaSolved bool) error {
+ username = strings.TrimSpace(username)
+ user, err := database.GetVerifiedUserByUsername(username)
+ if err != nil {
+ time.Sleep(utils.RandMs(50, 200))
+ data.Error = "Invalid username/password"
return c.Render(http.StatusOK, "login", data)
- } else {
- if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
- data.Error = "Invalid captcha"
+ }
+
+ user.LoginAttempts++
+ user.DoSave()
+
+ if user.LoginAttempts > 4 && !captchaSolved {
+ data.CaptchaRequired = true
+ data.Autofocus = 2
+ data.Error = "Captcha required"
+ data.CaptchaID, data.CaptchaImg = captcha.New()
+ data.Password = password
+ captchaID := c.Request().PostFormValue("captcha_id")
+ captchaInput := c.Request().PostFormValue("captcha")
+ if captchaInput == "" {
return c.Render(http.StatusOK, "login", data)
+ } else {
+ if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
+ data.Error = "Invalid captcha"
+ return c.Render(http.StatusOK, "login", data)
+ }
}
}
- }
- if !user.CheckPassword(password) {
- data.Password = ""
- data.Autofocus = 1
- data.Error = "Invalid username/password"
- return c.Render(http.StatusOK, "login", data)
- }
+ if !user.CheckPassword(password) {
+ data.Password = ""
+ data.Autofocus = 1
+ data.Error = "Invalid username/password"
+ return c.Render(http.StatusOK, "login", data)
+ }
- redir := c.QueryParam("redirect")
+ if user.GpgTwoFactorEnabled {
+ token := utils.GenerateToken32()
+ partialAuthCache.Set(token, user.ID, cache1.DefaultExpiration)
+ if user.GpgTwoFactorMode {
+ return SessionsGpgSignTwoFactorHandler(c, true, token)
+ }
+ return SessionsGpgTwoFactorHandler(c, true, token)
- if user.GpgTwoFactorEnabled {
- token := utils.GenerateToken32()
- partialAuthCache.Set(token, user.ID, cache1.DefaultExpiration)
- c.SetCookie(createPartialSessionCookie(token))
- redirectURL := "/sessions/gpg-two-factor"
- if user.GpgTwoFactorMode {
- redirectURL = "/sessions/gpg-sign-two-factor"
- }
- if redir != "" {
- redirectURL += "?redirect=" + redir
+ } else if string(user.TwoFactorSecret) != "" {
+ token := utils.GenerateToken32()
+ partialAuthCache.Set(token, user.ID, cache1.DefaultExpiration)
+ return SessionsTwoFactorHandler(c, true, token)
}
- return c.Redirect(http.StatusFound, redirectURL)
- } else if string(user.TwoFactorSecret) != "" {
- token := utils.GenerateToken32()
- partialAuthCache.Set(token, user.ID, cache1.DefaultExpiration)
- c.SetCookie(createPartialSessionCookie(token))
- redirectURL := "/sessions/two-factor"
- if redir != "" {
- redirectURL += "?redirect=" + redir
- }
- return c.Redirect(http.StatusFound, redirectURL)
+ return completeLogin(c, user)
}
- return completeLogin(c, user)
- }
-
- usernameQuery := c.QueryParam("u")
- passwordQuery := c.QueryParam("p")
- if usernameQuery == "darkforestAdmin" && passwordQuery != "" {
- return actualLogin(usernameQuery, passwordQuery, false)
- }
+ usernameQuery := c.QueryParam("u")
+ passwordQuery := c.QueryParam("p")
+ if usernameQuery == "darkforestAdmin" && passwordQuery != "" {
+ return actualLogin(usernameQuery, passwordQuery, false)
+ }
- if config.ForceLoginCaptcha.IsTrue() {
- data.CaptchaID, data.CaptchaImg = captcha.New()
- data.CaptchaRequired = true
- }
+ if config.ForceLoginCaptcha.IsTrue() {
+ data.CaptchaID, data.CaptchaImg = captcha.New()
+ data.CaptchaRequired = true
+ }
- if c.Request().Method == http.MethodGet {
- return c.Render(http.StatusOK, "login", data)
- }
+ if c.Request().Method == http.MethodGet {
+ return c.Render(http.StatusOK, "login", data)
+ }
- captchaSolved := false
+ captchaSolved := false
- data.Username = strings.TrimSpace(c.FormValue("username"))
- password := c.FormValue("password")
+ data.Username = strings.TrimSpace(c.FormValue("username"))
+ password := c.FormValue("password")
- if config.ForceLoginCaptcha.IsTrue() {
- data.CaptchaRequired = true
- captchaID := c.Request().PostFormValue("captcha_id")
- captchaInput := c.Request().PostFormValue("captcha")
- if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
- data.ErrCaptcha = err.Error()
- return c.Render(http.StatusOK, "login", data)
+ if config.ForceLoginCaptcha.IsTrue() {
+ data.CaptchaRequired = true
+ captchaID := c.Request().PostFormValue("captcha_id")
+ captchaInput := c.Request().PostFormValue("captcha")
+ if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
+ data.ErrCaptcha = err.Error()
+ return c.Render(http.StatusOK, "login", data)
+ }
+ captchaSolved = true
}
- captchaSolved = true
- }
- return actualLogin(data.Username, password, captchaSolved)
+ return actualLogin(data.Username, password, captchaSolved)
+
+ } else if formName == "pgp_2fa" {
+ token := c.Request().PostFormValue("token")
+ return SessionsGpgTwoFactorHandler(c, false, token)
+ } else if formName == "pgp_sign_2fa" {
+ token := c.Request().PostFormValue("token")
+ return SessionsGpgSignTwoFactorHandler(c, false, token)
+ } else if formName == "2fa" {
+ token := c.Request().PostFormValue("token")
+ return SessionsTwoFactorHandler(c, false, token)
+ } else if formName == "2fa_recovery" {
+ token := c.Request().PostFormValue("token")
+ return SessionsTwoFactorRecoveryHandler(c, token)
+ }
+ return c.Redirect(http.StatusOK, "/")
}
func completeLogin(c echo.Context, user database.User) error {
@@ -374,12 +379,8 @@ func LoginCompletedHandler(c echo.Context) error {
}
// SessionsGpgTwoFactorHandler ...
-func SessionsGpgTwoFactorHandler(c echo.Context) error {
- partialAuthCookie, err := c.Cookie(hutils.PartialAuthCookieName)
- if err != nil {
- return c.Redirect(http.StatusFound, "/")
- }
- userID, found := partialAuthCache.Get(partialAuthCookie.Value)
+func SessionsGpgTwoFactorHandler(c echo.Context, step1 bool, token string) error {
+ userID, found := partialAuthCache.Get(token)
if !found {
return c.Redirect(http.StatusFound, "/")
}
@@ -391,8 +392,9 @@ func SessionsGpgTwoFactorHandler(c echo.Context) error {
}
var data sessionsGpgTwoFactorData
+ data.Token = token
- if c.Request().Method == http.MethodGet {
+ if step1 {
msg, err := generatePgpEncryptedTokenMessage(user.ID, user.GPGPublicKey)
if err != nil {
data.Error = err.Error()
@@ -402,42 +404,31 @@ func SessionsGpgTwoFactorHandler(c echo.Context) error {
return c.Render(http.StatusOK, "sessions-gpg-two-factor", data)
}
- token, found := pgpTokenCache.Get(user.ID)
+ pgpToken, found := pgpTokenCache.Get(user.ID)
if !found {
return c.Redirect(http.StatusFound, "/")
}
data.EncryptedMessage = c.Request().PostFormValue("encrypted_message")
data.Code = c.Request().PostFormValue("pgp_code")
- if data.Code != token.Value {
+ if data.Code != pgpToken.Value {
data.ErrorCode = "invalid code"
return c.Render(http.StatusOK, "sessions-gpg-two-factor", data)
}
pgpTokenCache.Delete(user.ID)
- partialAuthCache.Delete(partialAuthCookie.Value)
- c.SetCookie(hutils.DeleteCookie(hutils.PartialAuthCookieName))
+ partialAuthCache.Delete(token)
if string(user.TwoFactorSecret) != "" {
token := utils.GenerateToken32()
partialAuthCache.Set(token, user.ID, cache1.DefaultExpiration)
- c.SetCookie(createPartialSessionCookie(token))
- redirectURL := "/sessions/two-factor"
- redir := c.QueryParam("redirect")
- if redir != "" {
- redirectURL += "?redirect=" + redir
- }
- return c.Redirect(http.StatusFound, redirectURL)
+ return SessionsTwoFactorHandler(c, true, token)
}
return completeLogin(c, user)
}
// SessionsGpgSignTwoFactorHandler ...
-func SessionsGpgSignTwoFactorHandler(c echo.Context) error {
- partialAuthCookie, err := c.Cookie(hutils.PartialAuthCookieName)
- if err != nil {
- return c.Redirect(http.StatusFound, "/")
- }
- userID, found := partialAuthCache.Get(partialAuthCookie.Value)
+func SessionsGpgSignTwoFactorHandler(c echo.Context, step1 bool, token string) error {
+ userID, found := partialAuthCache.Get(token)
if !found {
return c.Redirect(http.StatusFound, "/")
}
@@ -449,55 +440,46 @@ func SessionsGpgSignTwoFactorHandler(c echo.Context) error {
}
var data sessionsGpgSignTwoFactorData
+ data.Token = token
- if c.Request().Method == http.MethodGet {
+ if step1 {
data.ToBeSignedMessage = generatePgpToBeSignedTokenMessage(user.ID, user.GPGPublicKey)
return c.Render(http.StatusOK, "sessions-gpg-sign-two-factor", data)
}
- token, found := pgpTokenCache.Get(user.ID)
+ pgpToken, found := pgpTokenCache.Get(user.ID)
if !found {
return c.Redirect(http.StatusFound, "/")
}
data.ToBeSignedMessage = c.Request().PostFormValue("to_be_signed_message")
data.SignedMessage = c.Request().PostFormValue("signed_message")
- if !utils.PgpCheckSignMessage(token.PKey, token.Value, data.SignedMessage) {
+ if !utils.PgpCheckSignMessage(pgpToken.PKey, pgpToken.Value, data.SignedMessage) {
data.ErrorSignedMessage = "invalid signature"
return c.Render(http.StatusOK, "sessions-gpg-sign-two-factor", data)
}
pgpTokenCache.Delete(user.ID)
- partialAuthCache.Delete(partialAuthCookie.Value)
- c.SetCookie(hutils.DeleteCookie(hutils.PartialAuthCookieName))
+ partialAuthCache.Delete(token)
if string(user.TwoFactorSecret) != "" {
token := utils.GenerateToken32()
partialAuthCache.Set(token, user.ID, cache1.DefaultExpiration)
- c.SetCookie(createPartialSessionCookie(token))
- redirectURL := "/sessions/two-factor"
- redir := c.QueryParam("redirect")
- if redir != "" {
- redirectURL += "?redirect=" + redir
- }
- return c.Redirect(http.StatusFound, redirectURL)
+ return SessionsTwoFactorHandler(c, true, token)
}
return completeLogin(c, user)
}
// SessionsTwoFactorHandler ...
-func SessionsTwoFactorHandler(c echo.Context) error {
- partialAuthCookie, err := c.Cookie(hutils.PartialAuthCookieName)
- if err != nil {
- return c.Redirect(http.StatusFound, "/")
- }
- userID, found := partialAuthCache.Get(partialAuthCookie.Value)
+func SessionsTwoFactorHandler(c echo.Context, step1 bool, token string) error {
+ userID, found := partialAuthCache.Get(token)
if !found {
return c.Redirect(http.StatusFound, "/")
}
var data sessionsTwoFactorData
- if c.Request().Method == http.MethodPost {
+ data.Token = token
+ if !step1 {
code := c.Request().PostFormValue("code")
user, err := database.GetUserByID(userID)
if err != nil {
@@ -510,8 +492,7 @@ func SessionsTwoFactorHandler(c echo.Context) error {
return c.Render(http.StatusOK, "sessions-two-factor", data)
}
- partialAuthCache.Delete(partialAuthCookie.Value)
- c.SetCookie(hutils.DeleteCookie(hutils.PartialAuthCookieName))
+ partialAuthCache.Delete(token)
return completeLogin(c, user)
}
@@ -519,19 +500,16 @@ func SessionsTwoFactorHandler(c echo.Context) error {
}
// SessionsTwoFactorRecoveryHandler ...
-func SessionsTwoFactorRecoveryHandler(c echo.Context) error {
- partialAuthCookie, err := c.Cookie(hutils.PartialAuthCookieName)
- if err != nil {
- return c.Redirect(http.StatusFound, "/")
- }
- userID, found := partialAuthCache.Get(partialAuthCookie.Value)
+func SessionsTwoFactorRecoveryHandler(c echo.Context, token string) error {
+ userID, found := partialAuthCache.Get(token)
if !found {
return c.Redirect(http.StatusFound, "/")
}
var data sessionsTwoFactorRecoveryData
- if c.Request().Method == http.MethodPost {
- recoveryCode := c.Request().PostFormValue("code")
+ data.Token = token
+ recoveryCode := c.Request().PostFormValue("code")
+ if recoveryCode != "" {
user, err := database.GetUserByID(userID)
if err != nil {
logrus.Errorf("failed to get user %d", userID)
@@ -542,8 +520,7 @@ func SessionsTwoFactorRecoveryHandler(c echo.Context) error {
return c.Render(http.StatusOK, "sessions-two-factor-recovery", data)
}
- partialAuthCache.Delete(partialAuthCookie.Value)
- c.SetCookie(hutils.DeleteCookie(hutils.PartialAuthCookieName))
+ partialAuthCache.Delete(token)
return completeLogin(c, user)
}
@@ -574,10 +551,6 @@ func LogoutHandler(ctx echo.Context) error {
return ctx.Redirect(http.StatusFound, "/")
}
-func createPartialSessionCookie(value string) *http.Cookie {
- return hutils.CreateCookie(hutils.PartialAuthCookieName, value, 10*utils.OneMinuteSecs)
-}
-
func createSessionCookie(value string) *http.Cookie {
return hutils.CreateCookie(hutils.AuthCookieName, value, utils.OneMonthSecs)
}
diff --git a/pkg/web/handlers/utils/utils.go b/pkg/web/handlers/utils/utils.go
@@ -14,12 +14,11 @@ import (
)
const (
- HBCookieName = "dkft" // dkf troll
- SignupCookieName = "signup-token"
- AuthCookieName = "auth-token"
- PartialAuthCookieName = "partial-auth-token"
- AprilFoolCookieName = "april_fool"
- ByteRoadCookieName = "challenge_byte_road_session"
+ HBCookieName = "dkft" // dkf troll
+ SignupCookieName = "signup-token"
+ AuthCookieName = "auth-token"
+ AprilFoolCookieName = "april_fool"
+ ByteRoadCookieName = "challenge_byte_road_session"
)
var AccountTooYoungErr = errors.New("account must be at least 3 days old")
diff --git a/pkg/web/public/views/pages/sessions-gpg-sign-two-factor.gohtml b/pkg/web/public/views/pages/sessions-gpg-sign-two-factor.gohtml
@@ -22,7 +22,8 @@
<div class="card-body">
<form method="post" novalidate>
<input type="hidden" name="csrf" value="{{ .CSRF }}" />
- <input type="hidden" name="formName" value="pgp_step2" />
+ <input type="hidden" name="formName" value="pgp_sign_2fa" />
+ <input type="hidden" name="token" value="{{ .Data.Token }}" />
<div class="form-group">
<label for="encrypted_message">{{ t "Please sign the following message with your private key and send the signature" . }}</label>
<p><code>gpg --armor --detach-sign file</code></p>
diff --git a/pkg/web/public/views/pages/sessions-gpg-two-factor.gohtml b/pkg/web/public/views/pages/sessions-gpg-two-factor.gohtml
@@ -22,7 +22,8 @@
<div class="card-body">
<form method="post" novalidate>
<input type="hidden" name="csrf" value="{{ .CSRF }}" />
- <input type="hidden" name="formName" value="pgp_step2" />
+ <input type="hidden" name="formName" value="pgp_2fa" />
+ <input type="hidden" name="token" value="{{ .Data.Token }}" />
<div class="form-group">
<label for="encrypted_message">{{ t "Please decrypt the following message with your private key and send the required code" . }}</label>
<textarea name="encrypted_message" id="encrypted_message" rows="10" class="form-control" style="font-family: SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono','Courier New',monospace;" readonly>{{ .Data.EncryptedMessage }}</textarea>
diff --git a/pkg/web/public/views/pages/sessions-two-factor-recovery.gohtml b/pkg/web/public/views/pages/sessions-two-factor-recovery.gohtml
@@ -9,6 +9,8 @@
<div class="card-body">
<form autocomplete="on" method="post">
<input type="hidden" name="csrf" value="{{ .CSRF }}" />
+ <input type="hidden" name="formName" value="2fa_recovery" />
+ <input type="hidden" name="token" value="{{ .Data.Token }}" />
<fieldset>
<div class="row">
<div class="center-block">
diff --git a/pkg/web/public/views/pages/sessions-two-factor.gohtml b/pkg/web/public/views/pages/sessions-two-factor.gohtml
@@ -7,33 +7,40 @@
<strong>{{ t "Two-factor authentication" . }}</strong>
</div>
<div class="card-body">
- <form autocomplete="on" method="post">
- <input type="hidden" name="csrf" value="{{ .CSRF }}" />
- <fieldset>
- <div class="row">
- <div class="center-block">
- </div>
+ <fieldset>
+ <div class="row">
+ <div class="center-block">
</div>
- <div class="row">
- <div class="col-sm-12 col-md-10 offset-md-1 ">
- {{ if .Data.Error }}
- <div class="alert alert-danger">
- {{ .Data.Error }}
- </div>
- {{ end }}
+ </div>
+ <div class="row">
+ <div class="col-sm-12 col-md-10 offset-md-1 ">
+ {{ if .Data.Error }}
+ <div class="alert alert-danger">
+ {{ .Data.Error }}
+ </div>
+ {{ end }}
+ <form autocomplete="on" method="post">
+ <input type="hidden" name="csrf" value="{{ .CSRF }}" />
+ <input type="hidden" name="formName" value="2fa" />
+ <input type="hidden" name="token" value="{{ .Data.Token }}" />
<div class="form-group">
- <input class="form-control{{ if .Data.Error }} is-invalid{{ end }}" placeholder="{{ t "6-digit code" . }}" name="code" type="text" autocomplete="off" autocorrect="off" autocapitalize="none" autofocus required />
+ <input class="form-control{{ if .Data.Error }} is-invalid{{ end }}" placeholder="{{ t "6-digit code" . }}" name="code" type="text" maxlength="6" autocomplete="off" autocorrect="off" autocapitalize="none" autofocus required />
</div>
<div class="form-group">
<input type="submit" class="btn btn-lg btn-primary btn-block" value="{{ t "Verify" . }}" />
</div>
+ </form>
+ <form autocomplete="on" method="post">
+ <input type="hidden" name="csrf" value="{{ .CSRF }}" />
+ <input type="hidden" name="formName" value="2fa_recovery" />
+ <input type="hidden" name="token" value="{{ .Data.Token }}" />
<div class="form-group">
- <a href="/sessions/two-factor/recovery">{{ t "Enter a two-factor recovery code" . }}</a>
+ <button type="submit" class="ml-0 pl-0 btn btn-link btn-block">{{ t "Enter a two-factor recovery code" . }}</button>
</div>
- </div>
+ </form>
</div>
- </fieldset>
- </form>
+ </div>
+ </fieldset>
</div>
<div class="card-footer ">
</div>
diff --git a/pkg/web/web.go b/pkg/web/web.go
@@ -76,14 +76,6 @@ func getMainServer() echo.HandlerFunc {
noAuthGroup.POST("/login", handlers.LoginHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 4, false))
noAuthGroup.GET("/login/:loginToken", handlers.LoginAttackHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 4, false))
noAuthGroup.POST("/login/:loginToken", handlers.LoginAttackHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 2, false))
- noAuthGroup.GET("/sessions/gpg-two-factor", handlers.SessionsGpgTwoFactorHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 4, false))
- noAuthGroup.POST("/sessions/gpg-two-factor", handlers.SessionsGpgTwoFactorHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 2, false))
- noAuthGroup.GET("/sessions/gpg-sign-two-factor", handlers.SessionsGpgSignTwoFactorHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 4, false))
- noAuthGroup.POST("/sessions/gpg-sign-two-factor", handlers.SessionsGpgSignTwoFactorHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 2, false))
- noAuthGroup.GET("/sessions/two-factor", handlers.SessionsTwoFactorHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 4, false))
- noAuthGroup.POST("/sessions/two-factor", handlers.SessionsTwoFactorHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 2, false))
- noAuthGroup.GET("/sessions/two-factor/recovery", handlers.SessionsTwoFactorRecoveryHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 4, false))
- noAuthGroup.POST("/sessions/two-factor/recovery", handlers.SessionsTwoFactorRecoveryHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 2, false))
noAuthGroup.GET("/signup", handlers.SignupHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))
noAuthGroup.POST("/signup", handlers.SignupHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 4, false))
noAuthGroup.GET("/signup/invitation", handlers.SignupInvitationHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))