package web import ( "context" "fmt" "net/http" "github.com/golang-jwt/jwt/v5" ) type contextKey string const userIDKey contextKey = "userID" func (s *Server) hasAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("nfeeder_token") if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // parse and validate claims := &jwt.RegisteredClaims{} token, err := jwt.ParseWithClaims(cookie.Value, claims, func(t *jwt.Token) (interface{}, error) { // check alg if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } return jwtKey, nil }) /// golang-jwt the library internally runs a Valid(): /// Time Check: It compares time.Now() against the ExpiresAt value. /// Not Before Check: It checks if the nbf (Not Before) time has passed. /// Issued At Check: It ensures the iat isn't in the future. if err != nil || !token.Valid { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Verify issuer if claims.Issuer != ISSUER { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Success! Store userID in context for handlers to use ctx := context.WithValue(r.Context(), userIDKey, claims.Subject) next.ServeHTTP(w, r.WithContext(ctx)) }) }