# 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:

    mechanisms = client.get_tan_mechanisms()

The returned dictionary maps identifiers (generally: three-digit numerals) to instances of a `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 `set_tan_mechanism()`, which takes the identifier used as key in the `get_tan_mechanisms()` return value.

If the `description_required` attribute for the TAN mechanism is `MUST`, you will need to get a list of TAN media with `get_tan_media()` and select the appropriate one with `set_tan_medium()`.

Have a look at the source code of `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 Storing and restoring dialog state).

The selection of the active TAN mechanism/medium is stored with the persistent client data (see Storing and restoring client state).

class fints.client.FinTS3PinTanClient(bank_identifier, user_id, pin, server, customer_id=None, *args, **kwargs)
  get_current_tan_mechanism()
  get_tan_mechanisms()

    Get the available TAN mechanisms.

    Note: Only checks for HITANS versions listed in IMPLEMENTED_HKTAN_VERSIONS.

    Returns:

      Dictionary of security_function: TwoStepParameters objects.

  get_tan_media(media_type=fints.formals.TANMediaType2.ALL, media_class=fints.formals.TANMediaClass4.ALL)

    Get information about TAN lists/generators.

    Returns tuple of fints.formals.TANUsageOption and a list of fints.formals.TANMedia4 or fints.formals.TANMedia5 objects.

  set_tan_mechanism(security_function)
  set_tan_medium(tan_medium)

## 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):

class fints.client.NeedTANResponse(command_seg, tan_request, resume_method=None, tan_request_structured=False)
  challenge = None

    Textual challenge to be displayed to the user

  challenge_hhduc = None

    HHD_UC challenge to be transmitted to the TAN generator

  challenge_html = None

    HTML-safe challenge text, possibly with formatting

  challenge_matrix = None

    Matrix code challenge: tuple(mime_type, data)

  challenge_raw = None

    Raw challenge as received by the bank

  get_data() → bytes

    Return a compressed datablob representing this object.

    To restore the object, use `fints.client.NeedRetryResponse.from_data()`.

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:

fints.hhd.flicker.terminal_flicker_unix(code, field_width=3, space_width=3, height=1, clear=False, wait=0.05)

  Re-encodes a flicker code and prints it on a unix terminal.

  Parameters:

    - code – Challenge value

    - field_width – Width of fields in characters (default: 3).

    - space_width – Width of spaces in characters (default: 3).

    - height – Height of fields in characters (default: 1).

    - clear – Clear terminal after every line (default: `False`).

    - wait – Waiting interval between lines (default: 0.05).

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

    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:

    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:

class fints.client.FinTS3PinTanClient(bank_identifier, user_id, pin, server, customer_id=None, *args, **kwargs)
  send_tan(challenge: NeedTANResponse, tan: str)

    Sends a TAN to confirm a pending operation.

    Parameters:

      - challenge – NeedTANResponse to respond to

      - tan – TAN value

    Returns:

      Currently no response

For example:

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

## Storing and restoring TAN state

The `get_data()` method and `from_data()` factory method can be used to store and restore a TAN state object between steps.

class fints.client.NeedRetryResponse

  Base class for Responses that need the operation to be externally retried.

  A concrete subclass of this class is returned, if an operation cannot be completed and needs a retry/completion. Typical (and only) example: Requiring a TAN to be provided.

  classmethod from_data(blob)

    Restore an object instance from a compressed datablob.

    Returns an instance of a concrete subclass.

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

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()

Second step

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

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

class fints.formals.TwoStepParameters2(*args, **kwargs)
  name

    Name des Zwei-Schritt-Verfahrens

    Type:

      `str`

  max_length_input

    Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren

    Type:

      `int`

  allowed_format

    Erlaubtes Format im Zwei-Schritt-Verfahren

    Type:

      `fints.formals.AllowedFormat`

  text_return_value

    Text zur Belegung des Rückgabewertes im Zwei-Schritt-Verfahren

    Type:

      `str`

  max_length_return_value

    Maximale Länge des Rückgabewertes im Zwei-Schritt-Verfahren

    Type:

      `int`

  number_of_supported_lists

    Anzahl unterstützter aktiver TAN-Listen

    Type:

      `int`

  multiple_tans_allowed

    Mehrfach-TAN erlaubt

    Type:

      `bool`

  tan_time_dialog_association

    TAN Zeit- und Dialogbezug

    Type:

      `fints.formals.TANTimeDialogAssociation`

  tan_list_number_required

    TAN-Listennummer erforderlich

    Type:

      `fints.formals.TANListNumberRequired`

  cancel_allowed

    Auftragsstorno erlaubt

    Type:

      `bool`

  challenge_class_required

    Challenge-Klasse erforderlich

    Type:

      `bool`

  challenge_value_required

    Challenge-Betrag erforderlich

    Type:

      `bool`

  property VERSION

    TAN mechanism version

