.. _gares:

Globus Auth Requirements Errors (GAREs)
=======================================

'Globus Auth Requirements Error' is a response format that conveys to a client any
modifications to a session (i.e., "boosting") that will be required
to complete a particular request.

The ``globus_sdk.gare`` module provides a number of tools to make it easier to
identify and handle these errors when they occur.

GARE
----

The ``GARE`` class provides a model for working with Globus Auth Requirements Error
responses.

Services in the Globus ecosystem may need to communicate authorization requirements
to their consumers. For example, a service may need to instruct clients to have the user
consent to an additional scope, ``"foo"``. In such a case, ``GARE`` can provide
serialization into the well-known Globus Auth Requirements Error format:

.. code-block:: python

    from globus_sdk.gare import GARE

    error_doc = GARE(
        code="ConsentRequired",
        authorization_parameters=GlobusAuthorizationParameters(
            required_scopes=["foo"],
            session_message="Missing required 'foo' consent",
        ),
    )

    # Render a strict dictionary
    error.to_dict()

If non-canonical fields are needed, the ``extra`` argument can be used to
supply a dictionary of additional fields to include. Non-canonical fields present
in the provided dictionary when calling ``from_dict()`` are stored similarly.
You can include these fields in the rendered output dictionary
by specifying ``include_extra=True`` when calling ``to_dict()``.

.. code-block:: python

    from globus_sdk.gare import GARE

    error = GARE(
        code="ConsentRequired",
        authorization_parameters=GlobusAuthorizationParameters(
            required_scopes=["foo"],
            session_message="Missing required 'foo' consent",
        ),
        extra={
            "message": "Missing required 'foo' consent",
            "request_id": "WmMV97A1w",
            "required_scopes": ["foo"],
            "resource": "/transfer",
        },
    )

    # Render a dictionary with extra fields
    error.to_dict(include_extra=True)

These fields are stored by both the ``GARE`` and ``GlobusAuthenticationParameters``
classes in an ``extra`` attribute.

.. note::

    Non-canonical fields in a Globus Auth Requirements Error are primarily intended
    to make it easier for services to provide backward-compatibile error responses
    to clients that have not adopted the Globus Auth Requirements Error format. Avoid
    using non-canonical fields for any data that should be generically understood by
    a consumer of the error response.

.. _globus_app_gare_integration:

GlobusApp Integration
---------------------

In order to standardize a common GARE-handling approach, the SDK's
:ref:`GlobusApp <globus_apps>` contains a built-in GARE redrive mechanism (disabled
by default).

When enabled (setting the config flag ``auto_redrive_gares`` to ``True``), any
client created with a GlobusApp will receive an additional request-transport retry
handler. This handler will intercept the first occurrence of a 403 response which
parses as a GARE and "redrive it", by:

1. Initiating a fresh login request with the defined ``LoginFlowManager``
2. Caching new tokens for subsequent requests
3. Re-attempting the original request with the new tokens

This allows for very simple error-handling, particularly in scripts, wherein the
executing user or client is automatically prompted to remediate manually-required
steps (e.g., consent, MFA) without any additional code.


.. code-block:: python

    from globus_sdk import UserApp, GlobusAppConfig, TransferClient

    config = GlobusAppConfig(auto_redrive_gares=True)
    with UserApp("my-gare-demo", "<client-id>", config=config) as app:
        tc = TransferClient(app=app)

        # If the transfer service were to return a 403 GARE, the script runner would be
        # prompted to log in with any explicit specifications, e.g., MFA, consents.
        # Once they complete the login flow (by providing the newly minted auth code),
        # the original request is attempted once more.
        task = tc.submit_transfer(your_transfer_data)


Parsing Responses
-----------------

If you are writing a client to a Globus API, the ``gare`` subpackage provides utilities
to detect legacy Globus Auth requirements error response formats and normalize them.

To detect if a ``GlobusAPIError``, ``ErrorSubdocument``, or JSON response
dictionary represents an error that can be converted to a Globus Auth
Requirements Error, you can use, e.g.,:

.. code-block:: python

    from globus_sdk import gare

    error_dict = {
        "code": "ConsentRequired",
        "message": "Missing required foo consent",
    }
    # The dict is not a Globus Auth Requirements Error, so `False` is returned.
    gare.is_auth_requirements_error(error_dict)

    # The dict is not a Globus Auth Requirements Error and cannot be converted.
    gare.to_auth_requirements_error(error_dict)  # None

    error_dict = {
        "code": "ConsentRequired",
        "message": "Missing required foo consent",
        "required_scopes": ["urn:globus:auth:scope:transfer.api.globus.org:all[*foo]"],
    }
    gare.is_auth_requirements_error(error_dict)  # True
    gare.to_auth_requirements_error(error_dict)  # GARE

.. note::

    If a ``GlobusAPIError`` represents multiple errors that were returned in an
    array, ``to_auth_requirements_error()`` only returns the first error in that
    array that can be converted to the Globus Auth Requirements Error response format.
    In this case (and in general) it's preferable to use
    ``to_auth_requirements_errors()`` (which also accepts a list of
    ``GlobusAPIError``\ s, ``ErrorSubdocument``\ s, and JSON response dictionaries):

.. code-block:: python

    gare.to_auth_requirements_error(other_error)  # GARE
    gare.to_auth_requirements_errors([other_error])  # [GARE, ...]

Notes
-----

``GARE`` enforces types strictly when parsing a Globus Auth Requirements Error
response dictionary, and will raise a :class:`globus_sdk.ValidationError` if a
supported field is supplied with a value of the wrong type.

``GARE`` does not attempt to mimic or itself enforce any logic specific to the
Globus Auth service with regard to what represents a valid combination of fields
(e.g., ``session_required_mfa`` requires either ``session_required_identities`` or
``session_required_single_domain`` in order to be properly handled).

Reference
---------

.. currentmodule:: globus_sdk.gare

.. autoclass:: GARE
    :members:
    :inherited-members:

.. autoclass:: GlobusAuthorizationParameters
    :members:
    :inherited-members:

.. autofunction:: to_gare

.. autofunction:: to_gares

.. autofunction:: is_gare

.. autofunction:: has_gares
