Internal server error can not convert none type to str implicitly (From apache2 error log)

Hi Guys,
I am facing problem with my API after i have deployed on my Ubuntu-VM, I don’t know where i am missing please guide me to the correct solution.

Question - I have created my API in python with flask, I configured my API with auth0 according to this tutorial - Auth0 Python API SDK Quickstarts: Authorization. When i am running this API locally(flask default WSGI server) it’s working perfectly fine but when i deployed on my VM and API is running on WSGI server under apache server. I am getting internal server error when i went to error.log in apache i got this error →

" jsonurl = urlopen (“https://”+AUTH0_DOMAIN+"/.well-known/jwks.json ")
“TypeError: Can’t convert ‘NoneType’ object to str implicitly”

This error is from the python code that i added from above tutorial. In my server configuration file i have added this line also WSGIPassAuthorization On but still error is same. I have tried lot of things(like Rewrite in apache config) but none of them is working. Please suggest me solution.

Thanks

1 Like

If I had to guess I would say urlopen is returning None and that causes the error, while the reason the function return None could be that AUTH0_DOMAIN does not have the value you expect when you deploy which in turn makes the overall URL invalid.

Hi Jmangelo,

When i run same application with my flask inbuilt server everything is working fine with same domain. Only when i run the same thing on Apache server it returning me above mentioned error.

Yes, I understood that from your initial message, but that does not exclude the possibility above. In other words, when running locally AUTH0_DOMAIN is defined correctly when you run the application, but when you run it in the other context that is no longer true.

Have you confirmed for sure that that variable has the value you would expect?

Yes, I have tried hard coded value(auth domain) instead of getting from some where else but still i am getting the same internal error from server side.

Yes, I have tried hard coded value(auth domain) instead of getting from some where else but still i am getting the same internal error from server side.

Can you actually hardcode the value in line

jsonurl = urlopen (“https://”+AUTH0_DOMAIN+"/.well-known/jwks.json ")

so, instead of using the AUTH0_DOMAIN variable there, just hardcode the value. (So not just hardcoding it in line 19: AUTH0_DOMAIN = env.get("AUTH0_DOMAIN")) Just to see if that makes a difference.

Okay, what you told just now i tried but i got same error as internal server error. When i went in apache error log jwks = json.loads(jsonurl.read()) File "/usr/lib/python3.5/json/__init__.py", line 312, in loads s.__class__.__name__)) TypeError: the JSON object must be str, not 'bytes' this is what i am getting now. I think this also same error the response i am getting is not proper.

@Sarvesh Can you please post the entire source code for the section so we can see it as a whole. You can rename the actual tenant name to not make it publicly known.

I can’t reproduce the issue you’re getting, but you might give it a try with:

jwks = json.loads(jsonurl.readline().decode('utf-8'))

instead of

jwks = json.loads(jsonurl.read())

Anyhow, please post the code snippet of the entire section of yours. By the way: the error message you got now is a different one than the initial one, it refers to a different line in the code.

jwks = json.loads(jsonurl.readline().decode('utf-8'))

Above method i tried but still getting the same error that i got in initial stage
Error - jsonurl = urlopen ("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json") TypeError: Can't convert 'NoneType' object to str implicitly

This is my code snippet that i am using for my APIs. From this file i am calling my require_auth decorator for my routes that i have created for my APIs. This below snippet i got from auth0 official website in get started section with python. Please guide me if i am doing something wrong anywhere !

import json, os
from six.moves.urllib.request import urlopen
from functools import wraps
from flask import request, jsonify, _request_ctx_stack
from jose import jwt

AUTH0_DOMAIN = os.environ.get('MY_DOMAIN')
API_AUDIENCE = os.environ.get('MY_AUDIENCE')
ALGORITHMS = ["RS256"]


# Error handler
class AuthError(Exception):
    def __init__(self, error, status_code):
        self.error = error
        self.status_code = status_code


