Added login function
This commit is contained in:
parent
a1a928e7cb
commit
9d08b056f3
@ -12,6 +12,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
@ -50,6 +51,7 @@ func NewManager(db *sqlx.DB, log *slog.Logger, secret string, expireDays int) (m
|
||||
mngr.ExpireDays = expireDays
|
||||
return
|
||||
} // }}}
|
||||
|
||||
func (mngr *Manager) GenerateToken(data map[string]any) (string, error) { // {{{
|
||||
// Create a new token object, specifying signing method and the claims
|
||||
// you would like it to contain.
|
||||
@ -62,6 +64,30 @@ func (mngr *Manager) GenerateToken(data map[string]any) (string, error) { // {{{
|
||||
// Sign and get the complete encoded token as a string using the secret.
|
||||
return token.SignedString(mngr.secret)
|
||||
} // }}}
|
||||
func (mngr *Manager) VerifyToken(tokenString string) (jwt.Claims, error) { // {{{
|
||||
// Parse takes the token string and a function for looking up the key. The latter is especially
|
||||
// useful if you use multiple keys for your application. The standard is to use 'kid' in the
|
||||
// head of the token to identify which key to use, but the parsed token (head and claims) is provided
|
||||
// to the callback, providing flexibility.
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
|
||||
return mngr.secret, nil
|
||||
})
|
||||
if err != nil {
|
||||
mngr.log.Error("jwt", "error", err)
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
return claims, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} // }}}
|
||||
func (mngr *Manager) AuthenticationHandler(w http.ResponseWriter, r *http.Request) { // {{{
|
||||
var request struct {
|
||||
Username string
|
||||
@ -72,22 +98,38 @@ func (mngr *Manager) AuthenticationHandler(w http.ResponseWriter, r *http.Reques
|
||||
err := json.Unmarshal(body, &request)
|
||||
if err != nil {
|
||||
httpError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify username and password against the db user table.
|
||||
authenticated, user, err := mngr.Authenticate(request.Username, request.Password)
|
||||
if err != nil {
|
||||
httpError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !authenticated {
|
||||
httpError(w, errors.New("Authentication failed"))
|
||||
return
|
||||
}
|
||||
|
||||
// A new token is generated with the information.
|
||||
var token string
|
||||
data := make(map[string]any)
|
||||
data["uid"] = user.ID
|
||||
data["login"] = user.Username
|
||||
data["name"] = user.Name
|
||||
token, err = mngr.GenerateToken(data)
|
||||
if err != nil {
|
||||
httpError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
j, _ := json.Marshal(struct {
|
||||
OK bool
|
||||
User User
|
||||
}{true, user})
|
||||
OK bool
|
||||
User User
|
||||
Token string
|
||||
}{true, user, token})
|
||||
w.Write(j)
|
||||
} // }}}
|
||||
|
||||
@ -104,6 +146,15 @@ func (mngr *Manager) Authenticate(username, password string) (authenticated bool
|
||||
password,
|
||||
)
|
||||
err = row.Scan(&user.ID, &user.Username, &user.Name)
|
||||
if err != nil && err.Error() == "sql: no rows in result set" {
|
||||
err = nil
|
||||
authenticated = false
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
authenticated = user.ID > 0
|
||||
return
|
||||
} // }}}
|
||||
|
18
main.go
18
main.go
@ -99,6 +99,7 @@ func main() { // {{{
|
||||
}
|
||||
|
||||
http.HandleFunc("/", rootHandler)
|
||||
http.HandleFunc("/login", pageLogin)
|
||||
http.HandleFunc("/authenticate", AuthManager.AuthenticationHandler)
|
||||
http.HandleFunc("/service_worker.js", pageServiceWorker)
|
||||
listen := fmt.Sprintf("%s:%d", config.Network.Address, config.Network.Port)
|
||||
@ -136,9 +137,8 @@ func pageServiceWorker(w http.ResponseWriter, r *http.Request) { // {{{
|
||||
return
|
||||
}
|
||||
} // }}}
|
||||
|
||||
func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{
|
||||
// Index page is rendered since everything
|
||||
// else are specifically handled.
|
||||
page := HTMLTemplate.SimplePage{
|
||||
Layout: "main",
|
||||
Page: "index",
|
||||
@ -151,6 +151,20 @@ func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{
|
||||
return
|
||||
}
|
||||
} // }}}
|
||||
func pageLogin(w http.ResponseWriter, r *http.Request) { // {{{
|
||||
page := HTMLTemplate.SimplePage{
|
||||
Layout: "main",
|
||||
Page: "login",
|
||||
Version: VERSION,
|
||||
}
|
||||
|
||||
err := Webengine.Render(page, w, r)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
} // }}}
|
||||
|
||||
func sessionFilter(fn func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) { // {{{
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("Filtered ")
|
||||
|
Loading…
Reference in New Issue
Block a user