webscaffold/api_login.go

76 lines
1.8 KiB
Go

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
}