class fints.formals.TwoStepParameters3(*args, **kwargs)
  name

    Name des Zwei-Schritt-Verfahrens

    Type:

      `str`

  max_length_input

    Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren

    Type:

      `int`

  allowed_format

    Erlaubtes Format im Zwei-Schritt-Verfahren

    Type:

      `fints.formals.AllowedFormat`

  text_return_value

    Text zur Belegung des Rückgabewertes im Zwei-Schritt-Verfahren

    Type:

      `str`

  max_length_return_value

    Maximale Länge des Rückgabewertes im Zwei-Schritt-Verfahren

    Type:

      `int`

  number_of_supported_lists

    Anzahl unterstützter aktiver TAN-Listen

    Type:

      `int`

  multiple_tans_allowed

    Mehrfach-TAN erlaubt

    Type:

      `bool`

  tan_time_dialog_association

    TAN Zeit- und Dialogbezug

    Type:

      `fints.formals.TANTimeDialogAssociation`

  tan_list_number_required

    TAN-Listennummer erforderlich

    Type:

      `fints.formals.TANListNumberRequired`

  cancel_allowed

    Auftragsstorno erlaubt

    Type:

      `bool`

  challenge_class_required

    Challenge-Klasse erforderlich

    Type:

      `bool`

  challenge_value_required

    Challenge-Betrag erforderlich

    Type:

      `bool`

  initialization_mode

    Initialisierungsmodus

    Type:

      `fints.formals.InitializationMode`

  description_required

    Bezeichnung des TAN-Medium erforderlich

    Type:

      `fints.formals.DescriptionRequired`

  supported_media_number

    Anzahl unterstützter aktiver TAN-Medien

    Type:

      `int`

  property VERSION

    TAN mechanism version

class fints.formals.TwoStepParameters5(*args, **kwargs)
  zka_id

    ZKA TAN-Verfahren

    Type:

      `str`

  zka_version

    Version ZKA TAN-Verfahren

    Type:

      `str`

  name

    Name des Zwei-Schritt-Verfahrens

    Type:

      `str`

  max_length_input

    Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren

    Type:

      `int`

  allowed_format

    Erlaubtes Format im Zwei-Schritt-Verfahren

    Type:

      `fints.formals.AllowedFormat`

  text_return_value

    Text zur Belegung des Rückgabewertes im Zwei-Schritt-Verfahren

    Type:

      `str`

  max_length_return_value

    Maximale Länge des Rückgabewertes im Zwei-Schritt-Verfahren

    Type:

      `int`

  number_of_supported_lists

    Anzahl unterstützter aktiver TAN-Listen

    Type:

      `int`

  multiple_tans_allowed

    Mehrfach-TAN erlaubt

    Type:

      `bool`

  tan_time_dialog_association

    TAN Zeit- und Dialogbezug

    Type:

      `fints.formals.TANTimeDialogAssociation`

  tan_list_number_required

    TAN-Listennummer erforderlich

    Type:

      `fints.formals.TANListNumberRequired`

  cancel_allowed

    Auftragsstorno erlaubt

    Type:

      `bool`

  sms_charge_account_required

    SMS-Abbuchungskonto erforderlich

    Type:

      `fints.formals.SMSChargeAccountRequired`

  principal_account_required

    Auftraggeberkonto erforderlich

    Type:

      `fints.formals.PrincipalAccountRequired`

  challenge_class_required

    Challenge-Klasse erforderlich

    Type:

      `bool`

  challenge_structured

    Challenge strukturiert

    Type:

      `bool`

  initialization_mode

    Initialisierungsmodus

    Type:

      `fints.formals.InitializationMode`

  description_required

    Bezeichnung des TAN-Medium erforderlich

    Type:

      `fints.formals.DescriptionRequired`

  supported_media_number

    Anzahl unterstützter aktiver TAN-Medien

    Type:

      `int`

  property VERSION

    TAN mechanism version