benmcollins / libjwt

JWT C Library
Mozilla Public License 2.0
351 stars 164 forks source link

failed to parse jwt, jwt_decode seems broken #79

Closed katsar0v closed 5 years ago

katsar0v commented 5 years ago

I have two tokens:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.tJoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.GpCRdGxE4uClX6Vg7eAPwG-37ZvNBQXyfcldKzDG_QI

First one is a test token from https://github.com/TeslaGov/ngx-http-auth-jwt-module/blob/master/test.sh, second one I generated using PHP. According to jwt.io, the first token is not valid, the second is valid. But when I use the nginx module which uses jwt_decode from libjwt (which I compiled and installed), the first token is valid, second is invalid. Algorithm is HS256.

Secret for testing is 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF

benmcollins commented 5 years ago

I just tried on JWT.io and I got valid for the first, and invalid for the second.

katsar0v commented 5 years ago

I can see it turns to the second one?

image

benmcollins commented 5 years ago

Ok, I can reproduce that. Running some tests. Just to be clear, is your expectation this this should match JWT.io's JavaScript results?

benmcollins commented 5 years ago

Here's the issue. The key field on JWT.io expects a series of binary bytes (00FFAA being actually 0x00 0xFF 0xAA) and not a literal string. LibJWT and the PHP you used would expect something more like:

unsigned char key[] = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF";

As opposed to

unsigned char key[] = "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF";

These two things are quite different. If I use the first in LibJWT then I get VALID for the first token, but I have to use the second to get VALID for the second token.

So this is a non-issue in LibJWT, it's just confusion between string and binary in representing of the key.

katsar0v commented 5 years ago

The issue is that libjwt does not accept the second token as valid, though it is valid in jwt.io

katsar0v commented 5 years ago

My expectation is libjwt to accept second token, which is accepted by jwt.io

benmcollins commented 5 years ago

It does. These two tests pass:

START_TEST(test_jwt_decode_hs256_issue_1)
{
    const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.tJoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM";
    char key256[] = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF";
    jwt_t *jwt;
    int ret;

    ret = jwt_decode(&jwt, token, key256, sizeof(key256));
    ck_assert_int_eq(ret, 0);
    ck_assert(jwt != NULL);

    jwt_free(jwt);
}
END_TEST

START_TEST(test_jwt_decode_hs256_issue_2)
{
    const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.GpCRdGxE4uClX6Vg7eAPwG-37ZvNBQXyfcldKzDG_QI";
    char key256[] = "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF";
    jwt_t *jwt;
    int ret;

    ret = jwt_decode(&jwt, token, key256, strlen(key256));
    ck_assert_int_eq(ret, 0);
    ck_assert(jwt != NULL);

    jwt_free(jwt);
}
END_TEST

The issue isn't that it doesn't work, it's that you used the key as a string where as JWT.io assumes the "string" is actually a string of bytes in hex format.

benmcollins commented 5 years ago

I'm going to assume that nginx is using that key as a "string of bytes in hex format" like JWT.io is, and when you generated the token using PHP, you used it as a string of chars.

You need to make sure that PHP uses a string of bytes in hex, like I have in the above example, where each pair has \x prepended to it.

katsar0v commented 5 years ago

Thank you for the clarification, will see how to implement this in PHP. I also tried with Python, same result. Maybe some clarification / documentation would be useful, I know this library is only for C intended, but JWT is usually language independent. I didn't know that the key should be a string of bytes in hex.

katsar0v commented 5 years ago

I guess with python this is accomplished this way:

bytes.fromhex('00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF')
katsar0v commented 5 years ago

In python actually there is the PyJWT lib, which does not say anything about bytes in hex.

katsar0v commented 5 years ago

This is what I get with PyJWT:

image

Token is eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.qVjiumbfcTxYglYfqH40JAjrtJOYU68znIj_CE51W0M

benmcollins commented 5 years ago

Each library will have language specific nuances, not to mention that something like JWT.io's UI is implemented without regard to the JavaScript language, so it definitely needs some documentation on what is expected.

LibJWT is written in C, so the key is based on C language constructs. A string of chars, bytes, hex/ascii, etc, are all defined by the C language.

I wish I could account for documenting other implementations and their possible ambiguity with regard to the format of the key. However, it's out of my control.

