package main import ( "database/sql" "errors" "log" "net/http" "time" "webscaffold/models" "github.com/gofrs/uuid" "github.com/volatiletech/sqlboiler/boil" "github.com/volatiletech/sqlboiler/queries/qm" ) type apiLoginResponse struct { SessionKey string } func (this *Application) apiLogin(r *http.Request) (*apiLoginResponse, netError) { ctx := r.Context() email := r.PostFormValue(`email`) passwd := r.PostFormValue(`password`) // find user with matching email user, err := models.Users(qm.Where(`email = ?`, email), qm.Limit(1)).One(ctx, this.db) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, userFacingError{code: 403, e: errors.New(`Invalid authentication`)} } return nil, systemError{err} } // verify password ok, err := verifyPassword(int(user.PasswordFormat), user.PasswordHash, passwd) if err != nil { return nil, systemError{err} } if !ok { return nil, userFacingError{code: 403, e: errors.New(`Invalid authentication`)} } // Successful login // Maybe upgrade password format? if int(user.PasswordFormat) != hashFmtPreferred { if newHash, err := hashPassword(hashFmtPreferred, passwd); err == nil { user.PasswordFormat = int64(hashFmtPreferred) user.PasswordHash = newHash _, err := user.Update(ctx, this.db, boil.Whitelist(models.UserColumns.PasswordFormat, models.UserColumns.PasswordHash)) if err != nil { // Continue, but log error log.Printf(`couldn't update stored hash format for user '%s': %s`, email, err.Error()) } } } // Create a session token sess := models.Session{ ID: uuid.Must(uuid.NewV4()).String(), Mtime: time.Now().Unix(), UserID: user.ID, } err = sess.Insert(ctx, this.db, boil.Infer()) if err != nil { return nil, systemError{err} } // Successful login return &apiLoginResponse{SessionKey: sess.ID}, nil }