...
1 package webserver
2
3 import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "net/http"
8 "time"
9
10 "github.com/golang-jwt/jwt/v5"
11 )
12
13 type loginReq struct {
14 Username string `json:"user"`
15 Password string `json:"password"`
16 }
17
18 func (Server *WebUIServer) IsCorrectPassword(lr loginReq) bool {
19 return (Server.WebUser+Server.WebPassword == "") ||
20 (Server.WebUser == lr.Username && Server.WebPassword == lr.Password)
21 }
22
23 func (Server *WebUIServer) handleLogin(w http.ResponseWriter, r *http.Request) {
24 var (
25 err error
26 lr loginReq
27 token string
28 )
29
30 defer func() {
31 if err != nil {
32 http.Error(w, err.Error(), http.StatusInternalServerError)
33 }
34 }()
35
36 switch r.Method {
37 case "POST":
38 if err = json.NewDecoder(r.Body).Decode(&lr); err != nil {
39 return
40 }
41 if !Server.IsCorrectPassword(lr) {
42 http.Error(w, "can not authenticate this user", http.StatusUnauthorized)
43 return
44 }
45 if token, err = generateJWT(lr.Username); err != nil {
46 return
47 }
48 _, err = w.Write([]byte(token))
49
50 case "GET":
51 fmt.Fprintf(w, "only POST methods is allowed.")
52 return
53 }
54 }
55
56 type EnsureAuth struct {
57 handler http.HandlerFunc
58 }
59
60 func (ea *EnsureAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
61 if err := validateToken(r); err != nil {
62 http.Error(w, err.Error(), http.StatusUnauthorized)
63 return
64 }
65 ea.handler(w, r)
66 }
67
68 func NewEnsureAuth(handlerToWrap http.HandlerFunc) *EnsureAuth {
69 return &EnsureAuth{handlerToWrap}
70 }
71
72 var sampleSecretKey = []byte("5m3R7K4754p4m")
73
74 func generateJWT(username string) (string, error) {
75 token := jwt.New(jwt.SigningMethodHS256)
76 claims := token.Claims.(jwt.MapClaims)
77
78 claims["authorized"] = true
79 claims["username"] = username
80 claims["exp"] = time.Now().Add(time.Hour * 8).Unix()
81
82 return token.SignedString(sampleSecretKey)
83 }
84
85 func validateToken(r *http.Request) (err error) {
86 var t string
87 if r.Header["Token"] == nil {
88 t = r.URL.Query().Get("Token")
89 } else {
90 t = r.Header["Token"][0]
91 }
92 if t == "" {
93 return errors.New("can not find token in header")
94 }
95
96 _, err = jwt.Parse(t,
97 func(_ *jwt.Token) (any, error) {
98 return sampleSecretKey, nil
99 },
100 jwt.WithExpirationRequired(),
101 jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
102 return err
103 }
104