# Format error response and append status code
def get_token_auth_header():
    """Obtains the Access Token from the Authorization Header
    """
    auth = request.headers.get("Authorization", None)
    if not auth:
        raise AuthError({"code": "authorization_header_missing",
                        "description":
                            "Authorization header is expected"}, 401)

    parts = auth.split()

    if parts[0].lower() != "bearer":
        raise AuthError({"code": "invalid_header",
                        "description":
                            "Authorization header must start with"
                            " Bearer"}, 401)
    elif len(parts) == 1:
        raise AuthError({"code": "invalid_header",
                        "description": "Token not found"}, 401)
    elif len(parts) > 2:
        raise AuthError({"code": "invalid_header",
                        "description":
                            "Authorization header must be"
                            " Bearer token"}, 401)

    token = parts[1]
    return token

    def requires_auth(f):
        """Determines if the Access Token is valid
        """
        @wraps(f)
        def decorated(*args, **kwargs):
            token = get_token_auth_header()
            jsonurl = urlopen("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json")
            jwks = json.loads(jsonurl.read())
            unverified_header = jwt.get_unverified_header(token)
            rsa_key = {}
            for key in jwks["keys"]:
                if key["kid"] == unverified_header["kid"]:
                    rsa_key = {
                        "kty": key["kty"],
                        "kid": key["kid"],
                        "use": key["use"],
                        "n": key["n"],
                        "e": key["e"]
                    }
            if rsa_key:
                try:
                    payload = jwt.decode(
                        token,
                        rsa_key,
                        algorithms=ALGORITHMS,
                        audience=API_AUDIENCE,
                        issuer="https://"+AUTH0_DOMAIN+"/"
                    )
                except jwt.ExpiredSignatureError:
                    raise AuthError({"code": "token_expired",
                                    "description": "token is expired"}, 401)
                except jwt.JWTClaimsError:
                    raise AuthError({"code": "invalid_claims",
                                    "description":
                                        "incorrect claims,"
                                        "please check the audience and issuer"}, 401)
                except Exception:
                    raise AuthError({"code": "invalid_header",
                                    "description":
                                        "Unable to parse authentication"
                                        " token."}, 401)

            _request_ctx_stack.top.current_user = payload
            return f(*args, **kwargs)
        raise AuthError({"code": "invalid_header",
                        "description": "Unable to find appropriate key"}, 401)
    return decorated


    def requires_scope(required_scope):
        """Determines if the required scope is present in the Access Token
        Args:
            required_scope (str): The scope required to access the resource
        """
        token = get_token_auth_header()
        unverified_claims = jwt.get_unverified_claims(token)
        if unverified_claims.get("scope"):
                token_scopes = unverified_claims["scope"].split()
                for token_scope in token_scopes:
                    if token_scope == required_scope:
                        return True
        return False

Please keep BOTH changes mentioned in this thread. First, please keep the AUTH_DOMAIN value hardcoded instead of using the variable. Then also keep this

jwks = json.loads(jsonurl.readline().decode('utf-8'))

otherwise you keep jumping between the first and the second error.

So it should look like this (replace YOURTENANT with your Auth0 tenant/domain name).

            jsonurl = urlopen("https://YOURTENANT.auth0.com/.well-known/jwks.json")
            jwks = json.loads(jsonurl.readline().decode('utf-8'))

I tried with both changes hardcoded value of auth_domain and
jwks = json.loads(jsonurl.readline().decode('utf-8'))
above method. Now i got (401 UNAUTHORIZED) from postman with body
{"code": "invalid_header",
"description": "Unable to parse authentication token."}
also in apache error log i got
WARNING:flask_cors.core:Unknown option passed to Flask-CORS: headers

@Sarvesh Make sure that the access token is a valid base64url encoded JWT. You can test that by going to jwt.io and pasting the encoded JWT in there. If it’s valid, it should be decoded without problems. If not, there’s something wrong with the token.

Where are you getting the token from that you’re passing to your API? And to be sure: it’s an access and not an ID token, and it’s a JWT format?

The access token that i am using is correct, because i copied it from auth0 API test page itself. I also tested on jwt.io it perfectly fine.
See if i run my API from flask directly it works perfectly without any trouble, but when i run same API from Apache under WSGI server it giving above, mentioned error.
I think some configuration is there that i am missing or something else i don’t know !

1 Like