From cbaee078904332c311f24c9df623acb2291e2476 Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Wed, 25 Sep 2024 21:31:57 +0200 Subject: [PATCH] chore(auth): add service structs and test error handling --- db/auth.go | 29 +++++++++++++ service/auth.go | 107 +++++++++++++++++++++++++----------------------- 2 files changed, 84 insertions(+), 52 deletions(-) create mode 100644 db/auth.go diff --git a/db/auth.go b/db/auth.go new file mode 100644 index 0000000..eb609de --- /dev/null +++ b/db/auth.go @@ -0,0 +1,29 @@ +package db + +import ( + "database/sql" + "errors" + "strings" + + "github.com/google/uuid" +) + +type AuthDb interface { + InsertUser(userIs uuid.UUID, email string, password string) error +} +type AuthDbSqlite struct { + db *sql.DB +} + +func (a *AuthDbSqlite) InsertUser(userId uuid.UUID, email string, passwordHash []byte, salt []byte) error { + + _, err := a.db.Exec("INSERT INTO user (user_uuid, email, email_verified, is_admin, password, salt, created_at) VALUES (?, ?, FALSE, FALSE, ?, ?, datetime())", userId, email, passwordHash, salt) + if err != nil { + if strings.Contains(err.Error(), "email") { + return errors.New("Bad Request") + } + + utils.LogError("Could not insert user", err) + return ErrInternalServer + } +} diff --git a/service/auth.go b/service/auth.go index d8258a5..13b049c 100644 --- a/service/auth.go +++ b/service/auth.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "me-fit/db" "me-fit/template/auth" tempMail "me-fit/template/mail" "me-fit/types" @@ -33,7 +34,8 @@ var ( // NOT TESTED type AuthService struct { - db *sql.DB + db *sql.DB + dbSer *db.AuthDb } func NewAuthService(db *sql.DB) *AuthService { @@ -42,6 +44,58 @@ func NewAuthService(db *sql.DB) *AuthService { } } +func (a *AuthService) SignUp(email string, password string) (*types.SessionId, error) { + _, err := mail.ParseAddress(email) + if err != nil { + return nil, errors.Join(errors.New("Invalid email")) + } + + err = checkPassword(password) + if err != nil { + return nil, err + } + + userId, err := uuid.NewRandom() + if err != nil { + utils.LogError("Could not generate UUID for new user", err) + return nil, ErrInternalServer + } + + salt := make([]byte, 16) + _, err = rand.Read(salt) + if err != nil { + utils.LogError("Could not generate salt for new user", err) + return nil, ErrInternalServer + } + + hash := getHashPassword(password, salt) + + err := a.dbSer.InsertUser(a.db, userId, email, hash, salt) + _, err = a.db.Exec("INSERT INTO user (user_uuid, email, email_verified, is_admin, password, salt, created_at) VALUES (?, ?, FALSE, FALSE, ?, ?, datetime())", userId, email, hash, salt) + if err != nil { + // This does leak information about the email being in use, though not specifically stated + // It needs to be refacoteres to "If the email is not already in use, an email has been send to your address", or something + // The happy path, currently a redirect, needs to send the same message! + // Then it is also important to have the same compute time in both paths + // Otherwise an attacker could guess emails when comparing the response time + if strings.Contains(err.Error(), "email") { + return nil, errors.New("Bad Request") + } + + utils.LogError("Could not insert user", err) + return nil, ErrInternalServer + } + + sessionId, err := createSession(a.db, userId) + if err != nil { + return nil, err + } + + // Send verification email as a goroutine + go SendVerificationEmail(a.db, userId.String(), email) + return sessionId, nil +} + func (a *AuthService) SignIn(email string, password string) (*types.SessionId, error) { start := time.Now() @@ -117,57 +171,6 @@ func GetUserFromSessionId(db *sql.DB, sessionId types.SessionId) *types.User { return types.NewUser(userId, email, sessionId, emailVerified) } } -func SignUp(db *sql.DB, email string, password string) (*types.SessionId, error) { - _, err := mail.ParseAddress(email) - if err != nil { - return nil, errors.Join(errors.New("Invalid email")) - } - - err = checkPassword(password) - if err != nil { - return nil, err - } - - userId, err := uuid.NewRandom() - if err != nil { - utils.LogError("Could not generate UUID for new user", err) - return nil, ErrInternalServer - } - - salt := make([]byte, 16) - _, err = rand.Read(salt) - if err != nil { - utils.LogError("Could not generate salt for new user", err) - return nil, ErrInternalServer - } - - hash := getHashPassword(password, salt) - - _, err = db.Exec("INSERT INTO user (user_uuid, email, email_verified, is_admin, password, salt, created_at) VALUES (?, ?, FALSE, FALSE, ?, ?, datetime())", userId, email, hash, salt) - if err != nil { - // This does leak information about the email being in use, though not specifically stated - // It needs to be refacoteres to "If the email is not already in use, an email has been send to your address", or something - // The happy path, currently a redirect, needs to send the same message! - // Then it is also important to have the same compute time in both paths - // Otherwise an attacker could guess emails when comparing the response time - if strings.Contains(err.Error(), "email") { - return nil, errors.New("Bad Request") - } - - utils.LogError("Could not insert user", err) - return nil, ErrInternalServer - } - - sessionId, err := createSession(db, userId) - if err != nil { - return nil, err - } - - // Send verification email as a goroutine - go SendVerificationEmail(db, userId.String(), email) - return sessionId, nil -} - func ValidateEmail(db *sql.DB, token string) error { result, err := db.Exec(` UPDATE user