6

Trying to generate a JWT token using the PyJWT library.

When I use the below program to generate the JWT token - the token does not work.

However when I use the website https://jwt.io/ with the same details - the token works. Is there something that I'm missing.

I need python generate the token properly so I can automate some API's that need this token.

Python program:

import jwt
import base64

code = jwt.encode({'sub':'String','nbf':'1501594247',
                   'exp':'1501767047', 'iss': 'string', 'aud': 'String'},
       base64.b64encode('secret'), algorithm='HS256')
print code

Example:

code = jwt.encode({'sub':'AccountNUmber.QTVR','nbf':'1501594247','exp':'1501860089', 'iss': 'client_id', 'aud': 'https://login.google.com/oauth'},
                  base64.b64encode('secret'), algorithm='HS256')

Result:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbGllbnRfaWQiLCJhdWQiOiJodHRwczovL2xvZ2luLmdvb2dsZS5jb20vb2F1dGgiLCJzdWIiOiJBY2NvdW50TlVtYmVyLlFUVlIiLCJleHAiOiIxNTAxODYwMDg5IiwibmJmIjoiMTUwMTU5NDI0NyJ9.dRUUQYJ-RmxgoExwPyrvHPzX9SsxcpX1rOWlhisxNsg

Token generated by https://jwt.io/ :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbGllbnRfaWQiLCJhdWQiOiJodHRwczovL2xvZ2luLmdvb2dsZS5jb20vb2F1dGgiLCJzdWIiOiJBY2NvdW50TlVtYmVyLlFUVlIiLCJleHAiOiIxNTAxODYwMDg5IiwibmJmIjoiMTUwMTU5NDI0NyJ9.INp-ZnnL8Uj7MIwLYmpZtGyTyZG-oqZRNW8iZ145jVs

The token generated by https://jwt.io/ works when I call the endpoint. And I get a status code 200 (Success).

However when I use the token generated from my program it gives 'Invalid Token' - 400 (Bad Request).

jps
  • 20,041
  • 15
  • 75
  • 79
Michael Eltham
  • 107
  • 1
  • 5
  • What exactly do you mean with 'does not work'? What kind of error do you get? Can you pls. add the generated token to your question – jps Aug 03 '17 at 08:02
  • jps thanks for taking the time to reply. Have added an example and answer to question 'does not work' above. Let me know if I was clear enough. If not will try to explain more. – Michael Eltham Aug 03 '17 at 15:32
  • I think I figured out how it works. What still confuses me is that your jwt.io generated token above works. Please let me know if my answer works for you. – jps Aug 04 '17 at 07:55

2 Answers2

8

The error here is the base64.b64encodein your python statement. You are not supposed to encode the secret before you pass it to jwt.encode. I think the jwt.io debugger is a bit misleading in this regard:

enter image description here

The idea here is, that you have a secret that is stored in base64 encoded format somewhere and the jwt.io debugger will decode your secret when you tell him that it is encoded.

As you can check easily in idle:

>>> base64.b64encode('secret')
'c2VjcmV0'

And if you paste the encoded secret to the secret field on jwt.io and check the checkbox as shown in the picture above, you'll get the following token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbGllbnRfaWQiLCJhdWQiOiJodHRwczovL2xvZ2luLmdvb2dsZS5jb20vb2F1dGgiLCJzdWIiOiJBY2NvdW50TlVtYmVyLlFUVlIiLCJleHAiOiIxNTAxODYwMDg5IiwibmJmIjoiMTUwMTU5NDI0NyJ9.Nk6oCKBlT9lh4rtJZzF8JuyEsiH_HBaa3UhUx3DbWeI

which is identical to the output of the follwing python programs:

a) when you provide a secret directly, not encoded:

code = jwt.encode({'sub':'AccountNUmber.QTVR','nbf':'1501594247','exp':'1501860089', 'iss': 'client_id', 'aud': 'https://login.google.com/oauth'}, 
                  'secret', algorithm='HS256')

b) when you provide an encoded secret:

code = jwt.encode({'sub':'AccountNUmber.QTVR','nbf':'1501594247','exp':'1501860089', 'iss': 'client_id', 'aud': 'https://login.google.com/oauth'},
                  base64.b64decode('c2VjcmV0'), algorithm='HS256')

This should work.

jps
  • 20,041
  • 15
  • 75
  • 79
  • jps, Thanks for reply. However it still didn't crack it.. The endpoint expects a Base64 encoded version of the Password. So your method 1: direct secret 2. decode the encoded version of 'secret'. Both does not work.. Do you know if the jet.encode -- does any encoding on the 'secret' – Michael Eltham Aug 08 '17 at 14:39
  • as shown above, the jwt.encode does no encoding or decoding on the secret. If you provide a plain text secret as in a) you get the same result as on jwt.io when you paste the plain text secret there. And if you provide an encoded secret you have to decode it before you pass it to jwt.encode, again same result as on jwt.io. I guess ( I don't know your endpoint) that the error is on the endpoint side. Probably a similar problem regarding the handling of encoded or unencoded secrets. Pls. read my answer carefully and also provide more information in your question. – jps Aug 08 '17 at 20:17
  • 1
    have you been able to solve it? Would be fair to get an update after I invested some time to help. – jps Aug 18 '17 at 07:34
0

there is an error in documentation, I checked the source code of this library

class PyJWT(PyJWS):
    header_type = 'JWT'

    @staticmethod
    def _get_default_options():
        # type: () -> Dict[str, bool]
        return {
            'verify_signature': True,
            'verify_exp': True,
            'verify_nbf': True,
            'verify_iat': True,
            'verify_aud': True,
            'verify_iss': True,
            'require_exp': False,
            'require_iat': False,
            'require_nbf': False
        }

    def encode(self,
               payload,  # type: Union[Dict, bytes]
               key,  # type: str
               algorithm='HS256',  # type: str
               headers=None,  # type: Optional[Dict]
               json_encoder=None  # type: Optional[Callable]
               ):
        # Check that we get a mapping
        if not isinstance(payload, Mapping):
            raise TypeError('Expecting a mapping object, as JWT only supports '
                            'JSON objects as payloads.')

        # Payload
        for time_claim in ['exp', 'iat', 'nbf']:
            # Convert datetime to a intDate value in known time-format claims
            if isinstance(payload.get(time_claim), datetime):
                payload[time_claim] = timegm(payload[time_claim].utctimetuple())  # type: ignore

        json_payload = json.dumps(
            payload,
            separators=(',', ':'),
            cls=json_encoder
        ).encode('utf-8')

        return super(PyJWT, self).encode(
            json_payload, key, algorithm, headers, json_encoder
        )

So the solution be will

from jwt.api_jwt import PyJWT


payload = {
    'id': 5,
    'email': 'ASDASDA'
}

key = 'secret'

jwt_Obj=PyJWT()
jwt_token = jwt_Obj.encode( payload=payload,key= key )
decode_token=jwt_Obj.decode(jwt_token,key=key)

print (jwt_token)
print(decode_token)

Output should or (must) be b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6IkFTREFTREEiLCJpZCI6NX0.-Woz4Crgi5KZj_D99eGaKxY4VxosY7r_e7HTD9JaDqE'

{'email': 'ASDASDA', 'id': 5}

user name
  • 165
  • 1
  • 8