func TokenBlacklister(h http.Handler, store TokenStorage, secret []byte, opts ...TokenOpt) http.Handler {
	o := options{
		logger:     handler.NopLogger(),
		claimer:    func(c *jwt.StandardClaims) jwt.Claims { return c },
		expiration: time.Hour * 24 * 15,
		extractor:  request.MultiExtractor{headerExtractor{}, request.ArgumentExtractor{"token"}},
	}

	o.apply(opts)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		claims := o.claimer(&jwt.StandardClaims{})

		tokenStr, err := o.extractor.ExtractToken(r)
		if err != nil {
			o.logger.Print("Invalid request: ", err)

			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
			}

			return secret, nil
		})

		errCode := http.StatusInternalServerError
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// No reason to store an expired token
				if h == nil {
					w.WriteHeader(http.StatusOK)
				} else {
					h.ServeHTTP(w, r)
				}

				return
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				err = nil
			} else {
				errCode = http.StatusBadRequest
			}
		}

		if err == nil {
			var expiration time.Time
			if v, ok := token.Claims.(*jwt.StandardClaims); ok {
				expiration = time.Unix(v.ExpiresAt, 0)
			} else if v, ok := token.Claims.(jwt.MapClaims); ok {
				if exp, ok := v["exp"].(int64); ok {
					expiration = time.Unix(exp, 0)
				}
			}

			if expiration.IsZero() {
				expiration = time.Now().Add(o.expiration)
			}

			err = store.Store(tokenStr, expiration)
		}

		if err != nil {
			o.logger.Print("Error authenticating user: ", err)

			http.Error(w, err.Error(), errCode)
			return
		}

		if h == nil {
			w.WriteHeader(http.StatusOK)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}
func TokenBlacklister(h http.Handler, store TokenStorage, secret []byte, opts ...TokenOpt) http.Handler {
	o := options{
		logger:     handler.NopLogger(),
		claimer:    func(c *jwt.StandardClaims) jwt.Claims { return c },
		expiration: time.Hour * 24 * 15,
		extractor:  request.MultiExtractor{headerExtractor{}, request.ArgumentExtractor{"token"}},
	}

	o.apply(opts)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		claims := o.claimer(&jwt.StandardClaims{})

		tokenStr, err := o.extractor.ExtractToken(r)
		if err != nil {
			o.logger.Print("Invalid request: ", err)

			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
			}

			return secret, nil
		})

		errCode := http.StatusInternalServerError
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// No reason to store an expired token
				if h == nil {
					w.WriteHeader(http.StatusOK)
				} else {
					h.ServeHTTP(w, r)
				}

				return
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				err = nil
			} else {
				errCode = http.StatusBadRequest
			}
		}

		if err == nil {
			var expiration time.Time
			if v, ok := token.Claims.(*jwt.StandardClaims); ok {
				expiration = time.Unix(v.ExpiresAt, 0)
			} else if v, ok := token.Claims.(jwt.MapClaims); ok {
				if exp, ok := v["exp"].(int64); ok {
					expiration = time.Unix(exp, 0)
				}
			}

			if expiration.IsZero() {
				expiration = time.Now().Add(o.expiration)
			}

			err = store.Store(tokenStr, expiration)
		}

		if err != nil {
			o.logger.Print("Error authenticating user: ", err)

			http.Error(w, err.Error(), errCode)
			return
		}

		if h == nil {
			w.WriteHeader(http.StatusOK)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}
// TokenBlacklister returns a handler that will blacklist any token found in
// the request. Typically to be used around logout handlers.
//
// If handler h is nil, upon successful blacklist, 200 OK is returned.