katsar0v commented 5 years ago

Still cannot generate the token you have given me in python:

>>> str(bytes.fromhex(key_str))
'b\'\\x00\\x11"3DUfw\\x88\\x99\\xaa\\xbb\\xcc\\xdd\\xee\\xff\\x00\\x11"3DUfw\\x88\\x99\\xaa\\xbb\\xcc\\xdd\\xee\\xff\''
>>> key = str(bytes.fromhex(key_str))
>>> import jwt
>>> payload = {
...   "sub": "some-long-uuid",
...   "firstName": "hello",
...   "lastName": "world",
...   "roles": [
...     "this",
...     "that",
...     "theother"
...   ],
...   "iss": "issuer",
...   "personId": "75bb3cc7-b933-44f0-93c6-147b082fadb5",
...   "exp": 1908835200,
...   "iat": 1488819600,
...   "username": "hello.world"
... }
>>> algorithm = 'HS256'
>>> encoded = jwt.encode(payload, key, algorithm=algorithm)
>>> encoded
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.X1iI3-wIq5a3EYfwzjuOFOYIUVDA1tIJALCiCxWonjc'

Seems this issue only happens here, because in Python and PHP I am able to generate the same JWT tokens, which for some reason are not valid in libjwt for c

benmcollins commented 5 years ago
START_TEST(test_jwt_decode_hs256_issue_3)
{
    const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.qVjiumbfcTxYglYfqH40JAjrtJOYU68znIj_CE51W0M";
    const char key256[] = "00112233445566778899AABBCCDDEEFF001122334455"
            "66778899AABBCCDDEEFF";
    jwt_t *jwt;
    int ret;

    ret = jwt_decode(&jwt, token, (const unsigned char *)key256, strlen(key256));
    ck_assert_int_eq(ret, 0);
    ck_assert(jwt != NULL);

    jwt_free(jwt);
}
END_TEST

This test succeeds for me and verifies. I'm unsure how you are trying to validate in LibJWT, but since I can do it in LibJWT directly, I can only assume it's nginx that is getting in the way of proper validation. Maybe you should bring this up with them.

benmcollins commented 5 years ago

Better yet, use the hex bytes in Python or PHP like I suggested:

key = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"
benmcollins commented 5 years ago

This is the relevant code in ngx-http-auth-jwt

    // convert key from hex to binary, if a symmetric key

    auth_jwt_algorithm = jwtcf->auth_jwt_algorithm;
    if (auth_jwt_algorithm.len == 0 || (auth_jwt_algorithm.len == sizeof("HS256") - 1 && ngx_strncmp(auth_jwt_algorithm.data, "HS256", sizeof("HS256") - 1)==0))
    {
            keylen = jwtcf->auth_jwt_key.len / 2;
            keyBinary = ngx_palloc(r->pool, keylen);
            if (0 != hex_to_binary((char *)jwtcf->auth_jwt_key.data, keyBinary, jwtcf->auth_jwt_key.len))
            {
                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to turn hex key into binary");
                    goto redirect;
            }
    }

As you can see, the string is assumed to be a hex representation of a binary key. The README.md does not document this properly. So in order for you to get a proper validation, use the string of \x encoded bytes to generate the token.

katsar0v commented 5 years ago

It does not seem to work again:

katsarov@katsarov-ThinkPad-T470s:~$ python
Python 3.6.5 |Anaconda, Inc.| (default, Apr 29 2018, 16:14:56) 
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import jwt
>>> payload = {
...   "sub": "some-long-uuid",
...   "firstName": "hello",
...   "lastName": "world",
...   "roles": [
...     "this",
...     "that",
...     "theother"
...   ],
...   "iss": "issuer",
...   "personId": "75bb3cc7-b933-44f0-93c6-147b082fadb5",
...   "exp": 1908835200,
...   "iat": 1488819600,
...   "username": "hello.world"
... }
>>> key = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"
>>> encoded = jwt.encode(payload, key, algorithm='HS256')
>>> encoded
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.cFUZTwXPKzBwQ26rYlKDEzZgNZychTDnCZnhRszXNyw'

Then I get following logs:

