Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions docs/user-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ Step 1: Instantiate AuthClient object

Valid values for environment include `sandbox` and `production`. `redirect_uri` should be set in your Intuit Developer app's Keys tab under the right environment.

Track refresh token hard expiry (x_refresh_token_hard_expires_in) on both token and refresh responses.

To opt in, set `include_refresh_token_hard_expires_in` on the client to True::

auth_client.include_refresh_token_hard_expires_in = True

Step 2: Get Authorization URL
+++++++++++++++++++++++++++++

Expand All @@ -27,14 +33,16 @@ Get authorization url by specifying list of `intuitlib.enums.Scopes` ::
After user connects to the app, the callback URL has params for `state`, `auth_code` and `realm_id` (`realm_id` for Accounting and Payments scopes only)

Step 3: Get Tokens and Expiry details
++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++

The `auth_code` from URL params from Step 2 is used to get bearer tokens. Optionally, `realm_id` is passed to set this property for `auth_client` object. ::

auth_client.get_bearer_token(auth_code, realm_id=realm_id)

After successful response, `access_token`, `refresh_token`, etc properties of `auth_client` object are set.

After successful response, `access_token`, `refresh_token`, `expires_in`, `x_refresh_token_expires_in`, `x_refresh_token_hard_expires_in`,
etc properties of `auth_client` object are set.


Step 4 (OAuth): Sample API Call
+++++++++++++++++++++++++++++++

Expand Down Expand Up @@ -71,6 +79,8 @@ Or by passing the `refresh_token` as a parameter: ::

auth_client.refresh(refresh_token='EnterRefreshTokenHere')

If you opted in in Step 1, `refresh()` also returns `x_refresh_token_hard_expires_in` when the server provides it.

Revoke Tokens
-------------

Expand Down Expand Up @@ -110,7 +120,3 @@ In case of HTTP Errors, the client raises `intuitlib.exceptions.AuthClientError`
print(e.status_code)
print(e.content)
print(e.intuit_tid)




46 changes: 45 additions & 1 deletion intuitlib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
except (ModuleNotFoundError, ImportError):
from future.moves.urllib.parse import urlencode

from intuitlib.config import INCLUDE_REFRESH_TOKEN_HARD_EXPIRES_IN_HEADER
from intuitlib.utils import (
get_discovery_doc,
generate_token,
Expand Down Expand Up @@ -69,8 +70,45 @@ def __init__(self, client_id, client_secret, redirect_uri, environment, state_to
self.expires_in = None
self.refresh_token = refresh_token
self.x_refresh_token_expires_in = None
self.x_refresh_token_hard_expires_in = None
self.id_token = id_token

# When True, token-endpoint requests include the x-include-refresh-token-hard-expires-in header so the response carries
# x_refresh_token_hard_expires_in (absolute refresh-token lifespan).
self._include_refresh_token_hard_expires_in = False

@property
def include_refresh_token_hard_expires_in(self):
"""Whether token-endpoint requests send the hard-expires opt-in header.

When True, every subsequent get_bearer_token/refresh call sends
`x-include-refresh-token-hard-expires-in`. The parsed value is available
on `auth_client.x_refresh_token_hard_expires_in`.
"""
return self._include_refresh_token_hard_expires_in

@include_refresh_token_hard_expires_in.setter
def include_refresh_token_hard_expires_in(self, enabled):
if isinstance(enabled, bool):
self._include_refresh_token_hard_expires_in = enabled
elif isinstance(enabled, str):
normalized = enabled.strip().lower()
if normalized == 'true':
self._include_refresh_token_hard_expires_in = True
else:
self._include_refresh_token_hard_expires_in = False
else:
self._include_refresh_token_hard_expires_in = False
return self

def _apply_refresh_token_hard_expires_in_header(self, headers):
"""Conditionally add the hard-expires opt-in header.
Uses the instance flag include_refresh_token_hard_expires_in.
"""
if self._include_refresh_token_hard_expires_in:
headers[INCLUDE_REFRESH_TOKEN_HARD_EXPIRES_IN_HEADER] = 'true'
return headers

def setAuthorizeURLs(self, urlObject):
"""Set authorization url using custom values passed in the data dict
:param **data: data dict for custom authorizationURLS
Expand Down Expand Up @@ -124,6 +162,10 @@ def get_bearer_token(self, auth_code, realm_id=None):
'Authorization': get_auth_header(self.client_id, self.client_secret)
}

headers = self._apply_refresh_token_hard_expires_in_header(
headers
)

body = {
'grant_type': 'authorization_code',
'code': auth_code,
Expand All @@ -148,7 +190,9 @@ def refresh(self, refresh_token=None):
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': get_auth_header(self.client_id, self.client_secret)
}

headers = self._apply_refresh_token_hard_expires_in_header(
headers
)
body = {
'grant_type': 'refresh_token',
'refresh_token': token
Expand Down
8 changes: 7 additions & 1 deletion intuitlib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@
ACCEPT_HEADER = {
'Accept': 'application/json',
'User-Agent': '{0}-{1}-{2}-{3} {4} {5} {6}'.format('Intuit-OAuthClient', version.__version__,'Python', PYTHON_VERSION, OS_SYSTEM, OS_RELEASE_VER, OS_MACHINE)
}
}

# Header to request refresh token hard expiry info on token-endpoint calls
INCLUDE_REFRESH_TOKEN_HARD_EXPIRES_IN_HEADER = 'x-include-refresh-token-hard-expires-in'

# Response field key for the refresh token hard expiry lifespan (seconds)
X_REFRESH_TOKEN_HARD_EXPIRES_IN = 'x_refresh_token_hard_expires_in'
1 change: 1 addition & 0 deletions intuitlib/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Scopes(Enum):
CUSTOM_FIELDS = 'app-foundations.custom-field-definitions'
EMPLOYEE_COMPENSATION = 'payroll.compensation.read'
TAX = 'indirect-tax.tax-calculation.quickbooks'
TAX_COMPLIANCE_WORKPAPERS_READ = 'tax-compliance.workpapers.read'


# For migrated apps only
Expand Down
2 changes: 1 addition & 1 deletion intuitlib/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = '1.2.6'
__version__ = '1.2.7'