labd / commercetools-python-sdk

Commercetools Python SDK
https://commercetools-python-sdk.readthedocs.io/en/latest/
MIT License
17 stars 16 forks source link

BUG broken auth missing token #70

Closed rosscdh closed 4 years ago

rosscdh commented 5 years ago

commercetools==5.0.0

got valid creds (works with curl) creds are present

always dies *** oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.

seems related to https://github.com/requests/requests-oauthlib/issues/324

same happens with 4.1.0 so i believe its the interface to the oauthlib that has changed

davidweterings commented 5 years ago

We have not seen this issue so far, but our setup is probably a little bit different. Can you provide the code for how you construct the client? I'm curious what the api/auth/token url look like.

rosscdh commented 5 years ago

sure

CTP_PROJECT_KEY=test-project-because-its-broken
CTP_CLIENT_ID=ej****Rr
CTP_CLIENT_SECRET=qJu1***********************UZY
CTP_SCOPE=manage_project:test-project-because-its-broken
CTP_URL=https://api.sphere.io
CTP_TOKEN_URL=https://auth.sphere.io

.env gets loaded 100% right output is the same

from commercetools import Client
from django.conf import settings

#pdb here and i get
> settings.COMMERCETOOLS
{'project_key': 'test-project-because-its-broken', 'client_id': 'ej******Rr', 'client_secret': 'qJu1********UZY', 'scope': 'manage_project:test-project-because-its-broken', 'url': 'https://api.sphere.io', 'token_url': 'https://auth.sphere.io'}

# default to empty, otherwise extract from django.settings
COMMERCETOOLS = getattr(settings, 'COMMERCETOOLS', {
    'project_key': '',
    'client_id': '',
    'client_secret': '',
    'scope': '',
    'url': '',
    'token_url': '',
})

# is present

class CommerceToolsService:
    def __init__(self, *args, **kwargs):
        # use passed in client or just default from settings
        self.client = kwargs.get('client', Client(**COMMERCETOOLS))
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/private/tmp/output/testcommerce/apps/commercetools/services.py", line 13, in <module>
    class CommerceToolsService:
  File "/private/tmp/output/testcommerce/apps/commercetools/services.py", line 14, in CommerceToolsService
    client = Client(**COMMERCETOOLS)
  File "/private/tmp/output/venv/lib/python3.7/site-packages/commercetools/client.py", line 151, in __init__
    client_secret=self._config["client_secret"],
  File "/private/tmp/output/venv/lib/python3.7/site-packages/requests_oauthlib/oauth2_session.py", line 244, in fetch_token
    self._client.parse_request_body_response(r.text, scope=self.scope)
  File "/private/tmp/output/venv/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 411, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
  File "/private/tmp/output/venv/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 379, in parse_token_response
    validate_token_parameters(params)
  File "/private/tmp/output/venv/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 389, in validate_token_parameters
    raise MissingTokenError(description="Missing access token parameter.")
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.
rosscdh commented 5 years ago

same if i just plain use it

assert settings.COMMERCETOOLS.get('project_key'), 'no project_key'
assert settings.COMMERCETOOLS.get('client_id'), 'no client_id'
assert settings.COMMERCETOOLS.get('client_secret'), 'no client_secret'
assert settings.COMMERCETOOLS.get('scope'), 'no scope'
assert settings.COMMERCETOOLS.get('url'), 'no url'
assert settings.COMMERCETOOLS.get('token_url'), 'no token_url'

client = Client(
    project_key=settings.COMMERCETOOLS.get('project_key'),
    client_id=settings.COMMERCETOOLS.get('client_id'),
    client_secret=settings.COMMERCETOOLS.get('client_secret'),
    scope=settings.COMMERCETOOLS.get('scope'),
    url=settings.COMMERCETOOLS.get('url'),
    token_url=settings.COMMERCETOOLS.get('token_url'),
)

Traceback (most recent call last):
  File "<console>", line 7, in <module>
  File "/private/tmp/output/venv/lib/python3.7/site-packages/commercetools/client.py", line 151, in __init__
    client_secret=self._config["client_secret"],
  File "/private/tmp/output/venv/lib/python3.7/site-packages/requests_oauthlib/oauth2_session.py", line 244, in fetch_token
    self._client.parse_request_body_response(r.text, scope=self.scope)
  File "/private/tmp/output/venv/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 411, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
  File "/private/tmp/output/venv/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 379, in parse_token_response
    validate_token_parameters(params)
  File "/private/tmp/output/venv/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 389, in validate_token_parameters
    raise MissingTokenError(description="Missing access token parameter.")
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.
rosscdh commented 5 years ago

appears

        token = self._token_saver.get_token(
            self._config["client_id"], self._config["scope"]
        )

fails to collect a token and subsequent references fail.. perhaps instead of returning None it should throw errors in the oauth2 flow?

rosscdh commented 5 years ago

almost got it

scope=["<scopes>"],

is a list not a string.. will change


    def add_token(self, client_id, scopes, token:list):
        name = self._create_token_hash(client_id, scopes)
        self.storage[name] = token

    def get_token(self, client_id, scopes:list):
        name = self._create_token_hash(client_id, scopes)
        import pdb;pdb.set_trace()
        return self.storage.get(name)

but still getting the same missing token....

rosscdh commented 5 years ago

can you let me know what version of requests-oauthlib you are using?

davidweterings commented 5 years ago

Sure, we use

