freakboy3742 / pyxero

Python API for accessing the REST API of the Xero accounting tool.
BSD 3-Clause "New" or "Revised" License
279 stars 209 forks source link

Issue with OAuth2 Scope implementation (Scope has changes from..) #287

Open iamsarthakjoshi opened 4 years ago

iamsarthakjoshi commented 4 years ago

I don't understand this problem. I haven't tried changing the scopes in the process, but while verifying with request_uri (after the two-factor authentication) the problem appears every time saying "Scope has changed from...to ..."

image

    def start_xero_auth_view(self, user_key):
        # Get client_id, client_secret from config file or settings then
        credentials = OAuth2Credentials(
            XeroAuth2.client_id, XeroAuth2.client_secret, callback_uri=XeroAuth2.callback_uri,
            scope=[XeroScopes.OFFLINE_ACCESS, XeroScopes.ACCOUNTING_CONTACTS, XeroScopes.ACCOUNTING_TRANSACTIONS]
        )
        authorization_url = credentials.generate_url()
        XeroAuth2.__caches['xero_creds'] = credentials.state
        return authorization_url
    def process_xero_oauth_callback(self, request_uri, auth_code):
        cred_state = XeroAuth2.__caches.get('xero_creds')
        credentials = OAuth2Credentials(**cred_state)
        credentials.verify(request_uri)
        credentials.set_default_tenant()
        XeroAuth2.__caches['xero_creds'] = credentials.state
        return True

Any help is kindly appreciated. :)

Thank you.

sdolemelipone commented 4 years ago

The scope appears to be reverting to the default scope between the first and second view. Is there a possibility the cache is being changed by another part of your code in-between?

The results are consistent with scope not being passed into OAuth2Credentials.__init__() the second time around.

deoresheetal commented 4 years ago

I have the same problem. Whereas it also gives me the access_tokens and refresh_tokens

deoresheetal commented 4 years ago

@iamsarthakjoshi I resolved it with creating new credentials object in callback instead of passing old state

antlhuede commented 3 years ago

I am having the same issue, however the OAuth2Credentials scope is being defined correctly. When I go to verify it says the scope has changed to the same list of scopes provided by OP. After inspecting the value of the scope after construction, it is set to the same as it was in the start_auth_view.

The scope in both instances (start_auth_view and callback) is: ['offline_access', 'accounting.contacts.read', 'accounting.transactions.read', 'accounting.attachments.read']

However it gives the following error: Scope has changed from "accounting.transactions.read offline_access accounting.attachments.read accounting.contacts.read" to "accounting.attachments.read openid accounting.transactions.read offline_access accounting.journals.read accounting.reports.read accounting.contacts.read accounting.transactions projects accounting.attachments profile accounting.settings accounting.settings.read email accounting.contacts assets".

If you need any more contextual information please let me know.

Any help in resolving this other than drastically increasing the requested scope would be appreciated.

antlhuede commented 3 years ago

I have figured out the issue.

The application you are connecting to has previously granted the user a much larger scope than you are now requesting.

Xero does not reset the scope history of an account / application relationship when disconnecting that application from the organisation. As a result, the only way to reduce the scope granted by an application to a particular account is to delete the application from the Xero Developer portal and link to a new one. The Xero Support team told me that scopes are accumulative and if you have ever requested a larger scope previously it will always return that scope in the access token being granted.

Considering this, the only way to receive the exact scope you are expecting is to:

  1. Delete the application in the Xero Developer / My Apps page
  2. Create a new application in Xero Developer / My Apps
    • This will have to also have the correct redirect uri, etc
  3. Update your project to refer to the new application Client Id / Secret

Once you have done this, the scope you receive will be as expected.

If you don't mind that the scope has changed during the request, you can add the following to your settings.py file: os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = 'True'

This will tell the OAuth requests lib to not care about a changing scope during the request.

sdolemelipone commented 3 years ago

Thanks for sharing the solution!

antlhuede commented 3 years ago

Thanks for sharing the solution!

Glad the solution could help, I've been on and off trying to figure this out for a while, hopefully it will save others time in the future

schinckel commented 3 years ago

Is there something we can do in PyXero to make this "handled" better?

(Or is this resolved and I can close it?)

antlhuede commented 3 years ago

Is there something we can do in PyXero to make this "handled" better?

(Or is this resolved and I can close it?)

I think it's resolved as there is nothing you can really do on your end, other than maybe providing more information on this particular problem in your docs?