// Copyright 2022 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package node import ( "net/http" "strings" "time" "github.com/golang-jwt/jwt/v4" ) const jwtExpiryTimeout = 60 * time.Second type jwtHandler struct { keyFunc func(token *jwt.Token) (interface{}, error) next http.Handler } // newJWTHandler creates a http.Handler with jwt authentication support. func newJWTHandler(secret []byte, next http.Handler) http.Handler { return &jwtHandler{ keyFunc: func(token *jwt.Token) (interface{}, error) { return secret, nil }, next: next, } } // ServeHTTP implements http.Handler func (handler *jwtHandler) ServeHTTP(out http.ResponseWriter, r *http.Request) { var ( strToken string claims jwt.RegisteredClaims ) if auth := r.Header.Get("Authorization"); strings.HasPrefix(auth, "Bearer ") { strToken = strings.TrimPrefix(auth, "Bearer ") } if len(strToken) == 0 { http.Error(out, "missing token", http.StatusUnauthorized) return } // We explicitly set only HS256 allowed, and also disables the // claim-check: the RegisteredClaims internally requires 'iat' to // be no later than 'now', but we allow for a bit of drift. token, err := jwt.ParseWithClaims(strToken, &claims, handler.keyFunc, jwt.WithValidMethods([]string{"HS256"}), jwt.WithoutClaimsValidation()) switch { case err != nil: http.Error(out, err.Error(), http.StatusUnauthorized) case !token.Valid: http.Error(out, "invalid token", http.StatusUnauthorized) case !claims.VerifyExpiresAt(time.Now(), false): // optional http.Error(out, "token is expired", http.StatusUnauthorized) case claims.IssuedAt == nil: http.Error(out, "missing issued-at", http.StatusUnauthorized) case time.Since(claims.IssuedAt.Time) > jwtExpiryTimeout: http.Error(out, "stale token", http.StatusUnauthorized) case time.Until(claims.IssuedAt.Time) > jwtExpiryTimeout: http.Error(out, "future token", http.StatusUnauthorized) default: handler.next.ServeHTTP(out, r) } }