oauthlib==3.0.1 requests-oauthlib==1.2.0

rosscdh commented 5 years ago

Thanks David, unfortunately no luck so far

None of the following work, even taken directly from the docs fails with the same error..

I dont get how this works for you?

 pip install oauthlib==3.0.1 requests-oauthlib==1.2.0 commercetools==5.0.0 -U

        service = CommerceToolsService(**COMMERCETOOLS)

        service = CommerceToolsService(
            project_key=COMMERCETOOLS.get('project_key'),
            client_id=COMMERCETOOLS.get('client_id'),
            client_secret=COMMERCETOOLS.get('client_secret'),
            scope=COMMERCETOOLS.get('scope'),
            url=COMMERCETOOLS.get('url'),
            token_url=COMMERCETOOLS.get('token_url'),
        )

        service = Client(
            project_key='test-project-because-its-broken',
            client_id='ej2******Rr',
            client_secret='qJ****UZY',
            scope=['manage_project:test-project-because-its-broken'],
            url='https://api.sphere.io',
            token_url='https://auth.sphere.io',
        )
rosscdh commented 5 years ago

Alarmingly neither does the requests-oauth lib seem to work

        client_id = COMMERCETOOLS.get('client_id')
        client_secret = COMMERCETOOLS.get('client_secret')
        token_url = COMMERCETOOLS.get('token_url')
        scope =  COMMERCETOOLS.get('scope')
        auth = HTTPBasicAuth(client_id,
                             client_secret)
        client = BackendApplicationClient(client_id=client_id)
        oauth = OAuth2Session(client=client)
        token = oauth.fetch_token(token_url=token_url, auth=auth)

        client = BackendApplicationClient(client_id=client_id, scope=scope)
        oauth = OAuth2Session(client=client)
        token = oauth.fetch_token(token_url=token_url, client_id=client_id, client_secret=client_secret, include_client_id=True)

same errors..

davidweterings commented 5 years ago

At least I can reproduce it now :) we generally configure the client with environment variables so that's why we don't encounter it.

davidweterings commented 5 years ago

@rosscdh it's probably because your token url is incorrect, as per the (perhaps not so intuitive) docs its supposed to be: token_url="https://auth.sphere.io/oauth/token"

rosscdh commented 5 years ago

hmm i copy pasted from commercetools? ok ill try that thanks

rosscdh commented 5 years ago

omg.. gonna file a ticket with commercetools.. .seriously....

Thanks mate appreciate it.

davidweterings commented 5 years ago

@rosscdh I'm afraid the fault lies more with the Python implementation! We do some unexpected logic with the API urls. I think we should change it so token_url passed without oauth/token in the path should also work. I'll see if I can change that next week.

rosscdh commented 5 years ago

no problem that sounds good; glad to have helped a bit.

sorry for the bombardments but it was really weird :)

I see you guys are django devs (great) are you aware of wagtail ? am looking at building a poc for commercetools with wagtail in mind (cough https://github.com/douglas-treadwell/marshmallow-models)

rosscdh commented 5 years ago

What would be really really nice is some kind of hydration hook that is exposed just post marsmallow hydration; so that for example in django i could extract the master_data.current and apply a language map to it and thus have a clean {{ product }} object that is current and output at the right language relative to the viewer.. is this on your cards?

davidweterings commented 5 years ago

I'm afraid we have quite a backlog, we're also working on other Commercetools related functionality (https://github.com/labd/commercetools-go-sdk and more importantly https://github.com/labd/terraform-provider-commercetools) which requires a bit more attention from us. If it was something we would also need in our projects it would make it easier to give attention.

We do welcome contributions, I'm sure such a feature would be appreciated. You could implement hooks like wagtail does; https://github.com/wagtail/wagtail/blob/882f8f3cf8ddd79c30e611a48882b309e90dad0c/wagtail/core/hooks.py and then change the code generating code which creates the hook: https://github.com/labd/commercetools-python-sdk/blob/7f7a193fdaea3d632bdda662127e68f60995af40/codegen/generate_schema.py#L228

pimvernooij commented 5 years ago

Hi @rosscdh,

Actually several members of our team are core contributors to Wagtail, and we implement a lot of cms use cases using it!

And we have a full blown boilerplate implementation of django/wagtail/commercetools :)

Best, Pim

rosscdh commented 5 years ago

Thanks for the feedback @davidweterings yep ill take a look at that implementation (which is pretty much what i had in mind)

@pimvernooij very nice, and great to hear i also discovered wagtail when it first started and was immediately a huge fan due to its django and implementation not relying on lookups perfect add to that wagtail-baker and its a great solution for a range of customers.

Sounds like a good toolset and you guys made the correct logic steps and great to hear that the commercetool implementation is already there; has it been used commercially where i can see the site? of course i realise its probably not OSS so understand if that info is not available.

rosscdh commented 5 years ago

The TF implementation looks just about the right thing :) really great work guys

pimvernooij commented 5 years ago

Hi @rosscdh, I've sent you a linkedin invite - we could jump on a call and I can demo some things :)

rosscdh commented 5 years ago

Thanks ill try free some time up to dedicate to the demo!

rosscdh commented 5 years ago

@pimvernooij dont seem to have received an LN invite?

pimvernooij commented 5 years ago

Strange, 'pending' is the status of the invite with me:) Perhaps you can invite me? https://www.linkedin.com/in/pimvernooij/