2019/01/07 08:31:23 [error] 16#16: *1 Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.cFUZTwXPKzBwQ26rYlKDEzZgNZychTDnCZnhRszXNyw, client: 172.18.0.1, server: localhost.com, request: "GET /secret-page/ HTTP/2.0", host: "localhost.com"
2019/01/07 08:31:23 [error] 16#16: *1 failed to parse jwt, client: 172.18.0.1, server: localhost.com, request: "GET /secret-page/ HTTP/2.0", host: "localhost.com"
katsar0v commented 5 years ago

I can't generate the token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.tJoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM

Are you able to generate it using the same key? The library works with this key, but with none of mine

katsar0v commented 5 years ago

Okay I succeeded using this key:

katsarov@katsarov-ThinkPad-T470s:~$ php -a
Interactive mode enabled

php > $headers = array('alg' => 'HS256', 'typ' => 'JWT');
php > $payload = array (
php (   'sub' => 'some-long-uuid',
php (   'firstName' => 'hello',
php (   'lastName' => 'world',
php (   'roles' => 
php (   array (
php (     0 => 'this',
php (     1 => 'that',
php (     2 => 'theother',
php (   ),
php (   'iss' => 'issuer',
php (   'personId' => '75bb3cc7-b933-44f0-93c6-147b082fadb5',
php (   'exp' => 1908835200,
php (   'iat' => 1488819600,
php (   'username' => 'hello.world',
php ( );
php > $key = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF";
php > $headers_encoded = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(json_encode($headers)));
php > $payload_encoded = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(json_encode($payload)));
php > $signature = hash_hmac('sha256', $headers_encoded . "." . $payload_encoded, $key, true);
php > $signature_encoded = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
php > $jwt = $headers_encoded . "." . $payload_encoded . "." . $signature_encoded;
php > echo $jwt;
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.tJoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM
katsar0v commented 5 years ago

@benmcollins how do you generate

"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"

In python or PHP?

katsar0v commented 5 years ago

I did it in python:

# 3A98 is hex formatted key
signature = hmac.new(binascii.unhexlify('3A98'), signing_input, hashlib.sha256).digest().replace('=','')
signature_encoded = base64.urlsafe_b64encode(signature).replace(b'=',b'')
benmcollins commented 5 years ago

Good deal. I would definitely pass along to the ngx-http-auth-jwt team that they need to document that auth_jwt_key is expected to be binhex format. This has caused a lot of confusion and lost time on your part, and I can only imagine it would be the same for others.

katsar0v commented 5 years ago

Yes, this is solved. Since JWT is something used in many platforms and languages, this problem might occure more often than we think, thank you again for your help.

fitzyjoe commented 5 years ago

I updated the read-me on the nginx module to clarify.

https://github.com/TeslaGov/ngx-http-auth-jwt-module/blob/master/README.md

On Mon, Jan 7, 2019 at 9:04 AM Kristiyan notifications@github.com wrote:

Yes, this is solved. Since JWT is something used in many platforms and languages, this problem might occure more often than we think, thank you again for your help.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/benmcollins/libjwt/issues/79#issuecomment-451944534, or mute the thread https://github.com/notifications/unsubscribe-auth/AF68miIGelQWNArphocCsnwaUcFZQzFFks5vA1QAgaJpZM4ZqAZX .

benmcollins commented 5 years ago

@fitzyjoe Thanks!

TomaszWojtas commented 1 year ago

Guys, I've read this thread multiple times, but I am still struggling with getting aligned with jwt.io. I am generating JWT using Simple JWT Login for Wordpress (https://wordpress.org/plugins/simple-jwt-login/#description). It returns excatly the same key like jwt.io. My secret is: 24D996622132DA66448F1ECDE4B2B5B4D906AD4A43D1CA0D983E8BD87F3F9F98 Header: { "typ": "JWT", "alg": "HS256" } payload: { "iat": 1669650310, "exp": 1669653910, "email": "jwt_test@gmail.com", "username": "jwt_test" }

so my token is: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2Njk2NTAzMTAsImV4cCI6MTY2OTY1MzkxMCwiZW1haWwiOiJqd3RfdGVzdEBnbWFpbC5jb20iLCJ1c2VybmFtZSI6Imp3dF90ZXN0In0.kWAnL-JZFE1MYKXZHppPZ4mv7ov5zw-iBwUlAGy3bNk

Thanks to @conor-ettinoffe I took my secret into hex bytes and was able to generate token in python locally:

import binascii
import hashlib
import hmac
import json

import secrets
secrets.token_hex(32).upper()

header ='{"typ": "JWT", "alg": "HS256"}'.encode().replace(b' ', b'')
payload = '  "iat": 1669650310, "exp": 1669653910,"email": "jwt_test@gmail.com", "username": "jwt_test"'.encode().replace(b' ', b'')

signing_input = base64.urlsafe_b64encode(header).replace(
    b'=', b'')+b'.'+base64.urlsafe_b64encode(payload).replace(b'=', b'')

signature = hmac.new(binascii.unhexlify('24D996622132DA66448F1ECDE4B2B5B4D906AD4A43D1CA0D983E8BD87F3F9F98'),
                     signing_input, hashlib.sha256).digest().replace(b'=', b'')

signature_encoded = base64.urlsafe_b64encode(signature).replace(b'=', b'')

print((signing_input+b'.'+signature_encoded).decode())

I get: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ImlhdCI6MTY2OTY1MDMxMCwiZXhwIjoxNjY5NjUzOTEwLCJlbWFpbCI6Imp3dF90ZXN0QGdtYWlsLmNvbSIsInVzZXJuYW1lIjoiand0X3Rlc3Qi.LC2VeEqCMy0A_wdYQtlJ72KEJgIXKQoDL2aaU1RnBYY This token works for nginx, but it's diffrent than the previous one. I have tried to pass it in '\x24\xD9\x96\x62\x21\x32\xDA\x66\x44\x8F\x1E\xCD\xE4\xB2\xB5\xB4\xD9\x06\xAD\x4A\x43\xD1\xCA\x0D\x98\x3E\x8B\xD8\x7F\x3F\x9F\x98' format, to jwt.io, but no success.

What to do make them aligned? Either make nginx to accept the first one or generate the second one in jwt.io? @katsar0v @benmcollins @fitzyjoe You seem to understand the stuff much better :)

Sorry if the answer is above, but I still can't find it.

benmcollins commented 1 year ago

The first one shows "Invalid signature" on JWT.io. It's not a valid JWT.

I'm unsure how that second one would work on nginx. I see this with it on JWT.io

Screen Shot 2022-11-28 at 12 01 22 PM

What you've shown is two JWT tokens that are not valid, so they cannot work.

If I take your original header, payload, and key and put it in JWT.io (base64 encode of the key). I get this token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2Njk2NTAzMTAsImV4cCI6MTY2OTY1MzkxMCwiZW1haWwiOiJqd3RfdGVzdEBnbWFpbC5jb20iLCJ1c2VybmFtZSI6Imp3dF90ZXN0In0.Ot83IRpjWixa-bJPhYP2pSCzYtTZtY6imTDdTs2tGHA

I can then plug this into a simple routine and LibJWT validates it no problem:

    const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2Njk2NTAzMTAsImV4cCI6MTY2OTY1MzkxMCwiZW1haWwiOiJqd3RfdGVzdEBnbWFpbC5jb20iLCJ1c2VybmFtZSI6Imp3dF90ZXN0In0.Ot83IRpjWixa-bJPhYP2pSCzYtTZtY6imTDdTs2tGHA";
    // Key in bytes, base64 is: "JNmWYiEy2mZEjx7N5LK1tNkGrUpD0coNmD6L2H8/n5g"
    unsigned char key256[] = { 0x24, 0xD9, 0x96, 0x62, 0x21, 0x32, 0xDA, 0x66,
                               0x44, 0x8F, 0x1E, 0xCD, 0xE4, 0xB2, 0xB5, 0xB4,
                               0xD9, 0x06, 0xAD, 0x4A, 0x43, 0xD1, 0xCA, 0x0D,
                               0x98, 0x3E, 0x8B, 0xD8, 0x7F, 0x3F, 0x9F, 0x98 };
    jwt_t *jwt;
    int ret;

    ret = jwt_decode(&jwt, token, key256, sizeof(key256));
    // ret == 0 here for success
TomaszWojtas commented 1 year ago

@benmcollins thank you for so quick reply!

It's a little surprising, but when I put header, payload and secret I get this: image

Have you got any idea where this discrepancy come from?

benmcollins commented 1 year ago

@TomaszWojtas

This is using your key as a string and not hex. It is also not in base64. See my code above for the base64.