...
1 package webserver
2
3 import (
4 "errors"
5 "net/http"
6 "time"
7
8 jsoniter "github.com/json-iterator/go"
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 (s *WebUIServer) IsCorrectPassword(lr loginReq) bool {
19 return (s.WebUser+s.WebPassword == "") ||
20 (s.WebUser == lr.Username && s.WebPassword == lr.Password)
21 }
22
23 func (s *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 = jsoniter.ConfigFastest.NewDecoder(r.Body).Decode(&lr); err != nil {
39 return
40 }
41 if !s.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 default:
51 w.Header().Set("Allow", "POST")
52 http.Error(w, "only POST method is allowed", http.StatusMethodNotAllowed)
53 return
54 }
55 }
56
57 type EnsureAuth struct {
58 handler http.HandlerFunc
59 }
60
61 func (ea *EnsureAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
62 if err := validateToken(r); err != nil {
63 http.Error(w, err.Error(), http.StatusUnauthorized)
64 return
65 }
66 ea.handler(w, r)
67 }
68
69 func NewEnsureAuth(handlerToWrap http.HandlerFunc) *EnsureAuth {
70 return &EnsureAuth{handlerToWrap}
71 }
72
73 var sampleSecretKey = []byte("5m3R7K4754p4m")
74
75 func generateJWT(username string) (string, error) {
76 token := jwt.New(jwt.SigningMethodHS256)
77 claims := token.Claims.(jwt.MapClaims)
78
79 claims["authorized"] = true
80 claims["username"] = username
81 claims["exp"] = time.Now().Add(time.Hour * 8).Unix()
82
83 return token.SignedString(sampleSecretKey)
84 }
85
86 func validateToken(r *http.Request) (err error) {
87 var t string
88 if r.Header["Token"] == nil {
89 t = r.URL.Query().Get("Token")
90 } else {
91 t = r.Header["Token"][0]
92 }
93 if t == "" {
94 return errors.New("can not find token in header")
95 }
96
97 _, err = jwt.Parse(t,
98 func(_ *jwt.Token) (any, error) {
99 return sampleSecretKey, nil
100 },
101 jwt.WithExpirationRequired(),
102 jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
103 return err
104 }
105