Added login function
This commit is contained in:
parent
a1a928e7cb
commit
9d08b056f3
@ -12,6 +12,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -50,6 +51,7 @@ func NewManager(db *sqlx.DB, log *slog.Logger, secret string, expireDays int) (m
|
|||||||
mngr.ExpireDays = expireDays
|
mngr.ExpireDays = expireDays
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
||||||
func (mngr *Manager) GenerateToken(data map[string]any) (string, error) { // {{{
|
func (mngr *Manager) GenerateToken(data map[string]any) (string, error) { // {{{
|
||||||
// Create a new token object, specifying signing method and the claims
|
// Create a new token object, specifying signing method and the claims
|
||||||
// you would like it to contain.
|
// 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.
|
// Sign and get the complete encoded token as a string using the secret.
|
||||||
return token.SignedString(mngr.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) { // {{{
|
func (mngr *Manager) AuthenticationHandler(w http.ResponseWriter, r *http.Request) { // {{{
|
||||||
var request struct {
|
var request struct {
|
||||||
Username string
|
Username string
|
||||||
@ -72,22 +98,38 @@ func (mngr *Manager) AuthenticationHandler(w http.ResponseWriter, r *http.Reques
|
|||||||
err := json.Unmarshal(body, &request)
|
err := json.Unmarshal(body, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify username and password against the db user table.
|
// Verify username and password against the db user table.
|
||||||
authenticated, user, err := mngr.Authenticate(request.Username, request.Password)
|
authenticated, user, err := mngr.Authenticate(request.Username, request.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !authenticated {
|
if !authenticated {
|
||||||
httpError(w, errors.New("Authentication failed"))
|
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 {
|
j, _ := json.Marshal(struct {
|
||||||
OK bool
|
OK bool
|
||||||
User User
|
User User
|
||||||
}{true, user})
|
Token string
|
||||||
|
}{true, user, token})
|
||||||
w.Write(j)
|
w.Write(j)
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
||||||
@ -104,6 +146,15 @@ func (mngr *Manager) Authenticate(username, password string) (authenticated bool
|
|||||||
password,
|
password,
|
||||||
)
|
)
|
||||||
err = row.Scan(&user.ID, &user.Username, &user.Name)
|
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
|
authenticated = user.ID > 0
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
18
main.go
18
main.go
@ -99,6 +99,7 @@ func main() { // {{{
|
|||||||
}
|
}
|
||||||
|
|
||||||
http.HandleFunc("/", rootHandler)
|
http.HandleFunc("/", rootHandler)
|
||||||
|
http.HandleFunc("/login", pageLogin)
|
||||||
http.HandleFunc("/authenticate", AuthManager.AuthenticationHandler)
|
http.HandleFunc("/authenticate", AuthManager.AuthenticationHandler)
|
||||||
http.HandleFunc("/service_worker.js", pageServiceWorker)
|
http.HandleFunc("/service_worker.js", pageServiceWorker)
|
||||||
listen := fmt.Sprintf("%s:%d", config.Network.Address, config.Network.Port)
|
listen := fmt.Sprintf("%s:%d", config.Network.Address, config.Network.Port)
|
||||||
@ -136,9 +137,8 @@ func pageServiceWorker(w http.ResponseWriter, r *http.Request) { // {{{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
||||||
func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{
|
func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{
|
||||||
// Index page is rendered since everything
|
|
||||||
// else are specifically handled.
|
|
||||||
page := HTMLTemplate.SimplePage{
|
page := HTMLTemplate.SimplePage{
|
||||||
Layout: "main",
|
Layout: "main",
|
||||||
Page: "index",
|
Page: "index",
|
||||||
@ -151,6 +151,20 @@ func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{
|
|||||||
return
|
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) { // {{{
|
func sessionFilter(fn func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) { // {{{
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Printf("Filtered ")
|
fmt.Printf("Filtered ")
|
||||||
|
Loading…
Reference in New Issue
Block a user