Skip to content

Authentication

This module provides helpers to authenticate with Todoist using OAuth.

Quick start

import uuid

from todoist_api_python.authentication import get_auth_token, get_authentication_url

# 1. Generate a random state
state = str(uuid.uuid4())

# 2. Build the authorization URL
url = get_authentication_url(
    client_id="YOUR_CLIENT_ID",
    scopes=["data:read", "task:add"],
    state=state,
)

# 3. Redirect the user to `url`
# 4. Handle the OAuth callback and obtain the auth code
code = "CODE_YOU_OBTAINED"

# 5. Exchange code for an access token
auth_result = get_auth_token(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    code=code,
)

# 6. Ensure state is consistent, and done!
assert auth_result.state == state
access_token = auth_result.access_token

For detailed implementation steps and security considerations, refer to the Todoist OAuth documentation.

get_auth_token(client_id, client_secret, code, client=None)

Get access token using provided client ID, client secret, and auth code.

Source code in todoist_api_python/authentication.py
def get_auth_token(
    client_id: str,
    client_secret: str,
    code: str,
    client: httpx.Client | None = None,
) -> AuthResult:
    """Get access token using provided client ID, client secret, and auth code."""
    endpoint = _get_access_token_url()
    data = _build_auth_token_data(client_id, client_secret, code)

    with _managed_client(client) as managed_client:
        response = post(
            client=managed_client,
            url=endpoint,
            data=data,
        )

    data = response_json_dict(response)
    return AuthResult.from_dict(data)

get_auth_token_async(client_id, client_secret, code, client=None) async

Get access token asynchronously.

Source code in todoist_api_python/authentication.py
async def get_auth_token_async(
    client_id: str,
    client_secret: str,
    code: str,
    client: httpx.AsyncClient | None = None,
) -> AuthResult:
    """Get access token asynchronously."""
    endpoint = _get_access_token_url()
    data = _build_auth_token_data(client_id, client_secret, code)

    async with _managed_async_client(client) as managed_client:
        response = await post_async(
            client=managed_client,
            url=endpoint,
            data=data,
        )

    data = response_json_dict(response)
    return AuthResult.from_dict(data)

get_authentication_url(client_id, scopes, state)

Get authorization URL to initiate OAuth flow.

Source code in todoist_api_python/authentication.py
def get_authentication_url(client_id: str, scopes: list[Scope], state: str) -> str:
    """Get authorization URL to initiate OAuth flow."""
    if len(scopes) == 0:
        raise ValueError("At least one authorization scope should be requested.")

    endpoint = get_oauth_url(AUTHORIZE_PATH)
    query = {
        "client_id": client_id,
        "scope": ",".join(scopes),
        "state": state,
    }
    return f"{endpoint}?{urlencode(query)}"

revoke_auth_token(client_id, client_secret, token, client=None)

Revoke an access token.

Source code in todoist_api_python/authentication.py
def revoke_auth_token(
    client_id: str,
    client_secret: str,
    token: str,
    client: httpx.Client | None = None,
) -> bool:
    """Revoke an access token."""
    endpoint = _get_access_tokens_url()
    params = _build_revoke_auth_token_params(client_id, client_secret, token)

    with _managed_client(client) as managed_client:
        response = delete(client=managed_client, url=endpoint, params=params)

    return response.is_success

revoke_auth_token_async(client_id, client_secret, token, client=None) async

Revoke an access token asynchronously.

Source code in todoist_api_python/authentication.py
async def revoke_auth_token_async(
    client_id: str,
    client_secret: str,
    token: str,
    client: httpx.AsyncClient | None = None,
) -> bool:
    """Revoke an access token asynchronously."""
    endpoint = _get_access_tokens_url()
    params = _build_revoke_auth_token_params(client_id, client_secret, token)

    async with _managed_async_client(client) as managed_client:
        response = await delete_async(
            client=managed_client, url=endpoint, params=params
        )

    return response.is_success