diff --git a/server/api/auth.go b/server/api/auth.go index 390576c..b128b33 100644 --- a/server/api/auth.go +++ b/server/api/auth.go @@ -1,10 +1,24 @@ package api import ( - "fmt" "net/http" ) func (a *API) Login(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "FROM API") + body := ParseBody(r) + username := body.String("username") + password := body.String("password") + + token, err := a.App.Login(username, password) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + if token == "" { + http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) + return + } + + w.Write([]byte(token)) } diff --git a/server/app/auth.go b/server/app/auth.go new file mode 100644 index 0000000..7426bea --- /dev/null +++ b/server/app/auth.go @@ -0,0 +1,28 @@ +package app + +import ( + "database/sql" + "fmt" + + "git.ctrlz.es/mgdelacroix/craban/utils" +) + +func (a *App) Login(username, password string) (string, error) { + user, err := a.store.User().GetByUsername(username) + if err == sql.ErrNoRows { + return "", nil + } else if err != nil { + return "", fmt.Errorf("cannot get user by username: %w", err) + } + + if !utils.CheckHash(user.Password, password) { + return "", nil + } + + token, err := utils.GenerateToken(user.ID, *a.config.Secret) + if err != nil { + return "", fmt.Errorf("cannot generate token: %w", err) + } + + return token, nil +} diff --git a/server/model/user.go b/server/model/user.go index 5ee585b..2916e98 100644 --- a/server/model/user.go +++ b/server/model/user.go @@ -5,11 +5,11 @@ import ( ) type User struct { - ID int - Name string - Mail string - Username string - Password string + ID int `json:"id"` + Name string `json:"name"` + Mail string `json:"mail"` + Username string `json:"username"` + Password string `json:"-"` } func (u *User) IsValid() error { diff --git a/server/services/store/user.go b/server/services/store/user.go index d9d594c..78a14aa 100644 --- a/server/services/store/user.go +++ b/server/services/store/user.go @@ -68,6 +68,29 @@ func (us *UserStore) GetByID(id int) (*model.User, error) { return users[0], nil } +func (us *UserStore) GetByUsername(username string) (*model.User, error) { + query := us.Q().Select(userColumns...). + From("users"). + Where(sq.Eq{"username": username}) + + rows, err := query.Query() + if err != nil { + return nil, err + } + defer rows.Close() + + users, err := us.usersFromRows(rows) + if err != nil { + return nil, err + } + + if len(users) == 0 { + return nil, sql.ErrNoRows + } + + return users[0], nil +} + func (us *UserStore) Create(user *model.User) (*model.User, error) { query := us.Q().Insert("users"). Columns(userColumns[1:]...). diff --git a/webapp/src/client.js b/webapp/src/client.js index 1e48c86..3320bb2 100644 --- a/webapp/src/client.js +++ b/webapp/src/client.js @@ -5,7 +5,13 @@ export class Client { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) - }).then(r => r.text()).then(token => { + }).then(r => { + if (r.status === 200) { + return r.text() + } + console.error("INVALID") + throw new Error("invalid credentials") + }).then(token => { localStorage.setItem('token', token) return token }) diff --git a/webapp/src/pages/login.js b/webapp/src/pages/login.js index 7f5688a..42fdaf5 100644 --- a/webapp/src/pages/login.js +++ b/webapp/src/pages/login.js @@ -18,12 +18,17 @@ const Login = () => { const [username, setUsername] = useState("") const [password, setPassword] = useState("") + const [error, setError] = useState("") const handleSubmit = (e) => { e.preventDefault() - client.login(username, password).then(token => { - setToken(token) - }) + client.login(username, password) + .then(token => { + setToken(token) + }) + .catch(e => { + setError(e.toString()) + }) } const handleChange = (setFn) => { @@ -52,6 +57,8 @@ const Login = () => { onChange={handleChange(setPassword)} /> + {error !== "" &&

{error}

} + )