cjm
cjm

Reputation: 844

A JSON web token could not be decoded

I'm attempting to port the GitHub Apps sample starter code from Ruby to Python, but I'm running into trouble whilst generating the required JWT. The Ruby script looks like this, and works fine:

require 'openssl'
require 'jwt'  # https://rubygems.org/gems/jwt

private_pem = File.read(YOUR_PATH_TO_PEM)
private_key = OpenSSL::PKey::RSA.new(private_pem)

payload = {
  iat: Time.now.to_i,
  exp: Time.now.to_i + (10 * 60),
  iss: GITHUB_APP_IDENTIFIER
}

jwt = JWT.encode(payload, private_key, "RS256")
puts jwt

My Python script is as follows and produces the A JSON web token could not be decoded error when used against the GitHub API:

import os
import time
import jwt

APP_IDENTIFIER = os.environ["GITHUB_APP_IDENTIFIER"]

with open('./PRIVATE_KEY.pem', 'r') as f:
    PRIVATE_KEY = f.read()

payload = {"iat": int(time.time()),
           "exp": int(time.time()) + (10*60),
           "iss": APP_IDENTIFIER}

print(jwt.encode(payload, PRIVATE_KEY, algorithm='RS256'))

When I tried printing the private keys from both scripts, I found that the Ruby version had an extra newline character. I tried adding this to the private key in the Python script, but it didn't change the output.

My best guess is that the difference is something to do with the OpenSSL::PKey::RSA.new call, but I'm not sure what that does to the key.

Upvotes: 4

Views: 9975

Answers (1)

cjm
cjm

Reputation: 844

jwt.encode() will return you bytes in Python 3 which probably ends up having str() called on it somewhere in the sending pipeline. Calling str() on bytes objects in Python 3 can result in potentially surprising behaviour:

>>> a = b'hello'
>>> str(a)
"b'hello'"

The correct way to turn bytes into a string in Python 3 is to use:

>>> a.decode('utf-8')
'hello'

I added the call to decode on the end of my jwt.encode line and the API suddenly had no problems decoding the JWT.

Upvotes: 4

Related Questions