[Python] [ESI] 401 Error When Accessing ESI

Hi There,
Hoping I could get some help with authentication. I’m pretty new to coding. As a bit of background:

  • I copied this approach (postman to get the refresh token) in Google sheets and still works today
  • I’m trying to build out further and I am trying to recreate the request in python (below)
  • I’m following the web app instructions, this is being built with Flask
def get_access_token():
    url = 'https://login.eveonline.com/v2/oauth/token'
    call_time = datetime.datetime.now()
    response = requests.post(
        url,
        params={"grant_type":"refresh_token", 'refresh_token': os.environ['REFRESH_TOKEN']},
        headers={'Content-Type': 'application/x-www-form-urlencoded', 
                'Authorization': 'Basic '+ os.environ['CLIENT_ID']+':'+os.environ['CLIENT_SECRET']}
    )
# response.statuscode == 401 at this point
# below is moot, we dont get that far, I'm including it to show that 
# I've been trying to update 'REFRESH_TOKEN' if they send
# a different one back

    os.environ['TOKEN_EXPIRY'] = call_time + datetime.timedelta(seconds=response['expires_in'])
    os.environ['REFRESH_TOKEN'] = response['refresh_token']

    return response['access_token']
# --> TypeError: 'Response' object is not subscriptable

Excuse the abuse of os.environ for now please…

Anyone able to spot where I’m going wrong? Or is there something I can do to get a more descriptive error?

I have tried:

  • Adding and removing the 'Host':'login.eveonline.com' header
  • Removing the params parameter and constructing the url
  • hard-coding the keys/IDs instead of using system variables

Can you also share the response body you get back when it 401s? That will probably have some information in it that will narrow down the problem.

EDIT: You also need to base64 encode your client id and client secret as part of the Basic auth header. See Refreshing tokens | esi-docs. So that’s probably your problem.

1 Like

Thanks for taking a look. I’ve refactored slightly to get the B64 encoding:

def get_access_token():
    url = 'https://login.eveonline.com/v2/oauth/token'
    call_time = datetime.datetime.now()
    client_id = os.environ['CLIENT_ID']
    client_secret = os.environ['CLIENT_SECRET']
    response = requests.post(
        url,
        params={"grant_type":"refresh_token", 'refresh_token': os.environ['REFRESH_TOKEN']},
        headers={'Content-Type': 'application/x-www-form-urlencoded', 
                'Host':'login.eveonline.com'},
        auth=HTTPBasicAuth(client_id, client_secret))
    return response.content

# --> b'{"error":"invalid_grant","error_description":"Grant type is not supported."}'

I’ve also done a longer version which generates the auth string and then encodes in b64 and adds to the header, instead of using auth=HTTPBasicAuth(client_id, client_secret)), which throws the same error.

Aha!

Swapped params to data in the requests.post() method and it worked (along with the b64 change). Thank you so much!

Edit: final version for anyone looking in the future:

def get_access_token():
    url = 'https://login.eveonline.com/v2/oauth/token'
    call_time = datetime.datetime.now()
    client_id = os.environ['CLIENT_ID']
    client_secret = os.environ['CLIENT_SECRET']
    response = requests.post(
        url,
        data={'grant_type':'refresh_token', 'refresh_token': os.environ['REFRESH_TOKEN']},
        headers={'Content-Type': 'application/x-www-form-urlencoded', 
                'Host':'login.eveonline.com'},
        auth=HTTPBasicAuth(client_id, client_secret))
    return response.content

This returns a JSON exactly as per the docs:

{
  "access_token":"MXP...tg2",
  "token_type":"Bearer",
  "expires_in":1200,
  "refresh_token":"gEy...fM0"
}
1 Like