Add login endpoint and generate a valid token

This commit is contained in:
Miguel de la Cruz 2021-09-13 12:46:43 +02:00
parent 83a2d2a31f
commit 9b6f7cbdcc
6 changed files with 89 additions and 11 deletions

View file

@ -1,10 +1,24 @@
package api package api
import ( import (
"fmt"
"net/http" "net/http"
) )
func (a *API) Login(w http.ResponseWriter, r *http.Request) { 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))
} }

28
server/app/auth.go Normal file
View file

@ -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
}

View file

@ -5,11 +5,11 @@ import (
) )
type User struct { type User struct {
ID int ID int `json:"id"`
Name string Name string `json:"name"`
Mail string Mail string `json:"mail"`
Username string Username string `json:"username"`
Password string Password string `json:"-"`
} }
func (u *User) IsValid() error { func (u *User) IsValid() error {

View file

@ -68,6 +68,29 @@ func (us *UserStore) GetByID(id int) (*model.User, error) {
return users[0], nil 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) { func (us *UserStore) Create(user *model.User) (*model.User, error) {
query := us.Q().Insert("users"). query := us.Q().Insert("users").
Columns(userColumns[1:]...). Columns(userColumns[1:]...).

View file

@ -5,7 +5,13 @@ export class Client {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }) 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) localStorage.setItem('token', token)
return token return token
}) })

View file

@ -18,12 +18,17 @@ const Login = () => {
const [username, setUsername] = useState("") const [username, setUsername] = useState("")
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [error, setError] = useState("")
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault() e.preventDefault()
client.login(username, password).then(token => { client.login(username, password)
setToken(token) .then(token => {
}) setToken(token)
})
.catch(e => {
setError(e.toString())
})
} }
const handleChange = (setFn) => { const handleChange = (setFn) => {
@ -52,6 +57,8 @@ const Login = () => {
onChange={handleChange(setPassword)} onChange={handleChange(setPassword)}
/> />
{error !== "" && <p style={{color: 'red'}}>{error}</p>}
<Button variant="contained" color="primary" onClick={handleSubmit}>Login</Button> <Button variant="contained" color="primary" onClick={handleSubmit}>Login</Button>
</Box> </Box>
) )