File: tans.rst

package info (click to toggle)
python-fints 4.0.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 700 kB
  • sloc: python: 5,021; makefile: 196
file content (183 lines) | stat: -rw-r--r-- 6,016 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
.. _tans:

Working with TANs
=================

Many operations in FinTS will require a form of two-step authentication, called TANs. TANs are
mostly required for operations that move money or change details of a bank account. TANs can be
generated with a multitude of methods, including paper lists, smartcard readers, SMS messages, and
smartphone apps.

TAN methods
-----------

Before doing any operations involving TANs, you should get a list of supported TAN mechanisms:

.. code-block:: python

    mechanisms = client.get_tan_mechanisms()

The returned dictionary maps identifiers (generally: three-digit numerals) to instances of a
:func:`~fints.formals.TwoStepParametersCommon` subclass with varying fields, depending on the
version of the two-step process and the bank.

The `name` field of these objects provides a user-friendly name of the TAN mechanism that you
can display to the user to choose from. To select a TAN mechanism, you can use
:func:`~fints.client.FinTS3PinTanClient.set_tan_mechanism`, which takes the identifier used as
key in the :func:`~fints.client.FinTS3PinTanClient.get_tan_mechanisms` return value.

If the ``description_required`` attribute for the TAN mechanism is :attr:`~fints.formals.DescriptionRequired.MUST`,
you will need to get a list of TAN media with :func:`~fints.client.FinTS3PinTanClient.get_tan_media` and select the
appropriate one with :func:`~fints.client.FinTS3PinTanClient.set_tan_medium`.

Have a look at the source code of :func:`~fints.utils.minimal_interactive_cli_bootstrap` for an example on how to
ask the user for these properties.

You may not change the active TAN mechanism or TAN medium within a standing dialog (see :ref:`client-dialog-state`).

The selection of the active TAN mechanism/medium is stored with the persistent client data (see :ref:`client-state`).

.. autoclass:: fints.client.FinTS3PinTanClient
   :members: get_tan_mechanisms, set_tan_mechanism, get_current_tan_mechanism, get_tan_media, set_tan_medium
   :noindex:
   :undoc-members:

TAN challenges
--------------

When you try to perform an operation that requires a TAN to proceed, you will receive an object containing
the bank's challenge (and some internal data to continue the operation once the TAN has been processed):

.. autoclass:: fints.client.NeedTANResponse
   :undoc-members:
   :members:

The ``challenge`` attribute will contain human-readable instructions on how to proceed.

The ``challenge_html`` attribute will possibly contain a nicer, formatted, HTML version of the challenge text
that you should prefer if your primary interface can render HTML. The contents are guaranteed to be proper and
clean (by using the `bleach` library): They can be used with `mark_safe` in Django.

The ``challenge_hhduc`` attribute will contain the challenge to be used with a TAN generator device using the
Hand Held Device Unidirectional Coupling specification (such as a Flicker-Code).

Flicker-Code / optiTAN
----------------------

If you want to use chipTAN with an optical TAN device, we provide utilities to print the flicker code on
a unix terminal. Just pass the ``challenge_hhd_uc`` value to this method:

.. autofunction:: fints.hhd.flicker.terminal_flicker_unix

You should probably catch for ``KeyboardInterrupts`` to allow the user to abort the displaying and to continue
with the TAN:

.. code-block:: python

    try:
        terminal_flicker_unix(result.challenge_hhduc)
    except KeyboardInterrupt:
        pass

photoTAN
--------

If you want to use photoTAN, use the ``challenge_matrix`` attribute to access the image file, e.g. by writing it to
a file:

.. code-block:: python

    with open("tan.png", "wb") as writer:
       writer.write(result.challenge_matrix[1])
       writer.close()

Sending the TAN
---------------

Once obtained the TAN, you can send it with the ``send_tan`` client method:

.. autoclass:: fints.client.FinTS3PinTanClient
   :members: send_tan
   :noindex:

For example:

.. code-block:: python

    tan = input('Please enter the TAN code: ')
    result = client.send_tan(result, tan)


Storing and restoring TAN state
-------------------------------

The :func:`~fints.client.NeedTANResponse.get_data` method and
:func:`~fints.client.NeedRetryResponse.from_data` factory method can be used to store and restore
a TAN state object between steps.

.. autoclass:: fints.client.NeedRetryResponse
   :undoc-members:
   :members: from_data

You SHOULD use this facility together with the client and dialog state restoration facilities:


.. code-block:: python
   :caption: First step

    client = FinTS3PinTanClient(...)
    # Optionally: choose a tan mechanism with
    # client.set_tan_mechanism(…)

    with client:
        response = client.sepa_transfer(...)
    
        dialog_data = client.pause_dialog()
    client_data = client.deconstruct()
    tan_data = response.get_data()

.. code-block:: python
   :caption: Second step

    tan_request = NeedRetryResponse.from_data(tan_data)
    print("TAN request: {}".format(tan_request.challenge))
    tan = input('Enter TAN: ')

.. code-block:: python
   :caption: Third step

    tan_request = NeedRetryResponse.from_data(tan_data)
    client = FinTS3PinTanClient(..., from_data=client_data)
    with client.resume_dialog(dialog_data):
        response = client.send_tan(tan_request, tan)

    print(response.status)
    print(response.responses)


Reference
---------

.. autoclass:: fints.formals.TwoStepParameters2
   :noindex:
   :undoc-members:
   :members:
   :inherited-members:
   :member-order: bysource
   :exclude-members: is_unset, naive_parse, print_nested

.. autoclass:: fints.formals.TwoStepParameters3
   :noindex:
   :undoc-members:
   :members:
   :inherited-members:
   :member-order: bysource
   :exclude-members: is_unset, naive_parse, print_nested

.. autoclass:: fints.formals.TwoStepParameters5
   :noindex:
   :undoc-members:
   :members:
   :inherited-members:
   :member-order: bysource
   :exclude-members: is_unset, naive_parse, print_nested