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)[source]
get_current_tan_mechanism()[source]
get_tan_mechanisms()[source]

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)[source]

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)[source]
set_tan_medium(tan_medium)[source]

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)[source]
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[source]

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)[source]

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)[source]
send_tan(challenge: fints.client.NeedTANResponse, tan: str)[source]

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[source]

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)[source]

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)[source]
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
VERSION

TAN mechanism version

class fints.formals.TwoStepParameters3(*args, **kwargs)[source]
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
VERSION

TAN mechanism version

class fints.formals.TwoStepParameters5(*args, **kwargs)[source]
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
VERSION

TAN mechanism version