package web import ( "context" "errors" "fmt" "log" "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) { var tokenString string // Authorization Header Bearer token authHeader := r.Header.Get("Authorization") if len(authHeader) > 7 && authHeader[:7] == "Bearer " { tokenString = authHeader[7:] } // Fallback to Cookie (future Web Frontend) if tokenString == "" { if cookie, err := r.Cookie("nfeeder_token"); err == nil { tokenString = cookie.Value } } if tokenString == "" { WriteJSON(w, http.StatusUnauthorized, ErrorResponse{ Error: "unauthorized", }) return } // parse and validate claims := &jwt.RegisteredClaims{} token, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (interface{}, error) { // check/confirm alg if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } return s.jwtSecret, 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 { // Check if the error is specifically because the token expired if errors.Is(err, jwt.ErrTokenExpired) { WriteJSON(w, http.StatusUnauthorized, ErrorResponse{ Error: "Token expired: Please use your refresh token to get a new session", }) return } WriteJSON(w, http.StatusUnauthorized, ErrorResponse{ Error: "unauthorized", }) return } // Verify issuer if claims.Issuer != ISSUER { log.Printf("Invalid Token, issuer incorrect or tampered with") WriteJSON(w, http.StatusUnauthorized, ErrorResponse{ Error: "Invalid Token", }) return } // Success! Store userID in context for handlers to use ctx := context.WithValue(r.Context(), userIDKey, claims.Subject) next.ServeHTTP(w, r.WithContext(ctx)) }) }