Add login endpoint and generate a valid token
This commit is contained in:
parent
83a2d2a31f
commit
9b6f7cbdcc
6 changed files with 89 additions and 11 deletions
|
@ -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
28
server/app/auth.go
Normal 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
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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:]...).
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue