Skip to main content

A complete implementation of swedish BankID version 6. Easily initiate, collect and cancel authentication orders, show user message and handle errors.

Project description

bankid6


Overview


A complete implementation of swedish BankID authentication system version 6. It includes initiating/collect/cancel authentication order, User Message and exceptaion handling according to BankID Documentaion.

Installation


pip install bankid6

Supports python version 3.6 and later.

User Guide


Instantiate BankIdClient Class

BankIdClient provides functionality to initiate authentication or signing order, collect order result or cancel the order.

Instantiate for BankIdClient for test environment.

>>> from bankid6 import BankIdClient
>>> bankid_client = BankIdClient()

This instance will use BankID test url, test certificates which are provided in the package. It's configured for computers. It can be initialized for mobile devices which helps to render correct message and correct url for launching the app.

>>> bankid_client = BankIdClient(is_mobile=True)            # Default to False

To initialize for a production environment, ensure that the prod_env parameter is set to True. Additionally, you must provide the file paths for the certificate, the private key associated with the certificate, and the CA certificate.

>>> bankid_client = BankIdClient(prod_env=True, cert_pem=<certificate_filepath>, key_pem='<private_key_filepath>', ca_pem=<ca_certificate_filepath>)

Initiate Authentication or Signing Order

The BankIdClient offers auth and sign methods to initiate authentication and signing orders, respectively. These methods return a BankIdStartResponse object, which contains attributes to easily get the data for QR code and url for launching the BankID app.

>>> bankid_client = BankIdClient()
>>> start_response = bankid_client.auth('192.168.0.1')      # Takes IP of end user

>>> start_response.qr_data                                  # Calculated data to create QR code
'bankid.c03da000-d5de-4435-b81b-66154960784d.8.7a7f4a36307cb8d8ddb4fe86116764819def4c32c55d1ba792e6e4117be9a5a1'

>>> start_response.launch_url()                             # Depends on is_mobile parameter of the BankIdClient
'bankid:///?autostarttoken=aaba5bef-1066-42da-b6c6-0730b8c53997&redirect=null'

>>> start_response.launch_url('https://www.google.com')     # Takes a redirect url as parameter
'bankid:///?autostarttoken=aaba5bef-1066-42da-b6c6-0730b8c53997&redirect=https%3A%2F%2Fwww.google.com'

>>> [attr for attr in dir(start_response) if not attr.startswith('_')]
['autoStartToken', 'data', 'launch_url', 'orderRef', 'order_time', 'qrStartSecret', 'qrStartToken', 'qr_data', 'response', 'status_code', 'url']

Subsequent QR data can be found in response of collect method or from generate_qr_data function.

>>> from bankid6 import generate_qr_data
>>> generate_qr_data(start_response.order_time, start_response.qrStartToken, start_response.qrStartSecret)
'bankid.c03da000-d5de-4435-b81b-66154960784d.250.16f0f42bb4f1a99d41a31e38cd54866fce5a193e277f4d48339a7579ac51fe4e'

The BankIdClient also has phone_auth and phone_sign methods initiate authentication and signing orders while the customer is on the phone. You need to pass a personal number and the BankID will send the request to the customer's BankID app. These methods return a BankIdPhoneStartResponse object.

>>> phone_start_response = BankIdClient().phone_auth('199002113166', callInitiator="RP")
>>> [attr for attr in dir(phone_start_response) if not attr.startswith('_')]
['data', 'orderRef', 'response', 'status_code', 'url']

Both sign and phone_sign methods require userVisibleData parameter.

>>> BankIdClient().sign('192.168.0.1', userVisibleData="Hello! Sign this test documents")
<class 'bankid6.handlers.BankIdStartResponse'> response status: 200;
response data: {"orderRef": "6eaf4368-22ae-4309-8768-f58b772d1617", "autoStartToken": "3d973332-8abe-4273-b292-b16c975a1a39", "qrStartToken": "29d0b198-487d-42b8-91a8-c63fc94a2733", "qrStartSecret": "a611ccd5-5940-4160-9fde-20a251716bfb"}

These methods have required or optional parameters exactly as described in BankID documentation. These parameters are validated, processed as BankID requires and sent as the data of the request to the BankID.

The returned objects are derived from BankIdBaseResponse, parses any BankID response, creating an attribute with the same name as the key of the response data. BankIdBaseResponse has attributes related to actual HTTP response. This is also True for collect or cancel.

See the API Reference section for comprehensive documentation detailing parameters and return values.

Collect Order Result

Use collect method of BankIdClient object to get the order result.

>>> from bankid6 import BankIdClient
>>> bankid_client = BankIdClient()

>>> bankid_client.auth('192.168.0.1')
<class 'bankid6.handlers.BankIdStartResponse'> response status: 200
response data: {"orderRef": "ccc9b028-4c83-49f8-b5ae-a3bac54a7427", "autoStartToken": "96c45dea-d14c-4bc5-bf30-db4015d39da9", "qrStartToken": "d550a68e-1e4d-4d66-8dce-3c6497c8da73", "qrStartSecret": "4b3bba21-ebaa-4bf4-8856-e66271550b78"}

>>> bankid_client.collect()
<class 'bankid6.handlers.BankIdCollectResponse'> response status: 200;
response data: {"orderRef": "ccc9b028-4c83-49f8-b5ae-a3bac54a7427", "status": "pending", "hintCode": "outstandingTransaction"}

It returns BankIdCollectResponse which is derived from BankIdBaseResponse class.

>>> collect_response = bankid_client.collect()
>>> collect_response.status                             # BankID status from response data
'pending'
>>> collect_response.qr_data                            # calculates qr data according to the time             
'bankid.d9c9339c-b9d3-48a2-8289-8d66e1d28a08.21.941023af5b86d5b16aaa226ad7130ee08a1dcdca89e199639a3986336a0569fb'
>>> from bankid6 import CollectStatuses
>>> collect_response.status == CollectStatuses.pending  # don't have to remember status values
True

Like the BankID documentation, the response object provides information in completionData (object of BankIdCompletionData) attribute when status is "complete".

>>> cr = bankid_client.collect()
>>> print(cr.completionData.user.personalNumber) if cr.status == CollectStatuses.complete else print(cr.hintCode)

It's possible to decouple the collect method from order initiating methods.

>>> sr = BankIdClient().auth('192.168.0.1')
>>> cr = BankIdClient().collect(orderRef=sr.orderRef, qrStartToken=sr.qrStartToken, qrStartSecret=sr.qrStartSecret, order_time=sr.order_time)
>>> cr.qr_data
'bankid.d9c9339c-b9d3-48a2-8289-8d66e1d28a08.21.941023af5b86d5b16aaa226ad7130ee08a1dcdca89e199639a3986336a0569fb'

orderRef parameter is used to find the order and qrStartToken, qrStartSecret and order_time parameters are used to calculate the QR code. qr_data attribute in BankIdCollectResponse object is accessible if the collect method is invoked from the same client where order was initiated, or if these parameters are provided.

User Message

BankIdCollectResponse also has message attribute which contains user message according to BankID documentation when the status is 'pending' or 'failed'. A message depends on how order was initiated and the language. Each message is a dict constructed like this:

{
    "qrcode": {             # order initiated by scanning qr code
        "swedish": "",
        "english": ""
    },
    "onfile": {             # order initiated by starting the bankid app on the same device
        "swedish": "",
        "english": ""
    }
} 

UseTypes and Languages classes can be used to extract the correct message easily.

>>> cr = bankid_client.collect()
>>> from bankid6 import UseTypes, Languages
>>> print(cr.message[UseTypes.qrcode][Languages.en]) if cr.status in [CollectStatuses.complete, CollectStatuses.failed]
'Start your BankID app.'

You can make a subclass of Messages, override it's existing messages and pass it toBankIdClient as a parameter. Any message in Messages class is a attribute which starts with 'RFA' as in BankID documentation.

from bankid6 import Messages

print(Messages.RFA1.help_text) # prints 'status=pending, hintCode=outstandingTransaction, hintCode=noClient'

class MyMessage(Messages):
    RFA1 = {'test': 'yes'}
    RFA13 = ('<h3>swedish message<h3>', '<h3>english message</h3>')

bankid_client = BankIdClient(messages=MyMessage)

Their value can be a dict or a tuple of swedish and english messages. If the value is dict, it is returned as it is from message attribute of BankIdCollectResponse object.

Cancel Order

Use cancel mothod of BankIdClient object to cancel the order.

>>> bankid_client = BankIdClient()

>>> bankid_client.auth('192.168.0.1')
<class 'bankid6.handlers.BankIdStartResponse'> response status: 200
response data: {"orderRef": "ccc9b028-4c83-49f8-b5ae-a3bac54a7427", "autoStartToken": "96c45dea-d14c-4bc5-bf30-db4015d39da9", "qrStartToken": "d550a68e-1e4d-4d66-8dce-3c6497c8da73", "qrStartSecret": "4b3bba21-ebaa-4bf4-8856-e66271550b78"}

>>> bankid_client.cancel()
<class 'bankid6.handlers.BankIdCancelResponse'> response status: 200;
response data: {}

This can be decouple by using it's orderRef parameter.

>>> start_response = BankIdClient().auth('192.168.0.1')
>>> BankIdClient().auth(orderRef=start_response.orderRef)
<class 'bankid6.handlers.BankIdCancelResponse'> response status: 200;
response data: {}

Exceptions

All methods in BankIdClient can raise BankIdError or BankIdValidationError.

BankIdError is raised when a request is made to BankID server and it returned an error. It has message attribute with structure like:

{
    "swedish": "",
    "english": ""
}

If the message is available, it indicates that the message should be presented to the customer without any additional action required. Otherwise, the reason and action attributes can provide information on why the error occurred and what steps need to be taken to address it according to BankID documentation. Attribute response_data has the data received from bankid in dict format. See Api Reference section for more functionalities.

BankIdValidationError is raised before sending the request to BankID if any parameter is invalid.



Sample Script


import time
from bankid6 import BankIdClient, CollectStatuses, UseTypes, Languages, BankIdError, HintCodes

bankid_client = BankIdClient()

try:
    start_response = bankid_client.auth('192.168.0.1')
except BankIdError as bie:
    if bie.message:
        print("User Message: ", bie.message[Languages.en])
    else:
        raise Exception(f"Fatal error. reason: {bie.reason}; action needed: {bie.action}; response data: {bie.response_data}; response status: {bie.response_status}") from bie

print('QR data: ', start_response.qr_data)
print('Launch url: ', start_response.launch_url())


while True:
    time.sleep(1)

    try:
        collect_response = bankid_client.collect()
    except BankIdError as bie:
        if bie.message:
            print(bie.message[Languages.en])
            break
        else:
            raise Exception(f"Fatal error. reason: {bie.reason}; action needed: {bie.action}; response data: {bie.response_data}; response status: {bie.response_status}") from bie

    if collect_response.status == CollectStatuses.complete:
        print('Authenticated by: ', collect_response.completionData.user.name)
        break
    else:
        if collect_response.hintCode == HintCodes.outstandingTransaction:
            print('QR data: ', collect_response.qr_data)
        
        print("User Message: ", collect_response.message[UseTypes.qrcode][Languages.en])
        
        if collect_response.status == CollectStatuses.failed:
            break


API Reference


class BankIdClient()


def init(self, prod_env: bool=False, cert_pem: str=None, key_pem: str=None, ca_pem: str=None, request_timeout: int=None, messages: Messages=Messages, is_mobile: bool=False)

Parameters:

  • prod_env indicates if it's a production environment. Test or prod urls are chosen based on this. If it is True then key_pem, cert_pem and ca_pem are required. Otherwise test certificates which are already included in the package will be used. Any or all of the certificates can also be provided when the value is False
  • cert_pem file path of the certificate file
  • key_pem file path of the private key of the certificate.
  • ca_pem file path of CA certificate file
  • request_timeout number of seconds to wait for response
  • messages class or subclass of Messages class to override existing message
  • is_mobile if it's being used in a mobile device.

def auth(endUserIp: str, requirement: dict=None, userVisibleData: str=None, userNonVisibleData: str=None, userVisibleDataFormat: Union[str, True]=None):

Starts the bankid auth process. Use this when only user authentication is needed.

Parameters:

  • endUserIp Required. str. The user IP address as seen by RP. IPv4 and IPv6 is allowed.
  • requirement Optional. dict | object that can be converted to dict. Requirements dictionary on how the auth order must be performed. See BankID documentation for more details.
  • userVisibleData Optional. str. Text displayed to the user during authentication with BankID. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • userNonVisibleData Optional. str. Text that is not displayed to the user. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • userVisibleDataFormat Optional. The value can be set to "simpleMarkdownV1" or True. If it's True then the value is set to "simpleMarkdownV1".

Return: BankIdStartResponse

def sign(endUserIp: str, userVisibleData: str, requirement: dict=None, userNonVisibleData: str=None, userVisibleDataFormat: Union[str, True]=None)

Starts the bankid sign process. Use this when user is authenticated to sign something.

Parameters:

  • endUserIp Required. str. The user IP address as seen by RP. IPv4 and IPv6 is allowed.
  • userVisibleData Required. str. Text displayed to the user during authentication with BankID. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • requirement Optional. dict | object that can be converted to dict. Requirements dictionary on how the auth order must be performed. See BankID documentation for more details.
  • userNonVisibleData Optional. str. Text that is not displayed to the user. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • userVisibleDataFormat Optional. The value can be set to "simpleMarkdownV1" or True. If it's True then the value is set to "simpleMarkdownV1".

Return: BankIdStartResponse

def phone_auth(personalNumber: str, callInitiator: str, requirement: dict=None, userVisibleData: str=None, userNonVisibleData: str=None, userVisibleDataFormat: Union[str, True]=None)

Initiates an authentication order when the user is talking to the RP over the phone.

Parameters:

  • personalNumber Required. str. Any valid 12 digit personal number of the user.
  • callInitiator Required. str. choice between 'user' or 'RP'.
  • requirement Optional. dict | object that can be converted to dict. Requirements dictionary on how the auth order must be performed. See BankID documentation for more details.
  • userVisibleData Optional. str. Text displayed to the user during authentication with BankID. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • userNonVisibleData Optional. str. Text that is not displayed to the user. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • userVisibleDataFormat Optional. The value can be set to "simpleMarkdownV1" or True. If it's True then the value is set to "simpleMarkdownV1".

Return: BankIdPhoneStartResponse

def phone_sign(personalNumber, callInitiator, userVisibleData, requirement=None, userNonVisibleData=None, userVisibleDataFormat=None)

Initiates an signing order when the user is talking to the RP over the phone.

Parameters:

  • personalNumber Required. str. Any valid 12 digit personal number of the user.
  • callInitiator Required. str. choice between 'user' or 'RP'.
  • userVisibleData Required. str. Text displayed to the user during authentication with BankID. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • requirement Optional. dict | object that can be converted to dict. Requirements dictionary on how the auth order must be performed. See BankID documentation for more details.
  • userNonVisibleData Optional. str. Text that is not displayed to the user. Converted to UTF-8 encoded and then base 64 encoded string. 1 to 1500 characters after 64 encoding.
  • userVisibleDataFormat Optional. The value can be set to "simpleMarkdownV1" or True. If it's True then the value is set to "simpleMarkdownV1".

Return: BankIdPhoneStartResponse

def collect(orderRef: str=None, qrStartToken: str=None, qrStartSecret: str=None, order_time: int=None)

Collect the result of the auth, sign, phone_auth or phone_sign methods. If used from same client instance when order was initiated, it doesn't require any parameters

  • orderRef Optional. str. Can be found in response object from any order initiator methods. If given then corresponding order result will be requested. Usefull when the method is being used from different client instance than where the order was started.
  • qrStartToken Optional. str. Can be found in response object from any order initiator methods. If given, it will be used to calculate QR data.
  • qrStartSecret Optional. str. Can be found in response object from any order initiator methods. If given, it will be used to calculate QR data.
  • order_time Optional. int. Can be found in response object from any order initiator methods. If given, it will be used to calculate QR data.

Return: BankIdCollectResponse

def cancel(orderRef: str=None):

Cancels an ongoing sign or auth order. If used from same client instance when order was initiated, it doesn't require any parameters

  • orderRef Optional. str. Can be found in response object from any order initiator methods. If given then corresponding order result will be requested. Usefull when the method is being used from different client instance than where the order was started.

Return: BankIdCancelResponse


class BankIdBaseResponse()

response: requests.Response. object which was returned from sending the request. status: int. Http response code of the response data: dict. returned data in dict format url: str. The full url where the request was sent


class BankIdStartResponse(BankIdBaseResponse):

orderRef: str. Used to collect the status of the order. Parsed from BankID response. autoStartToken: str. Used to compile the start url. Parsed from BankID response. qrStartToken: str. Used to compute the animated QR code. Parsed from BankID response. qrStartSecret: str. Used to compute the animated QR code. Parsed from BankID response. order_time: int. order time in seconds since the Epoch.


class BankIdPhoneStartResponse(BankIdBaseResponse):

orderRef: str. Used to collect the status of the order. Parsed from BankID response.


class BankIdCollectResponse(BankIdBaseResponse)

orderRef: str. Used to collect the status of the order. Parsed from BankID response. status: str. 'pending' | 'complete' | 'failed'. Parsed from BankID response. hintCode: str. Only present for pending and failed orders. Parsed from BankID response. completionData: BankIdCompletionData. Only present for complete orders. Parsed from BankID response. message: dic. User message according to bankid documentation. which has following format:

{
    "qrcode": {             # order initiated by scanning qr code
        "swedish": "",
        "english": ""
    },
    "onfile": {             # order initiated by starting the bankid app on the same device
        "swedish": "",
        "english": ""
    }
} 
class BankIdCompletionData()

user: BankIdCompletionUserData. Authenticated user information. device: BankIdCompletionDeviceData. Authenticated user's device information. bankIdIssueDate: datatime. The date the BankID was issued to the user. stepUp: str. Information about extra verifications that were part of the transaction. signature: str. The signature as described in the BankID Signature Profile specification. Base64-encoded. ocspResponse: str. The OCSP response. Base64-encoded. The OCSP response is signed by a certificate that has the same issuer as the certificate being verified. The OSCP response has an extension for Nonce. The nonce is calculated as:

  • SHA-1 hash over the base 64 XML signature encoded as UTF-8.
  • 12 random bytes is added after the hash.
  • The nonce is 32 bytes (20 + 12). json: dict. Completion data in dict format.
class BankIdCompletionUserData()

personalNumber: str. The personal identity number. name: str. The given name and surname of the user. givenName: str. The given name of the user. surname: str. The surname of the user.

class BankIdCompletionDeviceData()

ipAddress: str. The IP address of the user agent as the BankID server discovers it. When an order is started with autoStartToken the RP can check that this match the IP they observe to ensure session fixation String. uhi: str. Unique hardware identifier for the users device.


class BankIdCancelResponse(BankIdBaseResponse)

...


class BankIdError(Exception)

reason: str. Reason of the exception according to BankID Documentation action: str. What action is needed for this exception according to BankID Documentation message: dict. Message for user. errorCode: str. Error code received in response data response: requests.Response. object which was returned from sending the request. response_status: int. Http response code of the response response_data: dict. returned data in dict format


class BankIdValidationError(Exception)

...

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bankid6-1.0.0.tar.gz (23.5 kB view details)

Uploaded Source

Built Distribution

bankid6-1.0.0-py3-none-any.whl (22.1 kB view details)

Uploaded Python 3

File details

Details for the file bankid6-1.0.0.tar.gz.

File metadata

  • Download URL: bankid6-1.0.0.tar.gz
  • Upload date:
  • Size: 23.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.12.2

File hashes

Hashes for bankid6-1.0.0.tar.gz
Algorithm Hash digest
SHA256 9a944bf54e1943f619a64dcbd105285aec6a53c560c110a99639ab218f2e7bb7
MD5 5f698c0d29d3688b6d3f731eabd60126
BLAKE2b-256 bca720def30745925156b83d7c6bfd487040b27b3ae64776528e3b63e0704cf0

See more details on using hashes here.

File details

Details for the file bankid6-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: bankid6-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 22.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.12.2

File hashes

Hashes for bankid6-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 61785520182dc97575de562c40d77fb1f2d3111d4816ebbb2930ed230078e454
MD5 e968602e876d722ad2268a30b7a524da
BLAKE2b-256 fe54242c8391375edc72bb72223995ef2301a862740c3cc1c9a161e9810850de

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page