Authorization
Important
See Get Started for information on how to register
a new app with TickTick
OAuth2
¶
Implements the Authorization flow for TickTick's Open API
__init__(self, client_id, client_secret, redirect_uri, scope='tasks:write tasks:read', state=None, session=None, env_key=None, cache_path='.token-oauth', check_cache=True)
special
¶
Initialize the object.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
client_id |
str |
Client ID string |
required |
client_secret |
str |
Client secret string |
required |
redirect_uri |
str |
Redirect uri |
required |
scope |
str |
Scope for the permissions. Current options are only the default. |
'tasks:write tasks:read' |
state |
str |
State parameter |
None |
session |
requests session |
Requests session |
None |
env_key |
str |
The environment variable name where the access token dictionary is stored as a string literal. |
None |
cache_path |
str |
The desired path of the file where the access token information will be stored. |
'.token-oauth' |
check_cache |
bool |
Whether to check the cache file for the access token information |
True |
Examples
This way would instantiate the steps to get a new access token, or just retrieve the cached one.
oauth = OAuth2(client_id=cliend_id,
client_secret=client_secret,
redirect_uri=redirect_uri)
If you are in a situation where you don't want to keep the cached token file, you can save the access token dictionary as a string literal in your environment, and pass the name of the variable to prevent having to request a new access token.
auth_client = OAuth2(client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri,
env_key='ACCESS_TOKEN_DICT')
Where in the environment you have declared ACCESS_TOKEN_DICT
to be
the string literal of the token dictionary:
'{'access_token': '628ff081-5331-4a37-8ddk-021974c9f43g',
'token_type': 'bearer', 'expires_in': 14772375,
'scope': 'tasks:read tasks:write',
'expire_time': 1637192935,
'readable_expire_time':
'Wed Nov 17 15:48:55 2021'}'
Source code in ticktick/oauth2.py
def __init__(self,
client_id: str,
client_secret: str,
redirect_uri: str,
scope: str = "tasks:write tasks:read", # only available options right now
state: str = None,
session=None,
env_key: str = None,
cache_path: str = '.token-oauth',
check_cache: bool = True
):
"""
Initialize the object.
Arguments:
client_id: Client ID string
client_secret: Client secret string
redirect_uri: Redirect uri
scope: Scope for the permissions. Current options are only the default.
state (str): State parameter
session (requests session): Requests session
env_key: The environment variable name where the access token dictionary is stored as a string literal.
cache_path: The desired path of the file where the access token information will be stored.
check_cache: Whether to check the cache file for the access token information
!!! examples
=== "Standard Method"
This way would instantiate the steps to get a new access token, or just retrieve the cached one.
```python
oauth = OAuth2(client_id=cliend_id,
client_secret=client_secret,
redirect_uri=redirect_uri)
```
=== "Check Environment Method"
If you are in a situation where you don't want to keep the cached token file, you can save the
access token dictionary as a string literal in your environment, and pass the name of the variable to
prevent having to request a new access token.
``` python
auth_client = OAuth2(client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri,
env_key='ACCESS_TOKEN_DICT')
```
Where in the environment you have declared `ACCESS_TOKEN_DICT` to be
the string literal of the token dictionary:
```
'{'access_token': '628ff081-5331-4a37-8ddk-021974c9f43g',
'token_type': 'bearer', 'expires_in': 14772375,
'scope': 'tasks:read tasks:write',
'expire_time': 1637192935,
'readable_expire_time':
'Wed Nov 17 15:48:55 2021'}'
```
"""
# If a proper session is passed then we will just use the existing session
self.session = session or requests_retry_session()
# Set the client_id
self._client_id = client_id
# Set the client_secret
self._client_secret = client_secret
# Set the redirect_uri
self._redirect_uri = redirect_uri
# Set the scope
self._scope = scope
# Set the state
self._state = state
# Initialize code parameter
self._code = None
# Set the cache handler
self.cache = CacheHandler(cache_path)
# Set the access token
self.access_token_info = None
# get access token
self.get_access_token(check_cache=check_cache, check_env=env_key)
get_access_token(self, check_cache=True, check_env=None)
¶
Retrieves the authorization token from cache or makes a new request for it.
Note
This method does not need to be called explicitly.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
check_cache |
bool |
Boolean on whether to check if the access token is in a cache file. |
True |
check_env |
str |
The environment variable name where the token dictionary is saved as a string literal. |
None |
Priority order for getting the access token:
1) From an already set class member in the current running instance
2) From an environment variable where the token dictionary is in a string literal form, and the name of the environment variable name is the value passed to the "check_env" parameter
3) From a cache file that contains the access token dictionary (normal case)
4) From a new token request (which will create a new cache file that contains the access token dictionary) (initial case if never setup)
Source code in ticktick/oauth2.py
def get_access_token(self, check_cache: bool = True, check_env: str = None):
"""
Retrieves the authorization token from cache or makes a new request for it.
!!! note
This method does not need to be called explicitly.
Arguments:
check_cache (bool): Boolean on whether to check if the access token is in a cache file.
check_env (str): The environment variable name where the token dictionary is saved as a string literal.
Priority order for getting the access token:
1) From an already set class member in the current running instance
2) From an environment variable where the token dictionary is in a string literal form,
and the name of the environment variable name is the value passed to the "check_env" parameter
3) From a cache file that contains the access token dictionary (normal case)
4) From a new token request (which will create a new cache file that contains the access
token dictionary) (initial case if never setup)
"""
# check the local state for if the access token exists
if self.access_token_info is not None:
token_info = self.validate_token(self.access_token_info)
if token_info is not None:
self.access_token_info = token_info
return token_info["access_token"]
# check if in the environment the access token is set
if check_env is not None:
# get the access token string
token_dict_string = os.getenv(check_env)
try:
converted_token_dict = ast.literal_eval(token_dict_string)
except:
raise ValueError("Access token in the environment must be a python dictionary contained"
" in a string literal")
token_info = self.validate_token(converted_token_dict)
if token_info is not None:
self.cache.write_token_to_cache(token_info)
self.access_token_info = token_info
return token_info["access_token"]
# check if the cache file exists with the token
if check_cache:
token_info = self.validate_token(self.cache.get_cached_token())
# validate token will always return a valid token
if token_info is not None:
self.access_token_info = token_info
return token_info["access_token"]
# access token is not stored anywhere, request a new token
token_info = self._request_access_token()
self.access_token_info = token_info
return token_info["access_token"]
is_token_expired(token_dict)
staticmethod
¶
Returns a boolean for if the access token is expired
Parameters:
Name | Type | Description | Default |
---|---|---|---|
token_dict |
dict |
Access token dictionary |
required |
Returns:
Type | Description |
---|---|
bool |
Whether the access token is expired |
Source code in ticktick/oauth2.py
@staticmethod
def is_token_expired(token_dict):
"""
Returns a boolean for if the access token is expired
Arguments:
token_dict (dict): Access token dictionary
Returns:
bool: Whether the access token is expired
"""
current_time = int(time.time())
return token_dict["expire_time"] - current_time < 60
validate_token(self, token_dict)
¶
Validates whether the access token is valid
Parameters:
Name | Type | Description | Default |
---|---|---|---|
token_dict |
dict |
Access token dictionary |
required |
Returns:
Type | Description |
---|---|
None or dict |
None if the token_dict is not valid, else token_dict |
Source code in ticktick/oauth2.py
def validate_token(self, token_dict):
"""
Validates whether the access token is valid
Arguments:
token_dict (dict): Access token dictionary
Returns:
None or dict: None if the token_dict is not valid, else token_dict
"""
# if the token info dictionary does not exist then bounce
if token_dict is None:
return None
# check if the token is expired
if self.is_token_expired(token_dict):
# make a new request for a valid token since there is currently no refresh token
new_token_dict = self._request_access_token()
return new_token_dict
return token_dict # original token_dict is valid
requests_retry_session(retries=3, backoff_factor=1, status_forcelist=(405, 500, 502, 504), session=None, allowed_methods=frozenset({'PUT', 'DELETE', 'POST', 'GET'}))
¶
Method for http retries
Source code in ticktick/oauth2.py
def requests_retry_session(retries=3,
backoff_factor=1,
status_forcelist=(405, 500, 502, 504),
session=None,
allowed_methods=frozenset(['GET', 'POST', 'PUT', 'DELETE'])):
"""
Method for http retries
"""
session = session or requests.session()
retry = Retry(
total=retries,
read=retries,
connect=retries,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
allowed_methods=allowed_methods
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session