diff --git a/server/handle_account_signin.go b/server/handle_account_signin.go
index a024f00..e42c444 100644
--- a/server/handle_account_signin.go
+++ b/server/handle_account_signin.go
@@ -2,7 +2,9 @@ package server
import (
"errors"
+ "fmt"
"strings"
+ "time"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/gorilla/sessions"
@@ -15,9 +17,10 @@ import (
)
type OauthSigninInput struct {
- Username string `form:"username"`
- Password string `form:"password"`
- QueryParams string `form:"query_params"`
+ Username string `form:"username"`
+ Password string `form:"password"`
+ AuthFactorToken string `form:"token"`
+ QueryParams string `form:"query_params"`
}
func (s *Server) getSessionRepoOrErr(e echo.Context) (*models.RepoActor, *sessions.Session, error) {
@@ -44,8 +47,9 @@ func (s *Server) getSessionRepoOrErr(e echo.Context) (*models.RepoActor, *sessio
func getFlashesFromSession(e echo.Context, sess *sessions.Session) map[string]any {
defer sess.Save(e.Request(), e.Response())
return map[string]any{
- "errors": sess.Flashes("error"),
- "successes": sess.Flashes("success"),
+ "errors": sess.Flashes("error"),
+ "successes": sess.Flashes("success"),
+ "tokenrequired": sess.Flashes("tokenrequired"),
}
}
@@ -83,6 +87,11 @@ func (s *Server) handleAccountSigninPost(e echo.Context) error {
idtype = "email"
}
+ queryParams := ""
+ if req.QueryParams != "" {
+ queryParams = fmt.Sprintf("?%s", req.QueryParams)
+ }
+
// TODO: we should make this a helper since we do it for the base create_session as well
var repo models.RepoActor
var err error
@@ -101,7 +110,7 @@ func (s *Server) handleAccountSigninPost(e echo.Context) error {
sess.AddFlash("Something went wrong!", "error")
}
sess.Save(e.Request(), e.Response())
- return e.Redirect(303, "/account/signin")
+ return e.Redirect(303, "/account/signin"+queryParams)
}
if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
@@ -111,7 +120,45 @@ func (s *Server) handleAccountSigninPost(e echo.Context) error {
sess.AddFlash("Something went wrong!", "error")
}
sess.Save(e.Request(), e.Response())
- return e.Redirect(303, "/account/signin")
+ return e.Redirect(303, "/account/signin"+queryParams)
+ }
+
+ // if repo requires auth factor token and one hasn't been provided, return error prompting for one
+ if repo.EmailAuthFactor && req.AuthFactorToken == "" {
+ err = s.createAndSendAuthCode(ctx, repo)
+ if err != nil {
+ sess.AddFlash("Something went wrong!", "error")
+ sess.Save(e.Request(), e.Response())
+ return e.Redirect(303, "/account/signin"+queryParams)
+ }
+
+ sess.AddFlash("requires 2FA token", "tokenrequired")
+ sess.Save(e.Request(), e.Response())
+ return e.Redirect(303, "/account/signin"+queryParams)
+ }
+
+ // if auth factor is required, now check that the one provided is valid
+ if repo.EmailAuthFactor {
+ if repo.AuthCode == nil || repo.AuthCodeExpiresAt == nil {
+ err = s.createAndSendAuthCode(ctx, repo)
+ if err != nil {
+ sess.AddFlash("Something went wrong!", "error")
+ sess.Save(e.Request(), e.Response())
+ return e.Redirect(303, "/account/signin"+queryParams)
+ }
+
+ sess.AddFlash("requires 2FA token", "tokenrequired")
+ sess.Save(e.Request(), e.Response())
+ return e.Redirect(303, "/account/signin"+queryParams)
+ }
+
+ if *repo.AuthCode != req.AuthFactorToken {
+ return helpers.InvalidTokenError(e)
+ }
+
+ if time.Now().UTC().After(*repo.AuthCodeExpiresAt) {
+ return helpers.ExpiredTokenError(e)
+ }
}
sess.Options = &sessions.Options{
@@ -127,8 +174,8 @@ func (s *Server) handleAccountSigninPost(e echo.Context) error {
return err
}
- if req.QueryParams != "" {
- return e.Redirect(303, "/oauth/authorize?"+req.QueryParams)
+ if queryParams != "" {
+ return e.Redirect(303, "/oauth/authorize"+queryParams)
} else {
return e.Redirect(303, "/account")
}
diff --git a/server/handle_server_create_session.go b/server/handle_server_create_session.go
index 37a952f..869e8ff 100644
--- a/server/handle_server_create_session.go
+++ b/server/handle_server_create_session.go
@@ -87,6 +87,13 @@ func (s *Server) handleCreateSession(e echo.Context) error {
return helpers.ServerError(e, nil)
}
+ if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
+ if err != bcrypt.ErrMismatchedHashAndPassword {
+ logger.Error("erorr comparing hash and password", "error", err)
+ }
+ return helpers.InputError(e, to.StringPtr("InvalidRequest"))
+ }
+
// if repo requires auth factor token and one hasn't been provided, return error prompting for one
if repo.EmailAuthFactor && (req.AuthFactorToken == nil || *req.AuthFactorToken == "") {
err = s.createAndSendAuthCode(ctx, repo)
@@ -103,7 +110,7 @@ func (s *Server) handleCreateSession(e echo.Context) error {
if repo.AuthCode == nil || repo.AuthCodeExpiresAt == nil {
err = s.createAndSendAuthCode(ctx, repo)
if err != nil {
- s.logger.Error("sending auth code", "error", err)
+ logger.Error("sending auth code", "error", err)
return helpers.ServerError(e, nil)
}
@@ -119,13 +126,6 @@ func (s *Server) handleCreateSession(e echo.Context) error {
}
}
- if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
- if err != bcrypt.ErrMismatchedHashAndPassword {
- logger.Error("erorr comparing hash and password", "error", err)
- }
- return helpers.InputError(e, to.StringPtr("InvalidRequest"))
- }
-
sess, err := s.createSession(ctx, &repo.Repo)
if err != nil {
logger.Error("error creating session", "error", err)
diff --git a/server/templates/signin.html b/server/templates/signin.html
index e589a33..3348c26 100644
--- a/server/templates/signin.html
+++ b/server/templates/signin.html
@@ -26,6 +26,10 @@
type="password"
placeholder="Password"
/>
+ {{ if .flashes.tokenrequired }}
+
+
+ {{ end }}