Thalhammer / jwt-cpp

A header only library for creating and validating json web tokens in c++
https://thalhammer.github.io/jwt-cpp/
MIT License
864 stars 235 forks source link

Decode JWT Token (Boost) #248

Closed hanusek closed 1 year ago

hanusek commented 1 year ago

What's your question?

How to decode JWT token (HS256) with Boost traits?

Additional Context

Hello. I have a token generated by https://jwt.io but I have a problem - Exception: Invalid input: not within alphabet. How to fix it? Encoded:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

VERIFY SIGNATURE:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload), 
your-256-bit-secret
)

Code:

#include <boost/json/src.hpp>
#include <jwt-cpp/traits/boost-json/traits.h>

using traits = jwt::traits::boost_json;
using claim = jwt::basic_claim<traits>;

void jwt_token(const std::string& token)
{
    const auto decoded = jwt::decode<traits>(token);
    std::cout << "decoded token: " << decoded.get_algorithm() << std::endl;
}

Error log:

token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Exception: Invalid input: not within alphabet
prince-chrismc commented 1 year ago

Hmm I am not able to repoduce 🤔

https://github.com/prince-chrismc/jwt-cpp/actions/runs/3116500580/jobs/5054387893#step:11:1536

Want to try my code?

hanusek commented 1 year ago

I solved it. The problem existed because my string (text) have white characters.

void jwt_token(const std::string& text)
{
    std::cout << "text:\t\t'" << text <<"'" << std::endl;

    auto clean_text = text;

    clean_text.erase(std::remove_if(clean_text.begin(), clean_text.end(), [](unsigned char c){return !std::isgraph(c);}), clean_text.end());

    const auto decoded = jwt::decode<traits>(clean_text);
    std::cout << "buggy alg: " << decoded.get_algorithm() << std::endl;

    const auto name = decoded.get_payload_claim("name");
    std::cout << "name: " << name << std::endl;

    const auto iat = decoded.get_payload_claim("iat");
    std::cout << "iat: " << name << std::endl;

    const auto sub = decoded.get_payload_claim("sub");
    std::cout << "sub: " << sub << std::endl;

    jwt::verify<traits>().allow_algorithm(jwt::algorithm::hs256{"your-256-bit-secret"}).verify(decoded);
  }
Thalhammer commented 1 year ago

Glad to hear you solved your issue. jwt-cpp expects a clean token as a parameter, not sure if we should check for preceding/trailing whitespace and remove it. What do you think @prince-chrismc ?

hanusek commented 1 year ago

@Thalhammer @prince-chrismc This problem was very difficult to detect. Because prints were the same. But strings weren't been the same. My string has a lot of white characters inside (Not only on end and begin).

I have a new problem with get_header_claims() and get_payload_claims().

I would like print all claims.

    const auto decoded = jwt::decode<traits>(clean_text);

    for (auto& e : decoded.get_header_claims()) {
        std::cout << e.first << " = " << e.second<< std::endl;
    }

I can't find test/example for this https://github.com/Thalhammer/jwt-cpp/blob/5c9787ebcadd122ae5c41248a0ac416f85eb846d/tests/traits/BoostJsonTest.cpp

Error:

