martyzz1 / heroku3.py

This is the updated Python wrapper for the Heroku API V3. https://devcenter.heroku.com/articles/platform-api-reference The Heroku REST API allows Heroku users to manage their accounts, applications, addons, and other aspects related to Heroku. It allows you to easily utilize the Heroku platform from your applications.
Other
118 stars 73 forks source link

Authorization Fails using Token #76

Open garhiggins opened 5 years ago

garhiggins commented 5 years ago

We used to authenticate using:

auth_token = subprocess.check_output(['heroku', 'auth:token'])
heroku = heroku3.from_key(auth_token)

This started failing recently (maybe a month ago?). Digging in, it seems like Heroku only supports a Bearer <Token> Authorization header (https://devcenter.heroku.com/articles/platform-api-quickstart#authentication). I don't see any reference to a Basic Authorization header - My best guess is that it was supported, but has been deprecated?

I was able to work around this and successfully authenticate with the following bit of code:

class BearerAuth(requests.auth.AuthBase):
    """ Token based authentication """

    def __init__(self, token):
        self.token = token

    def __eq__(self, other):
        return self.token == getattr(other, 'token', None)

    def __ne__(self, other):
        return not self == other

    def __call__(self, r):
        r.headers['Authorization'] = 'Bearer %s' % self.token
        return r

class Heroku31(heroku3.api.Heroku):
    """ Monkey patched Heroku3 - Heroku doesn't accept Basic Auth anymore """

    def authenticate(self, api_key):
        """Logs user into Heroku with given api_key."""
        self._api_key = api_key

        # Attach auth to session.
        self._session.auth = BearerAuth(self._api_key)

        return self._verify_api_key()

I'm kind of surprised nobody else has run into this - so, maybe we were just doing something wrong from the start?

martyzz1 commented 5 years ago

I'm not sure your use case is supported.. https://devcenter.heroku.com/articles/platform-api-quickstart#calling-the-api

You should instantiate your connection using your api-key. I would suggest storing this in an environment variable. And then invoking your heroku connection like so:-

  import heroku3
  heroku_conn = heroku3.from_key('YOUR_API_KEY')
martyzz1 commented 5 years ago

I just ran the example.py code here and it worked fine for me..

https://github.com/martyzz1/heroku3.py/blob/master/heroku3/examples.py

I'm wondering if the issue is your api key has been deleted/expired within heroku... (although to be fair I'm still using an old one....

I'll leave this issue open for a bit in case others are experiencing issues... I've had a quick look at the heroku changelog https://devcenter.heroku.com/changelog And there's nothing in there I can spot which looks tgo have deprecated API key support...

garhiggins commented 5 years ago

Thanks for the fast follow-up!

Are you saying that Heroku's API doesn't support our use case, or that heroku3 lib doesn't? I believe Heroku does -- the Authentication section in the guide you linked says that you can obtain your current token via heroku auth:token. And then in the Calling the API section says that the header should be passed as follows: Authorization: Bearer %HEROKU_API_KEY% The heroku3 lib isn't actually doing this - it's passing a Basic Authentication header (I don't know where or if it's documented that Heroku supports this). I was actually a bit surprised it worked.

I prefer our current strategy because the script can piggy-back off the user's existing credentials (using heroku auth:token) - there's no extra setup like creating an api token or setting environment variables.

martyzz1 commented 5 years ago

Many apologies for the slow response, I've been off on a new project.

I've been looking into this for a while and I can't explain it either. I think you are right in that Heroku quietly supports the header without the "Bearer" scheme declared.

Their old documentation seems to conflict with itself. The introductory paragraph talks about using an Authorization header with a value of ":$apikey" but then goes on to specify Authorization: Bearer $apikey

https://web.archive.org/web/20160605043359/https://devcenter.heroku.com/articles/platform-api-quickstart#authentication

All this being said, it should be using the Bearer Authorization header correctly...

If you pushed your suggested changes - which align well with the accepted way to implement this in the requests library - as a PR then I could probably look at getting it merged.