From fb3396ee0a7e85021dbc5ff208019361d0974b4e Mon Sep 17 00:00:00 2001 From: Will Andrews Date: Fri, 2 Jan 2026 19:36:41 +0000 Subject: [PATCH] refactor so that there's a 2FA type on the repo which replaces EmailAuthFactor Signed-off-by: Will Andrews --- models/models.go | 13 +++++++--- server/handle_account_signin.go | 18 +++++++------- server/handle_server_create_session.go | 33 ++++++++++++++------------ server/handle_server_get_session.go | 2 +- server/handle_server_update_email.go | 11 ++++++--- server/mail.go | 2 +- 6 files changed, 47 insertions(+), 32 deletions(-) diff --git a/models/models.go b/models/models.go index 0c8d574..c9c7236 100644 --- a/models/models.go +++ b/models/models.go @@ -8,6 +8,13 @@ import ( "github.com/bluesky-social/indigo/atproto/atcrypto" ) +type TwoFactorType string + +var ( + TwoFactorTypeNone = TwoFactorType("none") + TwoFactorTypeEmail = TwoFactorType("email") +) + type Repo struct { Did string `gorm:"primaryKey"` CreatedAt time.Time @@ -29,9 +36,9 @@ type Repo struct { Root []byte Preferences []byte Deactivated bool - EmailAuthFactor bool - AuthCode *string - AuthCodeExpiresAt *time.Time + TwoFactorCode *string + TwoFactorCodeExpiresAt *time.Time + TwoFactorType TwoFactorType `gorm:"default:none"` } func (r *Repo) SignFor(ctx context.Context, did string, msg []byte) ([]byte, error) { diff --git a/server/handle_account_signin.go b/server/handle_account_signin.go index e42c444..4bb8679 100644 --- a/server/handle_account_signin.go +++ b/server/handle_account_signin.go @@ -123,9 +123,9 @@ func (s *Server) handleAccountSigninPost(e echo.Context) error { 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 repo requires 2FA token and one hasn't been provided, return error prompting for one + if repo.TwoFactorType != models.TwoFactorTypeNone && req.AuthFactorToken == "" { + err = s.createAndSendTwoFactorCode(ctx, repo) if err != nil { sess.AddFlash("Something went wrong!", "error") sess.Save(e.Request(), e.Response()) @@ -137,10 +137,10 @@ func (s *Server) handleAccountSigninPost(e echo.Context) error { 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 2FAis required, now check that the one provided is valid + if repo.TwoFactorType != models.TwoFactorTypeNone { + if repo.TwoFactorCode == nil || repo.TwoFactorCodeExpiresAt == nil { + err = s.createAndSendTwoFactorCode(ctx, repo) if err != nil { sess.AddFlash("Something went wrong!", "error") sess.Save(e.Request(), e.Response()) @@ -152,11 +152,11 @@ func (s *Server) handleAccountSigninPost(e echo.Context) error { return e.Redirect(303, "/account/signin"+queryParams) } - if *repo.AuthCode != req.AuthFactorToken { + if *repo.TwoFactorCode != req.AuthFactorToken { return helpers.InvalidTokenError(e) } - if time.Now().UTC().After(*repo.AuthCodeExpiresAt) { + if time.Now().UTC().After(*repo.TwoFactorCodeExpiresAt) { return helpers.ExpiredTokenError(e) } } diff --git a/server/handle_server_create_session.go b/server/handle_server_create_session.go index 869e8ff..98d5db7 100644 --- a/server/handle_server_create_session.go +++ b/server/handle_server_create_session.go @@ -94,34 +94,34 @@ func (s *Server) handleCreateSession(e echo.Context) error { 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) + // if repo requires 2FA token and one hasn't been provided, return error prompting for one + if repo.TwoFactorType != models.TwoFactorTypeNone && (req.AuthFactorToken == nil || *req.AuthFactorToken == "") { + err = s.createAndSendTwoFactorCode(ctx, repo) if err != nil { - s.logger.Error("sending auth code", "error", err) + logger.Error("sending 2FA code", "error", err) return helpers.ServerError(e, nil) } return helpers.InputError(e, to.StringPtr("AuthFactorTokenRequired")) } - // 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 2FA is required, now check that the one provided is valid + if repo.TwoFactorType != models.TwoFactorTypeNone { + if repo.TwoFactorCode == nil || repo.TwoFactorCodeExpiresAt == nil { + err = s.createAndSendTwoFactorCode(ctx, repo) if err != nil { - logger.Error("sending auth code", "error", err) + logger.Error("sending 2FA code", "error", err) return helpers.ServerError(e, nil) } return helpers.InputError(e, to.StringPtr("AuthFactorTokenRequired")) } - if *repo.AuthCode != *req.AuthFactorToken { + if *repo.TwoFactorCode != *req.AuthFactorToken { return helpers.InvalidTokenError(e) } - if time.Now().UTC().After(*repo.AuthCodeExpiresAt) { + if time.Now().UTC().After(*repo.TwoFactorCodeExpiresAt) { return helpers.ExpiredTokenError(e) } } @@ -139,21 +139,24 @@ func (s *Server) handleCreateSession(e echo.Context) error { Did: repo.Repo.Did, Email: repo.Email, EmailConfirmed: repo.EmailConfirmedAt != nil, - EmailAuthFactor: repo.EmailAuthFactor, + EmailAuthFactor: repo.TwoFactorType != models.TwoFactorTypeNone, Active: repo.Active(), Status: repo.Status(), }) } -func (s *Server) createAndSendAuthCode(ctx context.Context, repo models.RepoActor) error { +func (s *Server) createAndSendTwoFactorCode(ctx context.Context, repo models.RepoActor) error { + // TODO: when implementing a new type of 2FA there should be some logic in here to send the + // right type of code + code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5)) eat := time.Now().Add(10 * time.Minute).UTC() - if err := s.db.Exec(ctx, "UPDATE repos SET auth_code = ?, auth_code_expires_at = ? WHERE did = ?", nil, code, eat, repo.Repo.Did).Error; err != nil { + if err := s.db.Exec(ctx, "UPDATE repos SET two_factor_code = ?, two_factor_code_expires_at = ? WHERE did = ?", nil, code, eat, repo.Repo.Did).Error; err != nil { return fmt.Errorf("updating repo: %w", err) } - if err := s.sendAuthCode(repo.Email, repo.Handle, code); err != nil { + if err := s.sendTwoFactorCode(repo.Email, repo.Handle, code); err != nil { return fmt.Errorf("sending email: %w", err) } diff --git a/server/handle_server_get_session.go b/server/handle_server_get_session.go index 60fd812..a5e41e6 100644 --- a/server/handle_server_get_session.go +++ b/server/handle_server_get_session.go @@ -23,7 +23,7 @@ func (s *Server) handleGetSession(e echo.Context) error { Did: repo.Repo.Did, Email: repo.Email, EmailConfirmed: repo.EmailConfirmedAt != nil, - EmailAuthFactor: repo.EmailAuthFactor, + EmailAuthFactor: repo.TwoFactorType != models.TwoFactorTypeNone, Active: repo.Active(), Status: repo.Status(), }) diff --git a/server/handle_server_update_email.go b/server/handle_server_update_email.go index 3f89e5d..481f014 100644 --- a/server/handle_server_update_email.go +++ b/server/handle_server_update_email.go @@ -33,7 +33,7 @@ func (s *Server) handleServerUpdateEmail(e echo.Context) error { // To disable email auth factor a token is required. // To enable email auth factor a token is not required. // If updating an email address, a token will be sent anyway - if urepo.EmailAuthFactor && req.EmailAuthFactor == false && req.Token == "" { + if urepo.TwoFactorType != models.TwoFactorTypeNone && req.EmailAuthFactor == false && req.Token == "" { return helpers.InvalidTokenError(e) } @@ -51,7 +51,12 @@ func (s *Server) handleServerUpdateEmail(e echo.Context) error { } } - query := "UPDATE repos SET email_update_code = NULL, email_update_code_expires_at = NULL, email_auth_factor = ?, email = ?" + twoFactorType := models.TwoFactorTypeNone + if req.EmailAuthFactor { + twoFactorType = models.TwoFactorTypeEmail + } + + query := "UPDATE repos SET email_update_code = NULL, email_update_code_expires_at = NULL, two_factor_type = ?, email = ?" if urepo.Email != req.Email { query += ",email_confirmed_at = NULL" @@ -59,7 +64,7 @@ func (s *Server) handleServerUpdateEmail(e echo.Context) error { query += " WHERE did = ?" - if err := s.db.Exec(ctx, query, nil, req.EmailAuthFactor, req.Email, urepo.Repo.Did).Error; err != nil { + if err := s.db.Exec(ctx, query, nil, twoFactorType, req.Email, urepo.Repo.Did).Error; err != nil { logger.Error("error updating repo", "error", err) return helpers.ServerError(e, nil) } diff --git a/server/mail.go b/server/mail.go index c7cf4c0..759625c 100644 --- a/server/mail.go +++ b/server/mail.go @@ -97,7 +97,7 @@ func (s *Server) sendEmailVerification(email, handle, code string) error { return nil } -func (s *Server) sendAuthCode(email, handle, code string) error { +func (s *Server) sendTwoFactorCode(email, handle, code string) error { if s.mail == nil { return nil } -- 2.51.2