/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h: In instantiation of ‘std::unordered_map<typename json_traits::string_type, jwt::basic_claim<json_traits> > jwt::details::map_of_claims<json_traits>::get_claims() const [with json_traits = jwt::traits::boost_json; typename json_traits::string_type = std::__cxx11::basic_string<char>]’:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2640:41:   required from ‘std::unordered_map<typename json_traits::string_type, jwt::basic_claim<json_traits> > jwt::decoded_jwt<json_traits>::get_header_claims() const [with json_traits = jwt::traits::boost_json; typename json_traits::string_type = std::__cxx11::basic_string<char>]’
/home/mhanusek/work/VersionControl/Cybersec/temp/auth/client/src/Client.cpp:38:45:   required from here
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2318:135: error: static assertion failed: currently there is a limitation on the internal implemantation of the `object_type` to have an `std::pair` like `value_type`
 2318 |                                                                                                   typename json_traits::object_type>::supports_claims_transform,
      |                                                                                                                                       ^~~~~~~~~~~~~~~~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2318:135: note: ‘jwt::details::is_valid_json_object<boost::json::standalone::value, std::__cxx11::basic_string<char>, boost::json::standalone::object>::supports_claims_transform’ evaluates to false
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2325:94: error: ‘const value_type’ {aka ‘const class boost::json::standalone::key_value_pair’} has no member named ‘first’
 2325 |                                                                    return std::make_pair(val.first, basic_claim_t{val.second});
      |                                                                                          ~~~~^~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2325:119: error: ‘const value_type’ {aka ‘const class boost::json::standalone::key_value_pair’} has no member named ‘second’
 2325 |                                                                    return std::make_pair(val.first, basic_claim_t{val.second});
      |                                                                                                                   ~~~~^~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2325:89: error: no matching function for call to ‘jwt::basic_claim<jwt::traits::boost_json>::basic_claim(<brace-enclosed initializer list>)’
 2325 |                                                                    return std::make_pair(val.first, basic_claim_t{val.second});
      |                                                                           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2157:17: note: candidate: ‘template<class Iterator> jwt::basic_claim<json_traits>::basic_claim(Iterator, Iterator) [with Iterator = Iterator; json_traits = jwt::traits::boost_json]’
 2157 |                 basic_claim(Iterator begin, Iterator end) : val(typename json_traits::array_type(begin, end)) {}
      |                 ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2157:17: note:   template argument deduction/substitution failed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2155:36: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim(const set_t&) [with json_traits = jwt::traits::boost_json; jwt::basic_claim<json_traits>::set_t = std::set<std::__cxx11::basic_string<char> >]’
 2155 |                 JWT_CLAIM_EXPLICIT basic_claim(const set_t& s) : val(typename json_traits::array_type(s.begin(), s.end())) {}
      |                                    ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2155:36: note:   conversion of argument 1 would be ill-formed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2154:36: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim(typename json_traits::value_type) [with json_traits = jwt::traits::boost_json; typename json_traits::value_type = boost::json::standalone::value]’
 2154 |                 JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::value_type v) : val(std::move(v)) {}
      |                                    ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2154:36: note:   conversion of argument 1 would be ill-formed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2153:36: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim(typename json_traits::array_type) [with json_traits = jwt::traits::boost_json; typename json_traits::array_type = boost::json::standalone::array]’
 2153 |                 JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::array_type a) : val(std::move(a)) {}
      |                                    ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2153:36: note:   conversion of argument 1 would be ill-formed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2151:36: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim(const date&) [with json_traits = jwt::traits::boost_json; jwt::date = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >]’
 2151 |                 JWT_CLAIM_EXPLICIT basic_claim(const date& d)
      |                                    ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2151:36: note:   conversion of argument 1 would be ill-formed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2150:36: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim(typename json_traits::string_type) [with json_traits = jwt::traits::boost_json; typename json_traits::string_type = std::__cxx11::basic_string<char>]’
 2150 |                 JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::string_type s) : val(std::move(s)) {}
      |                                    ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2150:36: note:   conversion of argument 1 would be ill-formed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2145:17: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim(jwt::basic_claim<json_traits>&&) [with json_traits = jwt::traits::boost_json]’
 2145 |                 basic_claim(basic_claim&&) = default;
      |                 ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2145:17: note:   conversion of argument 1 would be ill-formed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2144:17: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim(const jwt::basic_claim<json_traits>&) [with json_traits = jwt::traits::boost_json]’
 2144 |                 basic_claim(const basic_claim&) = default;
      |                 ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2144:17: note:   conversion of argument 1 would be ill-formed:
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2143:17: note: candidate: ‘jwt::basic_claim<json_traits>::basic_claim() [with json_traits = jwt::traits::boost_json]’
 2143 |                 basic_claim() = default;
      |                 ^~~~~~~~~~~
/home/mhanusek/.conan/data/jwt-cpp/0.6.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/jwt-cpp/jwt.h:2143:17: note:   candidate expects 0 arguments, 1 provided

Can you help me?

prince-chrismc commented 1 year ago

The boost library has a funky implementation which the library does not support for the "key value pairs"...

You should be able to covert the object maps to JSON as a workaround.

https://github.com/Thalhammer/jwt-cpp/blob/0628011aebb109a0ec3cdb2499106b5fc718fe5a/include/jwt-cpp/jwt.h#L2333

Sadly I am traveling so I can not write any code to show that

prince-chrismc commented 1 year ago

What do you think @prince-chrismc ?

I think the head of the problem is the error message. When it said it should say "the character at Index 14 ' ' is not apart of the alphabet" usually you need to stop through the code which is less fun

prince-chrismc commented 1 year ago

I opened a PR to that it will be easier to print the claims for Boost. #251

I would also like to break a separate issue for having more feedback in the errors of base64 decoding, is that good for everyone?