From c556aefc8f273baf2b045f3c3867607e992afa8a Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Tue, 21 Feb 2023 18:57:38 +0000 Subject: [PATCH 01/12] SDK-2267:updated refs --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5a3982cd..a2b33630 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ install_requires=[ "deprecated==1.2.13", "cryptography>=2.2.1", - "protobuf==3.13.0", + "protobuf==3.21.1", "requests>=2.11.1", "future>=0.18.2", "asn1==2.2.0", From f4368e3a129de77390eb3cb39799f6c5e3d76021 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Tue, 21 Feb 2023 19:02:49 +0000 Subject: [PATCH 02/12] SDK-2267:updated refs --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a2b33630..5b68e6d0 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ install_requires=[ "deprecated==1.2.13", "cryptography>=2.2.1", - "protobuf==3.21.1", + "protobuf==3.20.1", "requests>=2.11.1", "future>=0.18.2", "asn1==2.2.0", From 683a073c655656c0adb5aa3c42bf0f618092b0ff Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 14:15:06 +0100 Subject: [PATCH 03/12] SDK-2242 added, create session, qrcode, and retrive session,receipt --- examples/digitalidentity/.env.example | 3 + examples/digitalidentity/README.md | 11 + examples/digitalidentity/__init__.py | 0 examples/digitalidentity/app.py | 134 ++++++ ...t Prod SDK application-access-security.pem | 27 ++ examples/digitalidentity/requirements.in | 3 + examples/digitalidentity/requirements.txt | 60 +++ examples/digitalidentity/settings.py | 16 + .../static/assets/app-store-badge.png | Bin 0 -> 4077 bytes .../static/assets/app-store-badge@2x.png | Bin 0 -> 8819 bytes .../static/assets/company-logo.jpg | Bin 0 -> 4682 bytes .../static/assets/google-play-badge.png | Bin 0 -> 4957 bytes .../static/assets/google-play-badge@2x.png | Bin 0 -> 11267 bytes .../static/assets/icons/address.svg | 3 + .../static/assets/icons/calendar.svg | 5 + .../static/assets/icons/chevron-down-grey.svg | 7 + .../static/assets/icons/document.svg | 3 + .../static/assets/icons/email.svg | 14 + .../static/assets/icons/gender.svg | 5 + .../static/assets/icons/nationality.svg | 3 + .../static/assets/icons/phone.svg | 3 + .../static/assets/icons/profile.svg | 3 + .../static/assets/icons/verified.svg | 6 + .../digitalidentity/static/assets/logo.png | Bin 0 -> 2988 bytes .../digitalidentity/static/assets/logo@2x.png | Bin 0 -> 5609 bytes examples/digitalidentity/static/index.css | 152 +++++++ examples/digitalidentity/static/profile.css | 420 +++++++++++++++++ examples/digitalidentity/templates/index.html | 98 ++++ .../digitalidentity/templates/profile.html | 173 +++++++ .../templates/dynamic-share.html | 2 +- .../yoti_example_flask/templates/index.html | 2 +- examples/yoti_example_flask_di/.env.example | 3 + examples/yoti_example_flask_di/Dockerfile | 12 + examples/yoti_example_flask_di/README.md | 6 + examples/yoti_example_flask_di/app.py | 124 +++++ .../yoti_example_flask_di/docker-compose.yml | 12 + .../yoti_example_flask_di/requirements.in | 11 + .../yoti_example_flask_di/requirements.txt | 79 ++++ examples/yoti_example_flask_di/settings.py | 5 + .../static/assets/app-store-badge.png | Bin 0 -> 4077 bytes .../static/assets/app-store-badge@2x.png | Bin 0 -> 8819 bytes .../static/assets/company-logo.jpg | Bin 0 -> 4682 bytes .../static/assets/google-play-badge.png | Bin 0 -> 4957 bytes .../static/assets/google-play-badge@2x.png | Bin 0 -> 11267 bytes .../static/assets/icons/address.svg | 3 + .../static/assets/icons/calendar.svg | 5 + .../static/assets/icons/chevron-down-grey.svg | 7 + .../static/assets/icons/document.svg | 3 + .../static/assets/icons/email.svg | 14 + .../static/assets/icons/gender.svg | 5 + .../static/assets/icons/nationality.svg | 3 + .../static/assets/icons/phone.svg | 3 + .../static/assets/icons/profile.svg | 3 + .../static/assets/icons/verified.svg | 6 + .../static/assets/logo.png | Bin 0 -> 2988 bytes .../static/assets/logo@2x.png | Bin 0 -> 5609 bytes .../yoti_example_flask_di/static/index.css | 174 +++++++ .../yoti_example_flask_di/static/profile.css | 429 ++++++++++++++++++ .../templates/dynamic-share.html | 84 ++++ .../templates/index.html | 88 ++++ .../templates/profile.html | 160 +++++++ yoti_python_sdk/__init__.py | 5 + yoti_python_sdk/activity_details.py | 9 +- yoti_python_sdk/anchor.py | 4 +- yoti_python_sdk/attribute.py | 6 +- yoti_python_sdk/digital_identity/__init__.py | 5 + yoti_python_sdk/digital_identity/client.py | 259 +++++++++++ .../create_share_qr_code_result.py | 38 ++ .../create_share_session_result.py | 48 ++ .../get_share_qr_code_result.py | 47 ++ .../get_share_receipt_result.py | 71 +++ .../get_share_session_result.py | 75 +++ .../digital_identity/receipts/__init__.py | 0 .../digital_identity/receipts/base_content.py | 29 ++ .../receipts/crypto/__init__.py | 0 .../receipts/crypto/decryption.py | 86 ++++ .../digital_identity/receipts/crypto/utils.py | 43 ++ .../receipts/proto/EncryptedData.proto | 8 + .../receipts/proto/EncryptedData_pb2.py | 26 ++ .../receipts/proto/__init__.py | 0 .../receipts/receipt_item_key_response.py | 38 ++ .../receipts/receipt_response.py | 107 +++++ .../digital_identity/receipts/user_content.py | 35 ++ .../dynamic_scenario_builder.py | 1 + yoti_python_sdk/profile.py | 2 +- .../tests/digital_identity/__init__.py | 0 .../test_create_share_qr_code.py | 50 ++ .../test_create_share_session.py | 58 +++ .../test_get_share_qr_code.py | 65 +++ .../test_get_share_session.py | 29 ++ .../digital_identity/test_share_receipt.py | 32 ++ .../retrieve/test_get_session_result.py | 4 +- yoti_python_sdk/tests/test-key.pem | 51 +++ yoti_python_sdk/tests/test_client.py | 12 +- yoti_python_sdk/tests/test_profile.py | 3 +- 95 files changed, 3622 insertions(+), 16 deletions(-) create mode 100644 examples/digitalidentity/.env.example create mode 100644 examples/digitalidentity/README.md create mode 100644 examples/digitalidentity/__init__.py create mode 100644 examples/digitalidentity/app.py create mode 100644 examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem create mode 100644 examples/digitalidentity/requirements.in create mode 100644 examples/digitalidentity/requirements.txt create mode 100644 examples/digitalidentity/settings.py create mode 100644 examples/digitalidentity/static/assets/app-store-badge.png create mode 100644 examples/digitalidentity/static/assets/app-store-badge@2x.png create mode 100644 examples/digitalidentity/static/assets/company-logo.jpg create mode 100644 examples/digitalidentity/static/assets/google-play-badge.png create mode 100644 examples/digitalidentity/static/assets/google-play-badge@2x.png create mode 100644 examples/digitalidentity/static/assets/icons/address.svg create mode 100644 examples/digitalidentity/static/assets/icons/calendar.svg create mode 100644 examples/digitalidentity/static/assets/icons/chevron-down-grey.svg create mode 100644 examples/digitalidentity/static/assets/icons/document.svg create mode 100644 examples/digitalidentity/static/assets/icons/email.svg create mode 100644 examples/digitalidentity/static/assets/icons/gender.svg create mode 100644 examples/digitalidentity/static/assets/icons/nationality.svg create mode 100644 examples/digitalidentity/static/assets/icons/phone.svg create mode 100644 examples/digitalidentity/static/assets/icons/profile.svg create mode 100644 examples/digitalidentity/static/assets/icons/verified.svg create mode 100644 examples/digitalidentity/static/assets/logo.png create mode 100644 examples/digitalidentity/static/assets/logo@2x.png create mode 100644 examples/digitalidentity/static/index.css create mode 100644 examples/digitalidentity/static/profile.css create mode 100644 examples/digitalidentity/templates/index.html create mode 100644 examples/digitalidentity/templates/profile.html create mode 100644 examples/yoti_example_flask_di/.env.example create mode 100644 examples/yoti_example_flask_di/Dockerfile create mode 100644 examples/yoti_example_flask_di/README.md create mode 100644 examples/yoti_example_flask_di/app.py create mode 100644 examples/yoti_example_flask_di/docker-compose.yml create mode 100644 examples/yoti_example_flask_di/requirements.in create mode 100644 examples/yoti_example_flask_di/requirements.txt create mode 100644 examples/yoti_example_flask_di/settings.py create mode 100755 examples/yoti_example_flask_di/static/assets/app-store-badge.png create mode 100755 examples/yoti_example_flask_di/static/assets/app-store-badge@2x.png create mode 100644 examples/yoti_example_flask_di/static/assets/company-logo.jpg create mode 100755 examples/yoti_example_flask_di/static/assets/google-play-badge.png create mode 100755 examples/yoti_example_flask_di/static/assets/google-play-badge@2x.png create mode 100755 examples/yoti_example_flask_di/static/assets/icons/address.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/calendar.svg create mode 100644 examples/yoti_example_flask_di/static/assets/icons/chevron-down-grey.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/document.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/email.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/gender.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/nationality.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/phone.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/profile.svg create mode 100755 examples/yoti_example_flask_di/static/assets/icons/verified.svg create mode 100755 examples/yoti_example_flask_di/static/assets/logo.png create mode 100755 examples/yoti_example_flask_di/static/assets/logo@2x.png create mode 100644 examples/yoti_example_flask_di/static/index.css create mode 100644 examples/yoti_example_flask_di/static/profile.css create mode 100644 examples/yoti_example_flask_di/templates/dynamic-share.html create mode 100644 examples/yoti_example_flask_di/templates/index.html create mode 100644 examples/yoti_example_flask_di/templates/profile.html create mode 100644 yoti_python_sdk/digital_identity/__init__.py create mode 100644 yoti_python_sdk/digital_identity/client.py create mode 100644 yoti_python_sdk/digital_identity/create_share_qr_code_result.py create mode 100644 yoti_python_sdk/digital_identity/create_share_session_result.py create mode 100644 yoti_python_sdk/digital_identity/get_share_qr_code_result.py create mode 100644 yoti_python_sdk/digital_identity/get_share_receipt_result.py create mode 100644 yoti_python_sdk/digital_identity/get_share_session_result.py create mode 100644 yoti_python_sdk/digital_identity/receipts/__init__.py create mode 100644 yoti_python_sdk/digital_identity/receipts/base_content.py create mode 100644 yoti_python_sdk/digital_identity/receipts/crypto/__init__.py create mode 100644 yoti_python_sdk/digital_identity/receipts/crypto/decryption.py create mode 100644 yoti_python_sdk/digital_identity/receipts/crypto/utils.py create mode 100644 yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto create mode 100644 yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py create mode 100644 yoti_python_sdk/digital_identity/receipts/proto/__init__.py create mode 100644 yoti_python_sdk/digital_identity/receipts/receipt_item_key_response.py create mode 100644 yoti_python_sdk/digital_identity/receipts/receipt_response.py create mode 100644 yoti_python_sdk/digital_identity/receipts/user_content.py create mode 100644 yoti_python_sdk/tests/digital_identity/__init__.py create mode 100644 yoti_python_sdk/tests/digital_identity/test_create_share_qr_code.py create mode 100644 yoti_python_sdk/tests/digital_identity/test_create_share_session.py create mode 100644 yoti_python_sdk/tests/digital_identity/test_get_share_qr_code.py create mode 100644 yoti_python_sdk/tests/digital_identity/test_get_share_session.py create mode 100644 yoti_python_sdk/tests/digital_identity/test_share_receipt.py create mode 100644 yoti_python_sdk/tests/test-key.pem diff --git a/examples/digitalidentity/.env.example b/examples/digitalidentity/.env.example new file mode 100644 index 00000000..950e65a9 --- /dev/null +++ b/examples/digitalidentity/.env.example @@ -0,0 +1,3 @@ +# Required Keys +YOTI_CLIENT_SDK_ID=yourClientSdkId +YOTI_KEY_FILE_PATH=yourKeyFilePath diff --git a/examples/digitalidentity/README.md b/examples/digitalidentity/README.md new file mode 100644 index 00000000..2bfa2f8d --- /dev/null +++ b/examples/digitalidentity/README.md @@ -0,0 +1,11 @@ +# Yoti Share v2 Python Example + +## Running the example project + +1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values +2. Add your SDK ID to the [templates/index.html](templates/index.html) file +3. Create a virtual environment with `python3 -m venv .venv` +4. Active the environment `. .venv/bin/activate` +5. Install the dependencies with `pip install -r requirements.txt` +6. Start the server `flask run --port 8000` +7. Visit `http://127.0.0.1:8000` diff --git a/examples/digitalidentity/__init__.py b/examples/digitalidentity/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/digitalidentity/app.py b/examples/digitalidentity/app.py new file mode 100644 index 00000000..515c5ddd --- /dev/null +++ b/examples/digitalidentity/app.py @@ -0,0 +1,134 @@ +import json, requests +from flask import Flask, Response, request, render_template + + +from cryptography.fernet import base64 + +from settings import YOTI_API_URL, YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH + +from yoti_python_sdk.digital_identity import ( + DigitalIdentityClient +) + +app = Flask(__name__) + +@app.route("/") +def index(): + return render_template("index.html") + +@app.route("/sessions") +def sessions(): + session_config = { + "policy": { + "wanted": [ + { + "name": "date_of_birth", + "derivation": "age_over:18", + "optional": "false" + } + , + { + "name": "full_name" + }, + { + "name": "email_address" + }, + { + "name": "phone_number" + }, + { + "name": "selfie" + }, + { + "name": "date_of_birth", + "derivation": "age_over:18" + }, + { + "name": "nationality" + }, + { + "name": "gender" + }, + { + "name": "document_details" + }, + { + "name": "document_images" + }], + "wanted_auth_types": [], + "wanted_remember_me": "false", + }, + "extensions": [], + "subject": { + "subject_id": "some_subject_id_string" + }, # Optional reference to a user ID + "notification": { + "url": "https://webhook.site/818dc66b-e18b-4767-92c5-47c7af21629c", + "method": "POST", + "headers": {}, + "verifyTls": "true" + }, + "redirectUri": "/profile" # Mandatory redirect URI but not required for Non-browser flows + } + + digital_identity_client = DigitalIdentityClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH, YOTI_API_URL) + + share_session_result = digital_identity_client.create_share_session(session_config) + + session_id = share_session_result.id + + # create_qr_code_result = create_share_qr_code(share_session_result.id) + # get_share_session_result = get_share_session(share_session_result.id) + # get_qr_code_result = get_share_qr_code(create_qr_code_result.id) + + # Return Session ID JSON + return json.dumps({"session_id": session_id}) + +@app.route("/create-qr-code") +def create_qr_code(): + # Get query params - sessionId + session_id = request.args.get('sessionId') + digital_identity_client = DigitalIdentityClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH, YOTI_API_URL) + +# Create QR Code + create_qr_code_result = digital_identity_client.create_share_qr_code(session_id) + + # Return QR Code ID and URI JSON + return json.dumps({"qr_code_id": create_qr_code_result.id, "qr_code_uri": create_qr_code_result.uri}) + +@app.route("/render-qr-code") +def render_qr_code(): + # Get query params - qrCodeUri + qr_code_uri = request.args.get('qrCodeUri') + # Make a POST request to the API to create a QR Code image + url = "https://api.yoti.com/api/v1/qrcodes/image" + payload = { "url": str(qr_code_uri) } + headers = { + "Accept": "image/png", + "Content-Type": "application/json" + } + + response = requests.request("POST", url, json=payload, headers=headers) + + # Return QR Code Image as PNG + return Response(response.content, mimetype='image/png') + +@app.route("/profile") +def profile(): + # Get query params - receiptId + receipt_id = request.args.get('receiptId') + digital_identity_client = DigitalIdentityClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH, YOTI_API_URL) + + share_receipt = digital_identity_client.get_share_receipt(receipt_id) + age_over_verification = share_receipt.userContent.profile.find_age_over_verification(18) + selfie = share_receipt.userContent.profile.selfie.value + attribute_list = share_receipt.userContent.profile.attributes + full_name = share_receipt.userContent.profile.full_name.value + data = base64.b64encode(selfie).decode("utf-8") + selfie_image = "data:{0};base64,{1}".format("image/jpeg", data) + age_verified = age_over_verification.result + + return render_template("profile.html", age_verified=age_verified, selfie=selfie, full_name=full_name, selfie_image=selfie_image, attribute_list = attribute_list) + +if __name__ == "__main__": + app.run(host="0.0.0.0", ssl_context="adhoc") diff --git a/examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem b/examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem new file mode 100644 index 00000000..c3fc779a --- /dev/null +++ b/examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxtXhHY0ZxiTWSGjC8Y0EHsXpmV3FPJo6Dne1skP9e7pe2i0I +5tmvPVcQsOckrBjVP/H4G+Lzqtu7JfbD5BHZeqrec6PEITGBsrTAhcLjVQzUefN/ +mFkOinlOXBuPxMP5hZttFrNjqPjRhhlYrtQV2uOh/F/UGpqupgtJPKgF6mquDEGA +lHAxCDMwwl+NtZgg1N5w7pDUJYc/cNwI8X0afTTLneCqTSD+t9dARVgcXpnJm1WK +0XyJ4u6ENgN1fUcKPC3MP2isbTZe895dAVlvUH2ADD8qYfP8Nz8tcQ3DbXfjJC73 +hvuawmZIzotHqj2p6Oh9E6F3nLwlQPE18a599QIDAQABAoIBAArWI710T/QKVGpg +WUWIYbHap/NNlr8JicH5mLu1NGauntZFr49DTGdrrBN0GX3OoaqpQZQlf5GvhYjZ +ZM40gdWLY/HJ+lmzxMWMT9TKbRDY0OivkmPncKEv4MsozmJTKvFy6dRbpQITw3mL +PpfSo7lJAC5Mu7bSeNPAWDa30pC2tHtxjtoAstdABgqDMLpwD19O3FkWowR5q+6l +hFHi+bk+pzMc2X51PB1zsbpC+7US5p+N+qeo3ebrnncmj3yvh5OSah1NY+2CDWfR +gLyu0LrrGdgBg2lpAHdQvvQJhnQzVArKhItwVmW1TIcze0oNsZV/BOJetLj4lctz +Ih4xUjECgYEA+ZHJLsOMFmtFKWEOgDMrkRkfLpbgf0HuPM17InDLPDTaUeGjyTdM +CSxQiqbIW6g3bzHZ5idRhVYIPMgZ7/71X3Nueif5nNG5PHhiuNq7o1R2iuJ6FKms +p+/G9NsYG7THm2QlanFqp87hyuMkSt08Ugicb1GaOLs7UAcuCfnnF2UCgYEAy/Vw +5ezN5XiC7XxI/XszQRAuBPnHlQLMSAyUvZ6v2oEFK4nP2ysH5VnxGJFgXGIfykec +IdUnFtenTde8gCdueqFT2co0inslxVV8ETmEO8dJnPqnnXBunX0n5D3WqDBmJwXK +D2POa7xeXM5tdOxT61S0mSKVgtCQ9VU38OdHy1ECgYAZedpRncCVIUokGTZDu/V8 +kFXwiZJNK0vIhSlGsMDuWm7W4PO5PJ3UaeOm47OcN6XBAhO+PNFDjS62Fa8gIqSl +o8DpU19VtMr180wQlrOEzsBzGP9hUJjBY+apZBwn5+JgaG6xWPaMPsAp19oCkmbv +8NUXP/tAQ0ygtLrsZchDSQKBgHWcJ6j+H1CGaIFHXNOGWmzXRqIp4pOjlGarko2x +VthZ88BCbLCGJLx1W9h95CIBlzFOj9LWlf7PBjOWBqWjl0pxguegeSGtl38uJyfL +kdvitCkoRMU9kxuPkxRDMGe12QIBjZ3IQLzRV1yO0IFO0alvI+D2F17io+REasio +pTaxAoGBANSMXb+sAnXgyu1hK/fOI5QlKNqBfK/PcwrI0nYwHzIqs5Xs5/k5NLH9 +yZyIcDrrk5DJDWJ63gA0WjSL5oHzo5IOHSAkcMc/H7L/4rNFdYNmjkMaTK3Ms4In +xZcz7HaJ1Rxvh/y/lyDn27hsm30WpNfi6lcawBFHCTYdfyWIXj8P +-----END RSA PRIVATE KEY----- diff --git a/examples/digitalidentity/requirements.in b/examples/digitalidentity/requirements.in new file mode 100644 index 00000000..b0d3fbd8 --- /dev/null +++ b/examples/digitalidentity/requirements.in @@ -0,0 +1,3 @@ +flask>=3.0.1 +python-dotenv>=1.0.1 +yoti>=2.15 diff --git a/examples/digitalidentity/requirements.txt b/examples/digitalidentity/requirements.txt new file mode 100644 index 00000000..5cece086 --- /dev/null +++ b/examples/digitalidentity/requirements.txt @@ -0,0 +1,60 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --output-file=requirements.txt requirements.in +# +asn1==2.2.0 + # via yoti +blinker==1.7.0 + # via flask +certifi==2023.11.17 + # via requests +cffi==1.16.0 + # via cryptography +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via flask +cryptography==42.0.1 + # via + # pyopenssl + # yoti +deprecated==1.2.10 + # via yoti +flask==3.0.1 + # via -r requirements.in +future==0.18.3 + # via yoti +idna==3.6 + # via requests +iso8601==0.1.13 + # via yoti +itsdangerous==2.1.2 + # via flask +jinja2==3.1.3 + # via flask +markupsafe==2.1.4 + # via + # jinja2 + # werkzeug +protobuf==3.20.1 + # via yoti +pycparser==2.21 + # via cffi +pyopenssl==24.0.0 + # via yoti +python-dotenv==1.0.1 + # via -r requirements.in +pytz==2022.1 + # via yoti +requests==2.31.0 + # via yoti +urllib3==2.1.0 + # via requests +werkzeug==3.0.1 + # via flask +wrapt==1.16.0 + # via deprecated +yoti==2.14.2 + # via -r requirements.in diff --git a/examples/digitalidentity/settings.py b/examples/digitalidentity/settings.py new file mode 100644 index 00000000..cc48730b --- /dev/null +++ b/examples/digitalidentity/settings.py @@ -0,0 +1,16 @@ +from os import environ +from os.path import dirname, join + +from dotenv import load_dotenv + +dotenv_path = join(dirname(__file__), ".env") +load_dotenv(dotenv_path) + +YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID", None) +YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH", None) +YOTI_API_URL = environ.get("YOTI_API_URL", "https://api.yoti.com/share") + +if YOTI_CLIENT_SDK_ID is None or YOTI_KEY_FILE_PATH is None: + raise ValueError("YOTI_CLIENT_SDK_ID or YOTI_KEY_FILE_PATH is None") + +YOTI_APP_BASE_URL = environ.get("YOTI_APP_BASE_URL", "https://127.0.0.1:8000") diff --git a/examples/digitalidentity/static/assets/app-store-badge.png b/examples/digitalidentity/static/assets/app-store-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec996cc6288d68279c1d735c9d627c64d8a48c6 GIT binary patch literal 4077 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 literal 0 HcmV?d00001 diff --git a/examples/digitalidentity/static/assets/app-store-badge@2x.png b/examples/digitalidentity/static/assets/app-store-badge@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..84b34068fc22aa74f388f6db1a583ac6592f5420 GIT binary patch literal 8819 zcmXY11yoeu^CtwPMd=g?0YMs(F3AO?JC{_tq(d5(&K2nvBp0L?cIoa;>8_>gzkYxJ zGv~bb&O2xBotgXIy)&QBM1EG0!^fe-K|w*mSCE%cM?pd5K|UKk$3$KaRX-XaZ>X;7 za#AQ2!_>PdC~r6vWF$2`Q4ce*@2xd%dg2O=qTeV`=6+I(Q8!DDBsD1OSS^$(`V{?H z);45(OUs5$sX#XICB?ubAi2nr{Zne*=aa>hf_7l#>#5h-p#Xji|8&fSNm+~dB+kMtJ3J2N!;+SG~2W{F42 zs2UEtR@@EI}$!1_PufBa|7i_fN%AiIm#JQ%MqULuA06^3?rLSxE6-{i^U z^nY%n$_1sES`g}cjwfXyat?j=JoDN$^V<70N_ksvYyL*P`(giQX}aa&wwIRm_`=Y>G0XImejb7B#9$55#2 zgV5gy2D^m{=;Q5hoqZnUY5A(n`tEY7(%+r!!u~Wlt(W#RLN0whPJH>G?e3^hwMYE% zG)~>nfT*mzCh8+%RQl`K)5cqIZo2zbtf5dcW8Dl+Cb0(Ac0jOmz;@<0F`MVyh3*z{ z{)qgco9%2mOLLL6>_o7N`&)fi8pVw&@nNCq1 z*y&;BaxJfl|s7{g91MAjw5kTRLJ@BVJ(sos)6XyLO9^jw-$!;F8y1}It}R)@)NQmW!E+J zKMAJWaKCa59ye-Iu3ZbG((k$>(J?gJzuPzYy`8GAW#G_QIOl(NIHZw%tJxeMEOl3q zq{1^ElX}(^!BIa8Q*7O@gtj$a&VL=UEzY#MTOGf9F!4F88!FeUwR-|Dk8RZs3k~?E z!vI$JDm@GuuiN|x@bML zcO0l){IJDmb5e{X}oXTN?s;Cma^gd_V8O5Eh%A0u?f?`dv4zFWn=YfHj?*;D?NJ zozXhRM(8)e-8h7eK#U~Yd6?Z0{|p3ga7?PHPDFgO?QZFKzJSyJG}^0$5hqPF7`y7n zp3_gH^8$w_aKe#w`J@ft`f=;kGGJV%9!?-^A$-&TWXA64MlNu`Fzr=C4@v(dcg@4) z!bs}6<(zf4ACCffuA9p7r;*2;rI%l4qsM7wsYg+0yxly+Hq}hYySELw&&}O)g`{Zf zm&Rvn*fCXY2S;iV#oTtbf3TjZu_J3$Wmk-B9oj~(dd;`Otr zOkh}nsl{#-r)Za)3D8wc_mc7i`NxV)xUs1Qa|=EnlKVvtKDgzXbDT48nH9_8pVo!* z=)4qv*i|JPzxO?&Rp>zFY>M7~B7T<*07n zZom1g|J}K#6Ks}Ro#ZhofCD#@mJrOo(?>y1PiV$4P54ICklx#mS%itn=Pc6l)8 zC2}2ah&sQqzODCk=onCIn_*wCM?W<3A?&V8LuC$nvz0;R$05@!xPxnrz=(aT@?F=B z3q=Tj#n)nM5G+8Onas%GbL>9~McEM?#rPN>V(F+y^XU&}4sd+Hl&`LK?XdptX`N~m zZ1yH^H`F```SQUBv7`Yun(+h}rtGz}YZvtrv{pfPY zdQ|(5nJDylQ(vhFcI>4aqTPyOfd~;A5;h=Q<*}p-bn`Rq5gBljs@TV3EHfKv>Z&nW zDm}{iK!56`=lOEy9luLH6lR#z_dR?fGqM;p6B;GvCcFcNxRtWo3G9qqJCR`ynMVlV z`x$0%Z6-gk_yXH=ZhGa!%qw+oEh2rod9%zi*jkgv5aY||=0ccc8g@^Yf1pFnJ<&nx zRIiUK10KExr-;&_`2CJye0#o5_cY74V-~@(fZD?XCmUvdJjPP;q|5mTLS?=wkrVA# z{nZj)ipI`Z)lT$%-{ksxaBzJ!5FO>%|Qi+jQB zjz-_)eXN4C{@SZN=!oID6b;lFk=Rx_ewkQ3&J6e{+l`BI^5(3DcgkGh<4=b=1%#w? zE%gW)rNV)0_MM?Iz8?^q&Ne1(PMOIlEUjfDXr0`2PprKcnLJ$S$!@ki&Qn^}a>WEE zbcmFa)ne@i-v5<`ejCqAKDW9>>@w97&5%Lq093YgfeMjHo~9w(5>it7GHk&?@O$6q z#oGS*2gXg3Uq2Bhb@54h|B({F1EaDZf`7!lTy(4ZbsW691(p}LElyaQr7UPuOWr-6 z;Ef{XD^J1-J}Q`*d3nce_rsI5juJR5xDG;l1w5&-CCwxZR>4`*&(w!+#qd#eSK6KU z!Ks!_PmviwHkXc(yB2Ek#J;I?Sjgt;Xex4V@@@uaR8@*k;N%XN+gBN)_y}S-uqlw2 z5>kC!MW?$C9E+-K-Nq6UEW0)a4v~EK@?ED|sgktY@vUT&uN7oPk$M2fP@lz3_*C;+ z=&lx=_+2f{90^irx56umho-_57jK&^kA1g=4_z9PK{ru2N{i)>)b5jN9({(LCFSr! zvuFl}F2J}R^O*5^#q)HY9gLbX-f~Ez%liG54oCHILe0Dbr8UVo$Nm%2+J1zK+(Hjd zB6;8;dA-F(Qh}v?TZBcITGLM(J?^`ouV)=^9z5I=B|(t*t+P{+z*)sNWk3{r=bM{K z{W?ZvT)dles}$wIj%g0og=r>CgA%1dArm!{;1MpO>+2qI2c1fOjv7l?eeK!aIO5E} zJKLq7by`+&Jd5KzT>L&au*WgLbL1R8_-qy2icA(pbR&~rE_FX3qd%yda52#Om!z`F z_Fm+sPxXC4bsGLg@iW`k9u>O9wLpMYRmhS_)c-XH64R0jf!2duP}>FoOXM8rk0jok7h63<-*+W4w>tQ&OLGg26QaW zkDO4#Q8c*fN=RoN7EqJC72VhZppg3B=u7_cS?ZtU`PiFaAf$qNi#$CF<=djOTqy`n zcGnZqvF3Vl8fRARxVPFL`#r&yOvXVB&B5-5TI>EpR^=6k)wDfL|T79yrX zfw7#=5c=1&KzahoH81;I(>U~W3j_vJQ6)J9^<$^Z_}KfPFfjEzkj^^d=^ za_k+x;6LB)Bhvvbgf9jxnsE?Yc#jzrC&rlBqkr_OVn)Mh`F z4-5h9m54n*YrJ;g7GzGeQM4tg21?C*piIskuK#rmE0xHT$ZMZv8J+*dL}gQFCte?W z8U4H`S~o#>ebj%T`2Jtf3e0a$oMyjUc{utO_d6~4wYjUExq0yC_bCd*A%WX6+6#LJr*C#W?Y~;~>~ju@rFceNGmdUx z_%iOi!@JZ|-JI}1Cc&nbXG1I~G|nXv{;5Lef0 zRq8QYuX5k)ZZ45p^h9F;fo->ywY~i>j)k%#Ea|3tf6KFH8MX#0@-V4%(Z)0LguVe; zH}UP@Anhbb+#z|V3%QIAY8;i3PdfFE8U_#HuAxt}l=z{~AVfz%JU%}v%uEDx^wI0N z(XQ$jxlJr6O|Y&n^c%@f@!|@IDlMnW)ljv-u6(twThuFzDvji~B+b|@bl+D+#v6Y) zs1IMcPcsJe^ZMdI@))8U$!Ps&Fw4fSM!9tH;T)k!P$8@UL8?wo8TBqFBGt2bMm?%b zcfCCfp)R_I9l2>QhGnjk*dV#*)I@(VNStV0ltNpdBfe7i=jIKHZA zpkZ{2@jF`|hraP6gI(9XR2Ft^ME9bxR}sC?{Fd?L$_z}KU-U3YCv5`uyS&-OTye`q zm!8r7iRBW{DYqTTIyL?(z-wp1_j%l^>qXI1b&JyBPA_8ftVg2wh*0FDeO^*zIH(Vo zVqCGz;bty2)+twR#JRpYx@-GW_NM~<1tm;>miLek4$gCOXfmVdO z-=E9gIlMFwix;)tJ@oA~qp02~YgM24v&J=*iQ{+{4VJ}z(n7UtM&5+&;Iw(6%t@KO zqd(=scAY8BCotSY4y=Uh_BBRNMj8wW+}K&FLMfkFK|VkB1emx%P4aB+bCIg1iE`N~ z_Pyh89rz>Fo}q_-7E5klOfN*$KnFm0-})N*2*97@8`Q$N1O2ATjI|`-UP&mJ&Ho~d zYb$Trpyhz}^F5Q1X;wu89525l1$aj9>RghqZA~W{1siqKPGo|h(&YYGI0X2b6 zQotgws>P!7eU@+x&4<7At9dk6J7Uy62i2EVK=Jo2+b9+`OivG~YVBMoc&^SGLkYg) z(WHx7;qDD5N6p6$^>bEKWRQmKk!(k}FQ`DE`mOoebOzxofJXTE#tocGY)|+Ql0*|; z_Pbs`3qaAGM;$w_(a$$DJTc!=`r}(H`y;Z_A=JXl#Br4frj;+7i4{K5&5P8K%HJR3 ztBv1IK3t4EXEuLzL;89cRds5y!uJX=W}_W?@Ldh!u-7m-l=v0;_}BwZB1o_ z&I{;F$%$aKBX9-rfB#CFXmZQ*$!Dwn3XkT2l>LhHQ~{i=4f2jaZrmNDY+K^vCc4Zo zb|X5=hdsu6B*h4fBByw}8$6O9ZogbZb~gML%F-HUE+xmc&8j+$wb^Gg@MyhgPs{2- zk?R|a2_~VPQ7GH*oB#$SZB8H1ib;aj&jvUvORA}taOFrGc1Z_PSY^i*n0ISxhO{n- z!~wG1V{S(_%{J2S-cX?Pkd@kr%Rz3cY{v2ciJC>|cK18?V%@!h%$yz;=Ef{8(hov8 zMKwn5ZZzup!XQ9#%Z4?M?9O;3i?N7TmHpBnOR#Kol`5WnclNB`!AfiHm!~rH5F85uw}qgz!jvXzUeYQvVD2XEGPBzC_jX}vMh9&^AuJ>=pM z>c$;+A3e1OgP3|A1IhX5O+D5KpEKfqdtE*dEwi~@+O!SoVQYU>#Hdr{Hvn^;B$ z@b_?Oy2xlW!f8Bo={ZrwD!9F4=qp5JyGtjNP-oMB^P=_K2C4|{7NFS*h6oCf!+eQd zydk>&WPcVMno0J)p{g)ge_1;VeJXi2Iur6j_+C3|n=1o97>G=xQv|sb-QSkoksLH` zXQf!eJes($R=dfAOgIx?j61MoHVBzN1@rI})zklUpVq5;km@2rOXTt{ud>f2K1c)3 zM~*q_c86LI{n)hrbJcoz>T*&`NbPX2M|B?H0|JES-SX`qL^-F`9>j zO;xvO0t<5J%>JTz0fgg!U{Bc$P4-hmt-Op#J>w>E_#SNQ_FVqER#Wb;`X5xt+mM6G0^Z1QEn;f?c?QlF4|GCAwt zLl~9Nqrc{w7z9#8AYv{k-7B^ABM{-lU)sgU8l!AO97XTUxkHAvZ0Hwnv4(Zm z7M|PZoY(5MTJv!yCs+|tq%LA@gJTcz9RCpe3$2>0W!C{6l)w~3p}c@I1)TjA8ZQ!S zO$WP}F`<)9fzfC71#%y~4qUUR8f9Z>-b^*XGht$lUfA(55xl86O`&9RN|j_$UNhc` zV2Itp8bIZ^|= zwa=>8D~C8I@Aj;VB|!bfxI{98beNOrJ_;NKnuH?v_lQnC03Kr*Us-qj^jm^o*9XO%+_oUwxO#s?`Bu|@1#dXDOT_8L ztA8BGm^N4bZxzvjB-G;<%7JnCJ*7A8X$C$l4<$Q(8q@%|#g{;pHb6{8OzM*Mas zKm787N;}Ey#rkN>*m8Wr>EAOgJ2YQ4Fy&1Shpcx0#rob6kW+~rdQe?{MPe-K`MefZS{+Q*!cT%*O>5A=RZ5Lt@8{k?C50eu?Q)1!KRtoI4u3<>I+ zO}3k*+m<-QJ0sdJ>a?ng*D@#tQbo>&=g$v1Mo}C!&G5LK>v&?@YL^(F;orA8h2I^N z951XqdO%{+CE0IIOVnmeK*m7X!n>np$5KNQ0z4I;wbA6TE#jQv30Yk_ZA7B!m(y!Y zu1CBe0eM*DG||FRU=KcY_=Gm!@DdajXmG?;NoU3I%~K4;y%SXMv7al1xtYJcIL=C! z510X7NhMhM^L37n0Wd||`Z7h1x}$qu_fD25d?xC}iNz@4`}(GkcH9@WVU^}Qp6>BO z`ja(A$OPh3+>6j96{fiS!usj8)$%^_q!L(%zq}y|e<4=J@+s{bTcNTA9(cE>NKhxi zQRW9nVbtSh^A{Po_j=lNkyacF=T0W|U8iVhV)JT;)vn_j6IHBTmWePCJ9G-XC z^1R#X)%^Ks$^Ko$L{m@i4p+D>1Y4e`wZ0VZyu;9Yx3KW2-_Lyoonh-1gAm8epe!Bq z_1WE#sX)U|$?n=)$lOIU8H6Oh`S00-ViL{Nykn^-fUBFrIt*2}<4(J_D8 z75P_7tlMes6>yj!O=Me0o#gR!=ew))PFI+J+R6iOk*O5@YA#wBeQ2BI2^ZHb`;=@t zKio9}&rHcZn1MOvgQZ2X_*Obl@j606Xfr<$!BU%yR}`+wU++f~aDO_z0(Skmdn>f{ zRu8y4^kGa(%K02jV-)6JU z1L6HqTssX8#Y8;st*TFTaOcZ3)o=FUn7U?(FrJ<_iM`I-HDmN?+aT!bJr7=Tfkn|g zb~AmUYVD0l>7-t4LPGp$mvy1nVH5-@_mfK-fwQ_)RpB_2v^*f=60RoVZZdJ)_sT&e zTj?*GD()$)f0`ktDhJf%fn5zI6KU=;-T9<*sJ zZuT!%qULr6b(BVRw|(zXF27D^GI8(xD*VbQ%t_99@MV?efNXYqff>^EiE8ar5QVT^ zN*sEH(dDwwFF8LQC^t2rktIAWOo)0X^q0uUB}_@kThfWoxojwpz#oD@ zjov%lJFzX~ZAKDeerL#VvZ-lh*)r2|?-lT}0i7`vn)}%4-8fZl{EJfk4NhIY^U+Pv zsaK-6{Qav=W&0Tun(}b5PuuZe?g9SZG7)H$V2tNeE@~p#_)fq(my+t$Z6+|ELqL%{ z`wOKxv`xs)qFgb4u*)|dpSi^QSs%$SWyOSQ+;VZz50~ahug0K-`&)Q)SEK0uA^)0&uGcd zw(h}ChG63-5C%Xahi+k6(3)i3_+YkS7Ch03C?dTgt-N( zq&n9>O8!jkHFr(l)u4LxKVBUThf$XF1>=7lJ2M@|ONI>J5%xO4e`+}rZ|@T@>=iqR zG*HMSbF*vG-)%2A$ulx2Q{8G9}sQ*Z^o8%f+ZW+uu1seE8<@9Wwg=j-Wh5I30l z(Imhe|39UieBuVx!yozk&t}B36P7A_UEPd|aQp1=jj;c9BD};%jk7ezCDqAR#NViv&gC MlZs4*lPCscyWU2PMP1t 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 + + diff --git a/examples/digitalidentity/static/assets/icons/calendar.svg b/examples/digitalidentity/static/assets/icons/calendar.svg new file mode 100644 index 00000000..4f6b9bb7 --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/calendar.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/digitalidentity/static/assets/icons/chevron-down-grey.svg b/examples/digitalidentity/static/assets/icons/chevron-down-grey.svg new file mode 100644 index 00000000..6753becb --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/chevron-down-grey.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/digitalidentity/static/assets/icons/document.svg b/examples/digitalidentity/static/assets/icons/document.svg new file mode 100644 index 00000000..4c41271e --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/document.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/digitalidentity/static/assets/icons/email.svg b/examples/digitalidentity/static/assets/icons/email.svg new file mode 100644 index 00000000..c4582d6e --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/email.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/examples/digitalidentity/static/assets/icons/gender.svg b/examples/digitalidentity/static/assets/icons/gender.svg new file mode 100644 index 00000000..af5c5772 --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/gender.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/digitalidentity/static/assets/icons/nationality.svg b/examples/digitalidentity/static/assets/icons/nationality.svg new file mode 100644 index 00000000..e57d7522 --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/nationality.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/digitalidentity/static/assets/icons/phone.svg b/examples/digitalidentity/static/assets/icons/phone.svg new file mode 100644 index 00000000..b19cce04 --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/digitalidentity/static/assets/icons/profile.svg b/examples/digitalidentity/static/assets/icons/profile.svg new file mode 100644 index 00000000..5c514fc1 --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/profile.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/digitalidentity/static/assets/icons/verified.svg b/examples/digitalidentity/static/assets/icons/verified.svg new file mode 100644 index 00000000..7ca4dbb3 --- /dev/null +++ b/examples/digitalidentity/static/assets/icons/verified.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/examples/digitalidentity/static/assets/logo.png b/examples/digitalidentity/static/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c60227fabf339e9540e5daac4d2d25e121137752 GIT binary patch literal 2988 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 + + + + + + Share v2 Example + + + + + +
+
+
+ Yoti +
+ +

Digital Identity Share Example

+ +
+
+
+ +
+ +
+

The Yoti app is free to download and use:

+ +
+ + Download on the App Store + + + + get it on Google Play + +
+
+
+ + + + + diff --git a/examples/digitalidentity/templates/profile.html b/examples/digitalidentity/templates/profile.html new file mode 100644 index 00000000..b9b93e6c --- /dev/null +++ b/examples/digitalidentity/templates/profile.html @@ -0,0 +1,173 @@ + + + + + + + + Share v2 Example - Profile + + + + + +
+
+
+ Powered by + Yoti +
+ +
+ {% if selfie %} +
+ Yoti + +
+ {% endif %} + +
+ {{ full_name }} +
+
+
+ + +
+ + +
+
Attribute
+
Value
+
Anchors
+
+ +
+
+
S / V
+
Value
+
Sub type
+
+
+ +
+ {% for attribute in attribute_list %} +
+
+
+ + {{ attribute_list[attribute].name }} +
+
+ +
+
+ {% if attribute.name == "Structured Postal Address" %} + + {% elif attribute_list["name"] == "identity_profile_report" %} + + {% for key, value in attribute.value.items() %} + + + + {% endfor %} +
bb{{ key }}
+
{{ value | tojson(indent=2) }}
+
+ {% elif attribute_list[attribute].name == "document_details" %} + + + + + + +
Type{{ attribute_list[attribute].value.document_type }}
Issuing Country{{ attribute_list[attribute].value.issuing_country }}
Issuing Authority{{ attribute_list[attribute].value.issuing_authority }}
Document Number{{ attribute_list[attribute].value.document_number }}
Expiration Date{{ attribute_list[attribute].value.expiration_date }}
+ {% elif attribute_list[attribute].name == "document_images" %} + {% for image in attribute_list[attribute].value %} + + {% endfor %} + {% elif attribute_list[attribute].name == "selfie" %} + + + {% else %} + {{ attribute_list[attribute].value }} + {% endif %} +
+
+ +
+
S / V
+
Value
+
Sub type
+ + {% for source in attribute_list[attribute].anchors if source.anchor_type == "SOURCE" %} +
Source
+
{{ source.value }}
+
{{ source.sub_type }}
+ {% endfor %} + {% for verifier in attribute_list[attribute].anchors if verifier.anchor_type == "VERIFIER" %} +
Verifier
+
{{ verifier.value }}
+
{{ verifier.sub_type }}
+ {% endfor %} +
+
+ {% endfor %} +
+
+ +
+ + + + diff --git a/examples/yoti_example_flask/templates/dynamic-share.html b/examples/yoti_example_flask/templates/dynamic-share.html index 76c7921e..494f7e5d 100644 --- a/examples/yoti_example_flask/templates/dynamic-share.html +++ b/examples/yoti_example_flask/templates/dynamic-share.html @@ -62,7 +62,7 @@

The Yoti app is free to download and use: - + + + + + + diff --git a/examples/yoti_example_flask_di/templates/index.html b/examples/yoti_example_flask_di/templates/index.html new file mode 100644 index 00000000..dd02e3bc --- /dev/null +++ b/examples/yoti_example_flask_di/templates/index.html @@ -0,0 +1,88 @@ + + + + + + + Yoti client example + + + + + + +
+
+
+ Yoti +
+ +

We now accept Yoti

+ +
+
+
+ + + + +
+ +
+

The Yoti app is free to download and use:

+ +
+ + Download on the App Store + + + + get it on Google Play + +
+
+
+ + + + + + + diff --git a/examples/yoti_example_flask_di/templates/profile.html b/examples/yoti_example_flask_di/templates/profile.html new file mode 100644 index 00000000..5819341c --- /dev/null +++ b/examples/yoti_example_flask_di/templates/profile.html @@ -0,0 +1,160 @@ +{% macro parse_document_images(prop) %} + {% for image in prop.value %} + + {% endfor %} +{% endmacro %} + +{% macro parse_structured_address(prop) %} + + {% for key in prop.value %} + + + + + {% endfor %} +
{{ key }}{{ prop.value[key] }}
+{% endmacro %} + +{% macro parse_document_details(prop) %} + + + + + {% if prop.value.expiration_date %} + + {% endif %} + {% if prop.value.issuing_authority %} + + {% endif %} +
Type{{ prop.value.document_type }}
Issuing Country{{ prop.value.issuing_country }}
Document Number{{ prop.value.document_number }}
Expiration Date{{ prop.value.expiration_date }}
Issuing Authority{{ prop.value.issuing_authority }}
+{% endmacro %} + +{% macro parse_age_verification(prop) %} + + + + + + + + + + + + + +
Check Type{{ prop.value.check_type }}
Age{{ prop.value.age }}
Result{{ prop.value.result }}
+{% endmacro %} + +{% macro attribute(name, icon, prop, prevalue="") %} + {% if prop %} + {% if prop.value %} +
+
+
+ + {{ name }} +
+
+ +
+
+ {% if prop.name == "document_images" %} + {{ parse_document_images(prop) }} + {% elif prop.name == "structured_postal_address" %} + {{ parse_structured_address(prop) }} + {% elif prop.name == "document_details" %} + {{ parse_document_details(prop) }} + {% elif prop.name == "age_verified" %} + {{ parse_age_verification(prop) }} + {% else %} + {{ prevalue }} + {{ prop.value }} + {% endif %} +
+
+
+
S / V
+
Value
+
Sub type
+ {% for source in prop.sources %} +
Source
+
{{ source.value }}
+
{{ source.subtype }}
+ {% endfor %} + {% for verifier in prop.verifiers %} +
Verifier
+
{{ verifier.value }}
+
{{ verifier.subtype }}
+ {% endfor %} +
+
+ {% endif %} + {% endif %} +{% endmacro %} + + + + + + + + Yoti client example + + + + + +
+
+
+ Powered by + Yoti +
+ {% if selfie is not none %} +
+ Yoti + +
+ {% endif %} + +
+ {{ full_name.value }} +
+
+ +
+ +
+
Attribute
+
Value
+
Anchors
+
+ +
+
+
S / V
+
Value
+
Sub type
+
+
+ +
+ {% if given_names %}{{ attribute("Given names", "yoti-icon-profile", given_names) }}{% endif %} + {% if family_name %}{{ attribute("Family names", "yoti-icon-profile", family_name) }}{% endif %} + {% if phone_number %}{{ attribute("Mobile number", "yoti-icon-phone", phone_number) }}{% endif %} + {% if email_address %}{{ attribute("Email address", "yoti-icon-email", email_address) }}{% endif %} + {% if date_of_birth %}{{ attribute("Date of birth", "yoti-icon-calendar", date_of_birth) }}{% endif %} + {% if age_verified %}{{ attribute("Age verified", "yoti-icon-verified", age_verified, "Age Verification/") }}{% endif %} + {% if postal_address %}{{ attribute("Address", "yoti-icon-address", postal_address) }}{% endif %} + {% if structured_postal_address %}{{ attribute("Structured Address", "yoti-icon-address", structured_postal_address) }}{% endif %} + {% if gender %}{{ attribute("Gender", "yoti-icon-gender", gender) }}{% endif %} + {% if document_images %}{{ attribute("Document Images", "yoti-icon-profile", document_images) }}{% endif %} + {% if document_details %}{{ attribute("Document Details", "yoti-icon-profile", document_details) }} {% endif %} +
+ +
+
+ + + diff --git a/yoti_python_sdk/__init__.py b/yoti_python_sdk/__init__.py index e2084235..800e19bb 100644 --- a/yoti_python_sdk/__init__.py +++ b/yoti_python_sdk/__init__.py @@ -22,6 +22,7 @@ YOTI_PROFILE_ENDPOINT = "/api/v1" YOTI_DOC_SCAN_ENDPOINT = "/idverify/v1" +DIGITAL_IDENTITY_ENDPOINT = "/share" YOTI_API_PORT = environ.get("YOTI_API_PORT", DEFAULTS["YOTI_API_PORT"]) YOTI_API_VERSION = environ.get("YOTI_API_VERSION", DEFAULTS["YOTI_API_VERSION"]) @@ -36,6 +37,10 @@ "{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_DOC_SCAN_ENDPOINT), ) +YOTI_DIGITAL_IDENTITY_API_URL = environ.get( + "YOTI_API_URL2", + "{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, DIGITAL_IDENTITY_ENDPOINT), +) YOTI_API_VERIFY_SSL = environ.get( "YOTI_API_VERIFY_SSL", DEFAULTS["YOTI_API_VERIFY_SSL"] ) diff --git a/yoti_python_sdk/activity_details.py b/yoti_python_sdk/activity_details.py index 5364860e..49eb4f61 100644 --- a/yoti_python_sdk/activity_details.py +++ b/yoti_python_sdk/activity_details.py @@ -79,6 +79,7 @@ def __init__( self.parent_remember_me_id = receipt.get("parent_remember_me_id") self.outcome = receipt.get("sharing_outcome") self.receipt_id = receipt.get("receipt_id") + self.receipt_identifier = receipt.get("receipt_id") self.extra_data = receipt.get("extra_data") timestamp = receipt.get("timestamp") @@ -97,9 +98,14 @@ def remember_me_id(self): def user_id(self): return self.__remember_me_id + @property + @deprecated + def user_id(self): + return self.__remember_me_id + @user_id.setter @deprecated - def user_id(self, value): + def user_identifier(self, value): self.__remember_me_id = value @deprecated @@ -151,6 +157,7 @@ def __iter__(self): yield "parent_remember_me_id", self.parent_remember_me_id yield "outcome", self.outcome yield "receipt_id", self.receipt_id + yield "receipt_identifier", self.receipt_identifier yield "user_profile", self.user_profile yield "profile", self.profile yield "base64_selfie_uri", self.base64_selfie_uri diff --git a/yoti_python_sdk/anchor.py b/yoti_python_sdk/anchor.py index 924330ae..b053de4b 100644 --- a/yoti_python_sdk/anchor.py +++ b/yoti_python_sdk/anchor.py @@ -39,7 +39,9 @@ def __init__( self.__value = value self.__signed_timestamp = signed_timestamp self.__origin_server_certs = origin_server_certs - + self.idx = 0 + self.data = [] + def __iter__(self): return self diff --git a/yoti_python_sdk/attribute.py b/yoti_python_sdk/attribute.py index 5a5a5bf7..2a02b40d 100644 --- a/yoti_python_sdk/attribute.py +++ b/yoti_python_sdk/attribute.py @@ -2,7 +2,7 @@ class Attribute: - def __init__(self, name=None, value=None, anchors=None): + def __init__(self, name=None, value=None, anchors=None, icon = None): if name is None: name = "" if value is None: @@ -12,6 +12,7 @@ def __init__(self, name=None, value=None, anchors=None): self.__name = name self.__value = value self.__anchors = anchors + self.__icon = icon @property def name(self): @@ -36,3 +37,6 @@ def verifiers(self): return list( filter(lambda a: a.anchor_type == config.ANCHOR_VERIFIER, self.__anchors) ) + @property + def icon(self): + return self.__icon diff --git a/yoti_python_sdk/digital_identity/__init__.py b/yoti_python_sdk/digital_identity/__init__.py new file mode 100644 index 00000000..64e5f2c0 --- /dev/null +++ b/yoti_python_sdk/digital_identity/__init__.py @@ -0,0 +1,5 @@ +from .client import DigitalIdentityClient + +__all__ = [ + "DigitalIdentityClient" +] diff --git a/yoti_python_sdk/digital_identity/client.py b/yoti_python_sdk/digital_identity/client.py new file mode 100644 index 00000000..2368f6e2 --- /dev/null +++ b/yoti_python_sdk/digital_identity/client.py @@ -0,0 +1,259 @@ +import yoti_python_sdk + +import json, base64 + +from yoti_python_sdk.http import SignedRequest + +from .create_share_session_result import CreateShareSessionResult +from .get_share_session_result import GetShareSessionResult +from .create_share_qr_code_result import CreateShareQrCodeResult +from .get_share_qr_code_result import GetShareQrCodeResult +from .get_share_receipt_result import GetShareReceiptResult + +from .receipts.receipt_response import ReceiptResponse +from .receipts.receipt_item_key_response import ReceiptItemKeyResponse + +from .receipts.crypto.decryption import build_user_content_from_encrypted_content, unwrap_receipt_key + +class DigitalIdentityClient(object): + """ + Client used for communication with the Yoti Doc Scan service where any + signed request is required + """ + def __init__(self, sdk_id, key, api_url=None): + self.__sdk_id = sdk_id + self.__key = key + if api_url is not None: + self.__api_url = api_url + else: + self.__api_url = yoti_python_sdk.YOTI_DOC_SCAN_API_URL + def create_share_session(self, share_session_config): + """ + Creates a share session + + :param share_session_config: the share session config + :type share_session_config: dict + :return: the create share session result + :rtype: CreateShareSessionResult + """ + + + payload = json.dumps(share_session_config).encode("utf-8") + + request = ( + SignedRequest.builder() + .with_base_url(self.__api_url) + .with_header("Content-Type", "application/json") + .with_header("X-Yoti-Auth-Id", self.__sdk_id) + .with_pem_file(self.__key) + .with_endpoint("/v2/sessions") + .with_param("appId", self.__sdk_id) + .with_post() + .with_payload(payload) + .build() + ) + + response = request.execute() + + #if response.status_code != 201: + # raise Exception("Failed to create session", response) + + if response.status_code != 201: + print(f"Response Status Code: {response.status_code}") + print(f"Response Content: {response.text}") + raise Exception("Failed to create session", response) + + data = json.loads(response.text) + + return CreateShareSessionResult(data) + + def get_share_session(self, session_id): + """ + Retrieves a share session + + :param session_id: the session id + :type session_id: str + :return: the get share session result + :rtype: GetShareSessionResult + """ + + request = ( + SignedRequest.builder() + .with_base_url(self.__api_url) + .with_header("X-Yoti-Auth-Id", self.__sdk_id) + .with_pem_file(self.__key) + .with_endpoint("/v2/sessions/{}".format(session_id)) + .with_param("appId", self.__sdk_id) + .with_get() + .build() + ) + + response = request.execute() + + if response.status_code != 200: + raise Exception("Failed to get session", response) + + data = json.loads(response.text) + + return GetShareSessionResult(data) + + def create_share_qr_code(self,session_id): + """ + Creates a share QR code + + :param session_id: the session id + :type session_id: str + :return: the share QR code result + :rtype: CreateShareQrCodeResult + """ + + #Create an empty payload + payload = json.dumps({}).encode("utf-8") + + request = ( + SignedRequest.builder() + .with_base_url(self.__api_url) + .with_header("Content-Type", "application/json") + .with_header("X-Yoti-Auth-Id", self.__sdk_id) + .with_pem_file(self.__key) + .with_endpoint("/v2/sessions/{}/qr-codes".format(session_id)) + .with_param("appId", self.__sdk_id) + .with_post() + .with_payload(payload) + .build() + ) + + response = request.execute() + + if response.status_code != 201: + raise Exception("Failed to create qr code", response) + + data = json.loads(response.text) + + return CreateShareQrCodeResult(data) + + def get_share_qr_code(self,qrCodeId): + """ + Retrieves a share QR code + + :param qrCodeId: the qr code id + :type qrCodeId: str + :return: the get share QR code result + :rtype: GetShareQrCodeResult + """ + + request = ( + SignedRequest.builder() + .with_base_url(self.__api_url) + .with_header("X-Yoti-Auth-Id", self.__sdk_id) + .with_pem_file(self.__key) + .with_endpoint("/v2/qr-codes/{}".format(qrCodeId)) + .with_param("appId", self.__sdk_id) + .with_get() + .build() + ) + + response = request.execute() + + if response.status_code != 200: + raise Exception("Failed to get session", response) + + data = json.loads(response.text) + + return GetShareQrCodeResult(data) + + def fetch_receipt(self, receiptId): + """ + Fetches the receipt + + :param receiptId: the receipt id + :type receiptId: str + :return: the receipt response + :rtype: ReceiptResponse + """ + + # Convert the receipt id to a url safe base64 string + receiptIdUrl = base64.urlsafe_b64encode(base64.b64decode(receiptId)).decode() + + + request = ( + SignedRequest.builder() + .with_base_url(self.__api_url) + .with_header("X-Yoti-Auth-Id", self.__sdk_id) + .with_pem_file(self.__key) + .with_endpoint("/v2/receipts/{}".format(receiptIdUrl)) + .with_param("appId", self.__sdk_id) + .with_get() + .build() + ) + + response = request.execute() + + if response.status_code != 200: + raise Exception("Failed to get session", response) + + data = json.loads(response.text) + + return ReceiptResponse(data) + + def fetch_receipt_item_key(self, receiptItemKeyId): + """ + Fetches the receipt item key + + :param receiptItemKeyId: the receipt item key id + :type receiptItemKeyId: str + :return: the receipt item key response + :rtype: ReceiptItemKeyResponse + """ + + request = ( + SignedRequest.builder() + .with_base_url(self.__api_url) + .with_header("X-Yoti-Auth-Id", self.__sdk_id) + .with_pem_file(self.__key) + .with_endpoint("/v2/wrapped-item-keys/{}".format(receiptItemKeyId)) + .with_param("appId", self.__sdk_id) + .with_get() + .build() + ) + + response = request.execute() + + if response.status_code != 200: + raise Exception("Failed to get session", response) + + data = json.loads(response.text) + + return ReceiptItemKeyResponse(data) + + def get_share_receipt(self, receiptId): + """ + Retrieves a share receipt + + :param receiptId: the receipt id + :type receiptId: str + :return: the get share receipt result + :rtype: GetShareReceiptResult + """ + + receipt_response = self.fetch_receipt(receiptId) + item_key_id = receipt_response.wrappedItemKeyId + + if (item_key_id is None): + return GetShareReceiptResult(receipt_response) + + encrypted_item_key_response = self.fetch_receipt_item_key(item_key_id) + + receipt_content_key = unwrap_receipt_key( + receipt_response.wrappedKey, + encrypted_item_key_response.value, + encrypted_item_key_response.iv, + self.__key + ) + + user_content = build_user_content_from_encrypted_content( + receipt_response.otherPartyContent, + receipt_content_key, + ) + + return GetShareReceiptResult(receipt_response, user_content) diff --git a/yoti_python_sdk/digital_identity/create_share_qr_code_result.py b/yoti_python_sdk/digital_identity/create_share_qr_code_result.py new file mode 100644 index 00000000..8c3a6e3d --- /dev/null +++ b/yoti_python_sdk/digital_identity/create_share_qr_code_result.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +class CreateShareQrCodeResult(object): + + def __init__(self, data=None): + """ + :param data: the data + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__uri = data.get("uri", None) + + def to_dict(self): + return { + 'id': self.__id, + 'uri': self.__uri, + } + + @property + def id(self): + """ + :return: the qr code id + :rtype: str or None + """ + return self.__id + + @property + def uri(self): + """ + :return: the qr code uri + :rtype: str or None + """ + return self.__uri + \ No newline at end of file diff --git a/yoti_python_sdk/digital_identity/create_share_session_result.py b/yoti_python_sdk/digital_identity/create_share_session_result.py new file mode 100644 index 00000000..be947a1c --- /dev/null +++ b/yoti_python_sdk/digital_identity/create_share_session_result.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +class CreateShareSessionResult(object): + + def __init__(self, data=None): + """ + :param data: the data + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__status = data.get("status", None) + self.__expiry = data.get("expiry", None) + + def to_dict(self): + return { + 'id': self.__id, + 'status': self.__status, + 'expiry': self.__expiry, + } + + @property + def id(self): + """ + :return: the session id + :rtype: str or None + """ + return self.__id + + @property + def status(self): + """ + :return: the session status + :rtype: str or None + """ + return self.__status + + @property + def expiry(self): + """ + :return: the session expiry + :rtype: str or None + """ + return self.__expiry + \ No newline at end of file diff --git a/yoti_python_sdk/digital_identity/get_share_qr_code_result.py b/yoti_python_sdk/digital_identity/get_share_qr_code_result.py new file mode 100644 index 00000000..74a2652a --- /dev/null +++ b/yoti_python_sdk/digital_identity/get_share_qr_code_result.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +class GetShareQrCodeResult(object): + + def __init__(self, data=None): + """ + Initializes the GetShareQrCodeResult object. + + :param data: Dictionary containing QR code data. + :type data: dict or None + """ + data = data or {} + + self.__id = data.get("id") + self.__expiry = data.get("expiry") + self.__sessionId = data.get("sessionId") + self.__redirectUri = data.get("redirectUri") + + def to_dict(self): + """Converts the object to a dictionary representation.""" + return { + 'id': self.__id, + 'expiry': self.__expiry, + 'sessionId': self.__sessionId, + 'redirectUri': self.__redirectUri, + } + + @property + def id(self): + """Returns the QR code ID.""" + return self.__id + + @property + def expiry(self): + """Returns the QR code expiry.""" + return self.__expiry + + @property + def sessionId(self): + """Returns the session ID.""" + return self.__sessionId + + @property + def redirectUri(self): + """Returns the redirect URI.""" + return self.__redirectUri diff --git a/yoti_python_sdk/digital_identity/get_share_receipt_result.py b/yoti_python_sdk/digital_identity/get_share_receipt_result.py new file mode 100644 index 00000000..59b08cdf --- /dev/null +++ b/yoti_python_sdk/digital_identity/get_share_receipt_result.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from .receipts.receipt_response import ReceiptResponse +from .receipts.user_content import UserContent + +class GetShareReceiptResult(ReceiptResponse): + + def __init__(self, receipt_response: ReceiptResponse, user_content: UserContent = None): + """ + :param receipt_response: the receipt response + :type receipt_response: ReceiptResponse + :param user_content: the user content, defaults to a new UserContent if None + :type user_content: UserContent or None + """ + self.__userContent = user_content if user_content is not None else UserContent() + + self.__id = receipt_response.id + self.__sessionId = receipt_response.sessionId + self.__timestamp = receipt_response.timestamp + self.__rememberMeId = receipt_response.rememberMeId + self.__parentRememberMeId = receipt_response.parentRememberMeId + + def to_dict(self): + return { + 'id': self.__id, + 'sessionId': self.__sessionId, + 'timestamp': self.__timestamp, + 'rememberMeId': self.__rememberMeId, + 'parentRememberMeId': self.__parentRememberMeId, + } + + @property + def receiptId(self): + """Returns the receipt ID.""" + return self.__id + + @property + def sessionId(self): + """Returns the session ID.""" + return self.__sessionId + + @property + def timestamp(self): + """Returns the timestamp.""" + return self.__timestamp + + @property + def rememberMeId(self): + """Returns the remember me ID.""" + return self.__rememberMeId + + @property + def parentRememberMeId(self): + """Returns the parent remember me ID.""" + return self.__parentRememberMeId + + @property + def userContent(self): + """Returns the user content.""" + return self.__userContent + + @property + def profile(self): + """Returns the user's profile.""" + return self.__userContent.profile if self.__userContent else None + + @property + def extra_data(self): + """Returns the extra data.""" + return self.__userContent.extra_data if self.__userContent else {} diff --git a/yoti_python_sdk/digital_identity/get_share_session_result.py b/yoti_python_sdk/digital_identity/get_share_session_result.py new file mode 100644 index 00000000..9f171ecf --- /dev/null +++ b/yoti_python_sdk/digital_identity/get_share_session_result.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +class GetShareSessionResult(object): + + def __init__(self, data=None): + """ + :param data: the data + :type data: dict or None + """ + self.__data = data if data is not None else {} + + self.__id = self.__data.get("id") + self.__status = self.__data.get("status") + self.__created = self.__data.get("created") + self.__updated = self.__data.get("updated") + self.__expiry = self.__data.get("expiry") + self.__qrCode = self.__data.get("qrCode") + self.__receipt = self.__data.get("receipt") + + def to_dict(self): + return { + 'id': self.__id, + 'status': self.__status, + 'created': self.__created, + 'updated': self.__updated, + 'expiry': self.__expiry, + 'qrCode': self.__qrCode, + 'receipt': self.__receipt, + } + + @property + def id(self): + """Returns the session ID.""" + return self.__id + + @property + def status(self): + """Returns the session status.""" + return self.__status + + @property + def created(self): + """Returns the session creation timestamp.""" + return self.__created + + @property + def updated(self): + """Returns the session last updated timestamp.""" + return self.__updated + + @property + def expiry(self): + """Returns the session expiry timestamp.""" + return self.__expiry + + @property + def qrCode(self): + """Returns the QR code data.""" + return self.__qrCode + + @property + def qrCodeId(self): + """Returns the QR code ID, if available.""" + return self.__qrCode.get("id") if self.__qrCode else None + + @property + def receipt(self): + """Returns the receipt data.""" + return self.__receipt + + @property + def receiptId(self): + """Returns the receipt ID, if available.""" + return self.__receipt.get("id") if self.__receipt else None diff --git a/yoti_python_sdk/digital_identity/receipts/__init__.py b/yoti_python_sdk/digital_identity/receipts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/digital_identity/receipts/base_content.py b/yoti_python_sdk/digital_identity/receipts/base_content.py new file mode 100644 index 00000000..d72063f7 --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/base_content.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +class BaseContent(): + + def __init__(self, extra_data=None): + """ + :param data: the extra data + :type data: dict or None + """ + + if extra_data is None: + extra_data = dict() + + self.__extra_data = extra_data + + def to_dict(self): + return { + 'extra_data': self.__extra_data, + } + + @property + def extra_data(self): + """ + :return: the extra data + :rtype: dict + """ + return self.__extra_data + \ No newline at end of file diff --git a/yoti_python_sdk/digital_identity/receipts/crypto/__init__.py b/yoti_python_sdk/digital_identity/receipts/crypto/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/digital_identity/receipts/crypto/decryption.py b/yoti_python_sdk/digital_identity/receipts/crypto/decryption.py new file mode 100644 index 00000000..7fa974f3 --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/crypto/decryption.py @@ -0,0 +1,86 @@ +import base64 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + +from yoti_python_sdk.protobuf import protobuf +from yoti_python_sdk.profile import Profile +from ..user_content import UserContent +from .utils import decrypt_receipt_content + + +def unwrap_receipt_key(wrapped_receipt_key, encrypted_item_key, item_key_iv, pem): + # Decode the base64 encoded inputs + wrapped_receipt_key_buffer = base64.b64decode(wrapped_receipt_key) + encrypted_item_key_buffer = base64.b64decode(encrypted_item_key) + item_key_iv_buffer = base64.b64decode(item_key_iv) + + # Load the private key from PEM file + private_key = load_private_key(pem) + + # Decrypt the item key + decrypted_item_key = decrypt_item_key(private_key, encrypted_item_key_buffer) + + # Decrypt the wrapped receipt key + return decrypt_wrapped_receipt_key(decrypted_item_key, wrapped_receipt_key_buffer, item_key_iv_buffer) + + +def load_private_key(pem): + with open(pem, "rb") as key_file: + return serialization.load_pem_private_key( + key_file.read(), + password=None, + backend=default_backend() + ) + + +def decrypt_item_key(private_key, encrypted_item_key_buffer): + return private_key.decrypt( + encrypted_item_key_buffer, + padding.PKCS1v15() + ) + + +def decrypt_wrapped_receipt_key(decrypted_item_key, wrapped_receipt_key_buffer, item_key_iv_buffer): + tag_size = 16 # Size of the authentication tag + + # Separate the authentication tag from the ciphertext + cipher_text, tag = wrapped_receipt_key_buffer[:-tag_size], wrapped_receipt_key_buffer[-tag_size:] + + # Create the cipher and decrypt the data + cipher = Cipher(algorithms.AES(decrypted_item_key), modes.GCM(item_key_iv_buffer, tag), backend=default_backend()) + decryptor = cipher.decryptor() + + return decryptor.update(cipher_text) + decryptor.finalize() + + +def build_user_content_from_encrypted_content(content, receipt_content_key): + if content is None: + content = {'profile': '', 'extraData': ''} + + attributes, extra_data = decrypt_and_extract_content_data(content, receipt_content_key) + return UserContent(attributes, extra_data) + + +def decrypt_and_extract_content_data(content=None, receipt_content_key=None): + if content is None: + content = {'profile': '', 'extraData': ''} + + decrypted_profile = decrypt_receipt_content(content.get('profile'), receipt_content_key) + decrypted_extra_data = decrypt_receipt_content(content.get('extraData'), receipt_content_key) + + attributes = extract_attributes(decrypted_profile) + extracted_extra_data = decrypted_extra_data if decrypted_extra_data else None + + return attributes, extracted_extra_data + + +def extract_attributes(decrypted_profile): + proto = protobuf.Protobuf() + + if decrypted_profile: + user_profile_attribute_list = proto.attribute_list(decrypted_profile) + return user_profile_attribute_list.attributes if hasattr(user_profile_attribute_list, 'attributes') else None + + return None diff --git a/yoti_python_sdk/digital_identity/receipts/crypto/utils.py b/yoti_python_sdk/digital_identity/receipts/crypto/utils.py new file mode 100644 index 00000000..513ee6a0 --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/crypto/utils.py @@ -0,0 +1,43 @@ +import base64 +from google.protobuf.message import DecodeError +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + +from ..proto import EncryptedData_pb2 + +def decrypt_receipt_content(content, receipt_content_key): + if not content: + return None + + content_buffer = base64.b64decode(content) + + iv, cipher_text = decode_encrypted_data(content_buffer) + + cipher_text_buffer = base64.b64decode(cipher_text) + iv_buffer = base64.b64decode(iv) + + return decrypt_aes_cbc(cipher_text_buffer, iv_buffer, receipt_content_key) + +def decode_encrypted_data(binary_data): + encrypted_data = EncryptedData_pb2.EncryptedData() + + try: + encrypted_data.ParseFromString(binary_data) + except DecodeError: + raise ValueError("Failed to decode binary data") + + return ( + base64.b64encode(encrypted_data.iv).decode('utf-8'), + base64.b64encode(encrypted_data.cipher_text).decode('utf-8'), + ) + +def decrypt_aes_cbc(cipher_text, iv, secret): + cipher = Cipher(algorithms.AES(secret), modes.CBC(iv), backend=default_backend()) + decryptor = cipher.decryptor() + decrypted_data = decryptor.update(cipher_text) + decryptor.finalize() + + return strip_pkcs5_padding(decrypted_data) + +def strip_pkcs5_padding(data): + padding_length = data[-1] + return data[:-padding_length] diff --git a/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto b/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto new file mode 100644 index 00000000..a54cf86b --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package compubapi_v1; + +message EncryptedData { + bytes iv = 1; + bytes cipher_text = 2; +} \ No newline at end of file diff --git a/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py b/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py new file mode 100644 index 00000000..6eec60f4 --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: EncryptedData.proto +# Protobuf Python Version: 4.25.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x45ncryptedData.proto\x12\x0c\x63ompubapi_v1\"0\n\rEncryptedData\x12\n\n\x02iv\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63ipher_text\x18\x02 \x01(\x0c\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'EncryptedData_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_ENCRYPTEDDATA']._serialized_start=37 + _globals['_ENCRYPTEDDATA']._serialized_end=85 +# @@protoc_insertion_point(module_scope) diff --git a/yoti_python_sdk/digital_identity/receipts/proto/__init__.py b/yoti_python_sdk/digital_identity/receipts/proto/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/digital_identity/receipts/receipt_item_key_response.py b/yoti_python_sdk/digital_identity/receipts/receipt_item_key_response.py new file mode 100644 index 00000000..25a4d38c --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/receipt_item_key_response.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + + +class ReceiptItemKeyResponse: + def __init__(self, data=None): + """ + Initializes the ReceiptItemKeyResponse with provided data. + + :param data: The data containing id, iv, and value + :type data: dict or None + """ + self.__id = data.get("id") if data else None + self.__iv = data.get("iv") if data else None + self.__value = data.get("value") if data else None + + def to_dict(self): + """Returns the object data as a dictionary.""" + return { + 'id': self.__id, + 'iv': self.__iv, + 'value': self.__value, + } + + @property + def id(self): + """Returns the id.""" + return self.__id + + @property + def iv(self): + """Returns the iv.""" + return self.__iv + + @property + def value(self): + """Returns the value.""" + return self.__value diff --git a/yoti_python_sdk/digital_identity/receipts/receipt_response.py b/yoti_python_sdk/digital_identity/receipts/receipt_response.py new file mode 100644 index 00000000..681254c2 --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/receipt_response.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +class ReceiptResponse(object): + + def __init__(self, data=None): + """ + :param data: the data + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__sessionId = data.get("sessionId", None) + self.__timestamp = data.get("timestamp", None) + self.__rememberMeId = data.get("rememberMeId", None) + self.__parentRememberMeId = data.get("parentRememberMeId", None) + self.__content = data.get("content", None) + self.__otherPartyContent = data.get("otherPartyContent", None) + self.__wrappedItemKeyId = data.get("wrappedItemKeyId", None) + self.__wrappedKey = data.get("wrappedKey", None) + + def to_dict(self): + return { + 'id': self.__id, + 'sessionId': self.__sessionId, + 'timestamp': self.__timestamp, + 'rememberMeId': self.__rememberMeId, + 'parentRememberMeId': self.__parentRememberMeId, + 'content': self.__content, + 'otherPartyContent': self.__otherPartyContent, + 'wrappedItemKeyId': self.__wrappedItemKeyId, + 'wrappedKey': self.__wrappedKey, + } + + @property + def id(self): + """ + :return: the id + :rtype: str or None + """ + return self.__id + + @property + def sessionId(self): + """ + :return: the session id + :rtype: str or None + """ + return self.__sessionId + + @property + def timestamp(self): + """ + :return: the timestamp + :rtype: str or None + """ + return self.__timestamp + + @property + def rememberMeId(self): + """ + :return: the remember me id + :rtype: str or None + """ + return self.__rememberMeId + + @property + def parentRememberMeId(self): + """ + :return: the parent remember me id + :rtype: str or None + """ + return self.__parentRememberMeId + + @property + def content(self): + """ + :return: the content + :rtype: str or None + """ + return self.__content + + @property + def otherPartyContent(self): + """ + :return: the other party content + :rtype: str or None + """ + return self.__otherPartyContent + + @property + def wrappedItemKeyId(self): + """ + :return: the wrapped item key id + :rtype: str or None + """ + return self.__wrappedItemKeyId + + @property + def wrappedKey(self): + """ + :return: the wrapped key + :rtype: str or None + """ + return self.__wrappedKey diff --git a/yoti_python_sdk/digital_identity/receipts/user_content.py b/yoti_python_sdk/digital_identity/receipts/user_content.py new file mode 100644 index 00000000..9d19c2bd --- /dev/null +++ b/yoti_python_sdk/digital_identity/receipts/user_content.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.profile import Profile + +from .base_content import BaseContent + +class UserContent(BaseContent): + + def __init__(self, attributes=None, extra_data=None): + """ + :param data: the attributes + :type data: array or None + :param extra_data: the extra data + :type extra_data: dict or None + """ + super().__init__(extra_data) + if attributes is None: + attributes = [] + + self.__profile = Profile(attributes) + + def to_dict(self): + return { + 'profile': self.__profile, + } + + @property + def profile(self): + """ + :return: the profile + :rtype: Profile + """ + return self.__profile + \ No newline at end of file diff --git a/yoti_python_sdk/dynamic_sharing_service/dynamic_scenario_builder.py b/yoti_python_sdk/dynamic_sharing_service/dynamic_scenario_builder.py index 854387da..d88236df 100644 --- a/yoti_python_sdk/dynamic_sharing_service/dynamic_scenario_builder.py +++ b/yoti_python_sdk/dynamic_sharing_service/dynamic_scenario_builder.py @@ -5,6 +5,7 @@ class DynamicScenarioBuilder(object): + print("Hello, World!") def __init__(self): self.__scenario = { "policy": DynamicPolicyBuilder().build(), diff --git a/yoti_python_sdk/profile.py b/yoti_python_sdk/profile.py index 2f230dc9..83491c67 100644 --- a/yoti_python_sdk/profile.py +++ b/yoti_python_sdk/profile.py @@ -15,6 +15,7 @@ def __init__(self, profile_attributes): if profile_attributes: for field in profile_attributes: + # print("field %s" % field) try: value = attribute_parser.value_based_on_content_type( field.value, field.content_type @@ -33,7 +34,6 @@ def __init__(self, profile_attributes): anchors = Anchor().parse_anchors(field.anchors) self.attributes[field.name] = Attribute(field.name, value, anchors) - except ValueError as ve: if logging.getLogger().propagate: logging.warning(ve) diff --git a/yoti_python_sdk/tests/digital_identity/__init__.py b/yoti_python_sdk/tests/digital_identity/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/tests/digital_identity/test_create_share_qr_code.py b/yoti_python_sdk/tests/digital_identity/test_create_share_qr_code.py new file mode 100644 index 00000000..7d2dfcb0 --- /dev/null +++ b/yoti_python_sdk/tests/digital_identity/test_create_share_qr_code.py @@ -0,0 +1,50 @@ +import pytest +from yoti_python_sdk.digital_identity.create_share_qr_code_result import CreateShareQrCodeResult + +def test_create_share_qr_code_result_with_data(): + # Arrange + data = { + 'id': 'test_qr_code_id', + 'uri': 'https://example.com/qr_code_uri' + } + + # Act + result = CreateShareQrCodeResult(data) + + # Assert + assert result.id == 'test_qr_code_id' + assert result.uri == 'https://example.com/qr_code_uri' + assert result.to_dict() == { + 'id': 'test_qr_code_id', + 'uri': 'https://example.com/qr_code_uri' + } + +def test_create_share_qr_code_result_without_data(): + # Act + result = CreateShareQrCodeResult() + + # Assert + assert result.id is None + assert result.uri is None + assert result.to_dict() == { + 'id': None, + 'uri': None + } + +def test_create_share_qr_code_result_partial_data(): + # Arrange + data = { + 'id': 'test_qr_code_id' + # uri is missing + } + + # Act + result = CreateShareQrCodeResult(data) + + # Assert + assert result.id == 'test_qr_code_id' + assert result.uri is None + assert result.to_dict() == { + 'id': 'test_qr_code_id', + 'uri': None + } diff --git a/yoti_python_sdk/tests/digital_identity/test_create_share_session.py b/yoti_python_sdk/tests/digital_identity/test_create_share_session.py new file mode 100644 index 00000000..5b786f35 --- /dev/null +++ b/yoti_python_sdk/tests/digital_identity/test_create_share_session.py @@ -0,0 +1,58 @@ +import pytest +from yoti_python_sdk.digital_identity.create_share_session_result import CreateShareSessionResult + +def test_create_share_session_result_with_data(): + # Arrange + data = { + 'id': 'test_session_id', + 'status': 'ACTIVE', + 'expiry': '2024-12-31T23:59:59Z' + } + + # Act + result = CreateShareSessionResult(data) + + # Assert + assert result.id == 'test_session_id' + assert result.status == 'ACTIVE' + assert result.expiry == '2024-12-31T23:59:59Z' + assert result.to_dict() == { + 'id': 'test_session_id', + 'status': 'ACTIVE', + 'expiry': '2024-12-31T23:59:59Z' + } + +def test_create_share_session_result_without_data(): + # Act + result = CreateShareSessionResult() + + # Assert + assert result.id is None + assert result.status is None + assert result.expiry is None + assert result.to_dict() == { + 'id': None, + 'status': None, + 'expiry': None + } + +def test_create_share_session_result_partial_data(): + # Arrange + data = { + 'id': 'test_session_id', + 'status': 'ACTIVE' + # expiry is missing + } + + # Act + result = CreateShareSessionResult(data) + + # Assert + assert result.id == 'test_session_id' + assert result.status == 'ACTIVE' + assert result.expiry is None + assert result.to_dict() == { + 'id': 'test_session_id', + 'status': 'ACTIVE', + 'expiry': None + } diff --git a/yoti_python_sdk/tests/digital_identity/test_get_share_qr_code.py b/yoti_python_sdk/tests/digital_identity/test_get_share_qr_code.py new file mode 100644 index 00000000..0e4eb7db --- /dev/null +++ b/yoti_python_sdk/tests/digital_identity/test_get_share_qr_code.py @@ -0,0 +1,65 @@ +import pytest +from yoti_python_sdk.digital_identity.get_share_qr_code_result import GetShareQrCodeResult + +def test_get_share_qr_code_result_with_data(): + # Arrange + data = { + 'id': 'test_qr_code_id', + 'expiry': '2024-12-31T23:59:59Z', + 'sessionId': 'test_session_id', + 'redirectUri': 'https://example.com' + } + + # Act + result = GetShareQrCodeResult(data) + + # Assert + assert result.id == 'test_qr_code_id' + assert result.expiry == '2024-12-31T23:59:59Z' + assert result.sessionId == 'test_session_id' + assert result.redirectUri == 'https://example.com' + assert result.to_dict() == { + 'id': 'test_qr_code_id', + 'expiry': '2024-12-31T23:59:59Z', + 'sessionId': 'test_session_id', + 'redirectUri': 'https://example.com' + } + +def test_get_share_qr_code_result_without_data(): + # Act + result = GetShareQrCodeResult() + + # Assert + assert result.id is None + assert result.expiry is None + assert result.sessionId is None + assert result.redirectUri is None + assert result.to_dict() == { + 'id': None, + 'expiry': None, + 'sessionId': None, + 'redirectUri': None + } + +def test_get_share_qr_code_result_partial_data(): + # Arrange + data = { + 'id': 'test_qr_code_id', + 'sessionId': 'test_session_id' + # expiry and redirectUri are missing + } + + # Act + result = GetShareQrCodeResult(data) + + # Assert + assert result.id == 'test_qr_code_id' + assert result.expiry is None + assert result.sessionId == 'test_session_id' + assert result.redirectUri is None + assert result.to_dict() == { + 'id': 'test_qr_code_id', + 'expiry': None, + 'sessionId': 'test_session_id', + 'redirectUri': None + } diff --git a/yoti_python_sdk/tests/digital_identity/test_get_share_session.py b/yoti_python_sdk/tests/digital_identity/test_get_share_session.py new file mode 100644 index 00000000..0b6c7716 --- /dev/null +++ b/yoti_python_sdk/tests/digital_identity/test_get_share_session.py @@ -0,0 +1,29 @@ +import pytest +from yoti_python_sdk.digital_identity.get_share_session_result import GetShareSessionResult + +def test_get_share_session_result_with_valid_data(): + # Arrange + session_data = { + 'id': 'test_session_id', + 'status': 'active', + 'created': '2024-10-24T10:00:00Z', + 'updated': '2024-10-24T11:00:00Z', + 'expiry': '2024-10-24T12:00:00Z', + 'qrCode': {'id': 'test_qr_code_id'}, + 'receipt': {'id': 'test_receipt_id'} + } + + # Act + session_result = GetShareSessionResult(session_data) + + # Assert + assert session_result.id == 'test_session_id' + assert session_result.status == 'active' + assert session_result.created == '2024-10-24T10:00:00Z' + assert session_result.updated == '2024-10-24T11:00:00Z' + assert session_result.expiry == '2024-10-24T12:00:00Z' + assert session_result.qrCode == {'id': 'test_qr_code_id'} + assert session_result.receipt == {'id': 'test_receipt_id'} + assert session_result.qrCodeId == 'test_qr_code_id' + assert session_result.receiptId == 'test_receipt_id' + assert session_result.to_dict() == session_data diff --git a/yoti_python_sdk/tests/digital_identity/test_share_receipt.py b/yoti_python_sdk/tests/digital_identity/test_share_receipt.py new file mode 100644 index 00000000..808d7cb0 --- /dev/null +++ b/yoti_python_sdk/tests/digital_identity/test_share_receipt.py @@ -0,0 +1,32 @@ +import pytest +from yoti_python_sdk.digital_identity.receipts.receipt_response import ReceiptResponse +from yoti_python_sdk.digital_identity.get_share_receipt_result import GetShareReceiptResult + +def test_get_share_receipt_result_with_receipt_response_and_default_user_content(): + # Arrange + receipt_data = { + 'id': 'test_receipt_id', + 'sessionId': 'test_session_id', + 'timestamp': '2024-10-24T10:00:00Z', + 'rememberMeId': 'test_remember_me_id', + 'parentRememberMeId': 'test_parent_remember_me_id' + } + receipt_response = ReceiptResponse(receipt_data) + + # Act + result = GetShareReceiptResult(receipt_response) + + # Assert + assert result.receiptId == 'test_receipt_id' + assert result.sessionId == 'test_session_id' + assert result.timestamp == '2024-10-24T10:00:00Z' + assert result.rememberMeId == 'test_remember_me_id' + assert result.parentRememberMeId == 'test_parent_remember_me_id' + assert result.userContent is not None + assert result.to_dict() == { + 'id': 'test_receipt_id', + 'sessionId': 'test_session_id', + 'timestamp': '2024-10-24T10:00:00Z', + 'rememberMeId': 'test_remember_me_id', + 'parentRememberMeId': 'test_parent_remember_me_id' + } diff --git a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py index 04456ae3..49ae918a 100644 --- a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py @@ -99,8 +99,8 @@ def test_should_filter_checks(self): assert len(result.liveness_checks) == 1 assert isinstance(result.liveness_checks[0], LivenessCheckResponse) - assert len(result.text_data_checks) == 1 - assert isinstance(result.text_data_checks[0], TextDataCheckResponse) + assert len(result.id_document_text_data_checks) == 1 + assert isinstance(result.id_document_text_data_checks[0], TextDataCheckResponse) assert len(result.id_document_text_data_checks) == 1 assert isinstance(result.id_document_text_data_checks[0], TextDataCheckResponse) diff --git a/yoti_python_sdk/tests/test-key.pem b/yoti_python_sdk/tests/test-key.pem new file mode 100644 index 00000000..98c76418 --- /dev/null +++ b/yoti_python_sdk/tests/test-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/yoti_python_sdk/tests/test_client.py b/yoti_python_sdk/tests/test_client.py index 52ff9115..32fe13e0 100644 --- a/yoti_python_sdk/tests/test_client.py +++ b/yoti_python_sdk/tests/test_client.py @@ -165,11 +165,11 @@ def test_requesting_activity_details_with_correct_data( assert isinstance(activity_details, ActivityDetails) assert ( - activity_details.user_id + activity_details.user_identifier == "Hig2yAT79cWvseSuXcIuCLa5lNkAPy70rxetUaeHlTJGmiwc/g1MWdYWYrexWvPU" ) assert ( - activity_details.receipt_id + activity_details.receipt_identifier == "9HNJDX5bEIN5TqBm0OGzVIc1LaAmbzfx6eIrwNdwpHvKeQmgPujyogC+r7hJCVPl" ) assert activity_details.timestamp == datetime(2016, 7, 19, 8, 55, 38) @@ -213,11 +213,11 @@ def test_requesting_activity_details_with_null_profile( activity_details = client.get_activity_details(encrypted_request_token) assert ( - activity_details.user_id + activity_details.user_identifier == "ijH4kkqMKTG0FSNUgQIvd2Z3Nx1j8f5RjVQMyoKOvO/hkv43Ik+t6d6mGfP2tdrN" ) assert ( - activity_details.receipt_id + activity_details.receipt_identifier == "Eq3+P8qjAlxr4d2mXKCUvzKdJTchI53ghwYPZXyA/cF5T+m/HCP1bK5LOmudZASN" ) assert activity_details.timestamp == datetime(2016, 11, 14, 11, 35, 33) @@ -271,11 +271,11 @@ def test_requesting_activity_details_with_missing_profile( activity_details = client.get_activity_details(encrypted_request_token) assert ( - activity_details.user_id + activity_details.user_identifier == "ijH4kkqMKTG0FSNUgQIvd2Z3Nx1j8f5RjVQMyoKOvO/hkv43Ik+t6d6mGfP2tdrN" ) assert ( - activity_details.receipt_id + activity_details.receipt_identifier == "Eq3+P8qjAlxr4d2mXKCUvzKdJTchI53ghwYPZXyA/cF5T+m/HCP1bK5LOmudZASN" ) assert activity_details.timestamp == datetime(2016, 11, 14, 11, 35, 33) diff --git a/yoti_python_sdk/tests/test_profile.py b/yoti_python_sdk/tests/test_profile.py index d34d4a4e..18246d3f 100644 --- a/yoti_python_sdk/tests/test_profile.py +++ b/yoti_python_sdk/tests/test_profile.py @@ -678,8 +678,7 @@ def test_get_age_over_verification(attribute_value, expected_age_over, expected_ ) human_profile = Profile(attribute_list) - print(human_profile.attributes) - + age_verifications = human_profile.get_age_verifications() age_verification = human_profile.find_age_over_verification(expected_age_over) From 53cd367e1d1efe3ed4afe63f688f38f519450f6a Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 14:26:43 +0100 Subject: [PATCH 04/12] SDK-2242 removed local test protobuf --- .../receipts/proto/EncryptedData.proto | 8 ------ .../receipts/proto/EncryptedData_pb2.py | 26 ------------------- .../receipts/proto/__init__.py | 0 3 files changed, 34 deletions(-) delete mode 100644 yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto delete mode 100644 yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py delete mode 100644 yoti_python_sdk/digital_identity/receipts/proto/__init__.py diff --git a/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto b/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto deleted file mode 100644 index a54cf86b..00000000 --- a/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; - -package compubapi_v1; - -message EncryptedData { - bytes iv = 1; - bytes cipher_text = 2; -} \ No newline at end of file diff --git a/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py b/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py deleted file mode 100644 index 6eec60f4..00000000 --- a/yoti_python_sdk/digital_identity/receipts/proto/EncryptedData_pb2.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: EncryptedData.proto -# Protobuf Python Version: 4.25.2 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x45ncryptedData.proto\x12\x0c\x63ompubapi_v1\"0\n\rEncryptedData\x12\n\n\x02iv\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63ipher_text\x18\x02 \x01(\x0c\x62\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'EncryptedData_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_ENCRYPTEDDATA']._serialized_start=37 - _globals['_ENCRYPTEDDATA']._serialized_end=85 -# @@protoc_insertion_point(module_scope) diff --git a/yoti_python_sdk/digital_identity/receipts/proto/__init__.py b/yoti_python_sdk/digital_identity/receipts/proto/__init__.py deleted file mode 100644 index e69de29b..00000000 From 171bb0c275ab47d5b5c5ffc11533c19b2597a444 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 15:06:47 +0100 Subject: [PATCH 05/12] SDK-2242 added missing files --- yoti_python_sdk/digital_identity/receipts/crypto/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yoti_python_sdk/digital_identity/receipts/crypto/utils.py b/yoti_python_sdk/digital_identity/receipts/crypto/utils.py index 513ee6a0..7609cf19 100644 --- a/yoti_python_sdk/digital_identity/receipts/crypto/utils.py +++ b/yoti_python_sdk/digital_identity/receipts/crypto/utils.py @@ -3,8 +3,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from ..proto import EncryptedData_pb2 - +from yoti_python_sdk.protobuf.common_public_api import EncryptedData_pb2 def decrypt_receipt_content(content, receipt_content_key): if not content: return None From e92d2115ceac8250fc47acb0b68622e7f4899442 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 15:40:19 +0100 Subject: [PATCH 06/12] Updated github actions --- .github/workflows/tests.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 9506b63c..0cddf52b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9, "3.10"] + python-version: [3.7, 3.8, 3.9, 3.10] steps: - uses: actions/checkout@v2 @@ -22,11 +22,11 @@ jobs: with: python-version: ${{ matrix.python-version }} - - run: pip install -U setuptools + - run: pip install -U setuptools wheel - - run: pip install -r requirements.txt + - run: pip install --no-cache-dir -r requirements.txt - - run: pip install -e .[dev] + - run: pip install --no-cache-dir -e .[dev] - run: pytest -v @@ -44,12 +44,12 @@ jobs: with: python-version: 3.9 - - run: pip install --upgrade setuptools + - run: pip install --upgrade setuptools wheel - - run: pushd examples/aml && pip install -r requirements.txt && popd + - run: pushd examples/aml && pip install --no-cache-dir -r requirements.txt && popd - - run: pushd examples/yoti_example_django && pip install --upgrade pip && pip install -r requirements.txt && popd + - run: pushd examples/yoti_example_django && pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt && popd - - run: pushd examples/yoti_example_flask && pip install -r requirements.txt && popd + - run: pushd examples/yoti_example_flask && pip install --no-cache-dir -r requirements.txt && popd - - run: pushd examples/doc_scan && pip install -r requirements.txt && popd + - run: pushd examples/doc_scan && pip install --no-cache-dir -r requirements.txt && popd From bd31f9107aa2a052714f9f9743434bad970c8965 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 15:41:48 +0100 Subject: [PATCH 07/12] Updated github actions --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 0cddf52b..8fbf3400 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9, 3.10] + python-version: [3.7, 3.8, 3.9, "3.10"] steps: - uses: actions/checkout@v2 From 2cd58c4c784d015639ea9cf235a78a489dca7f7d Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 15:45:10 +0100 Subject: [PATCH 08/12] Updated github actions --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5b68e6d0..78448d7c 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "pylint==1.9.4", "pylint-exit>=1.1.0", "python-coveralls==2.9.3", - "coverage==4.5.4", + "coverage==6.0", "mock==2.0.0", "virtualenv==20.15.1", "flake8==4.0.1", From 8a1ac33e061753a1c716b57ecfd72b7e9676634e Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 16:09:58 +0100 Subject: [PATCH 09/12] SDK-2242-removed-files --- .github/workflows/tests.yaml | 14 +++++----- .gitignore | 1 + examples/digitalidentity/.gitignore | 1 + examples/digitalidentity/keys/.gitignore | 1 + ...t Prod SDK application-access-security.pem | 27 ------------------- examples/digitalidentity/requirements.txt | 2 +- 6 files changed, 11 insertions(+), 35 deletions(-) create mode 100644 examples/digitalidentity/.gitignore create mode 100644 examples/digitalidentity/keys/.gitignore delete mode 100644 examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8fbf3400..e20606c6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -24,9 +24,9 @@ jobs: - run: pip install -U setuptools wheel - - run: pip install --no-cache-dir -r requirements.txt + - run: pip install -r requirements.txt - - run: pip install --no-cache-dir -e .[dev] + - run: pip install -e .[dev] - run: pytest -v @@ -44,12 +44,12 @@ jobs: with: python-version: 3.9 - - run: pip install --upgrade setuptools wheel + - run: pip install --upgrade setuptools - - run: pushd examples/aml && pip install --no-cache-dir -r requirements.txt && popd + - run: pushd examples/aml && pip install -r requirements.txt && popd - - run: pushd examples/yoti_example_django && pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt && popd + - run: pushd examples/yoti_example_django && pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt && popd - - run: pushd examples/yoti_example_flask && pip install --no-cache-dir -r requirements.txt && popd + - run: pushd examples/yoti_example_flask && pip install -r requirements.txt && popd - - run: pushd examples/doc_scan && pip install --no-cache-dir -r requirements.txt && popd + - run: pushd examples/doc_scan && pip install -r requirements.txt && popd diff --git a/.gitignore b/.gitignore index 022b5cb0..6c611549 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ examples/yoti_example_flask/static/YotiSelfie.jpg #.pem files for examples examples/yoti_example_django/*.pem examples/yoti_example_flask/*.pem +examples/digitalidentity/*.pem .scannerwork .venv/ diff --git a/examples/digitalidentity/.gitignore b/examples/digitalidentity/.gitignore new file mode 100644 index 00000000..612424a3 --- /dev/null +++ b/examples/digitalidentity/.gitignore @@ -0,0 +1 @@ +*.pem \ No newline at end of file diff --git a/examples/digitalidentity/keys/.gitignore b/examples/digitalidentity/keys/.gitignore new file mode 100644 index 00000000..612424a3 --- /dev/null +++ b/examples/digitalidentity/keys/.gitignore @@ -0,0 +1 @@ +*.pem \ No newline at end of file diff --git a/examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem b/examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem deleted file mode 100644 index c3fc779a..00000000 --- a/examples/digitalidentity/keys/Mehmet Prod SDK application-access-security.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAxtXhHY0ZxiTWSGjC8Y0EHsXpmV3FPJo6Dne1skP9e7pe2i0I -5tmvPVcQsOckrBjVP/H4G+Lzqtu7JfbD5BHZeqrec6PEITGBsrTAhcLjVQzUefN/ -mFkOinlOXBuPxMP5hZttFrNjqPjRhhlYrtQV2uOh/F/UGpqupgtJPKgF6mquDEGA -lHAxCDMwwl+NtZgg1N5w7pDUJYc/cNwI8X0afTTLneCqTSD+t9dARVgcXpnJm1WK -0XyJ4u6ENgN1fUcKPC3MP2isbTZe895dAVlvUH2ADD8qYfP8Nz8tcQ3DbXfjJC73 -hvuawmZIzotHqj2p6Oh9E6F3nLwlQPE18a599QIDAQABAoIBAArWI710T/QKVGpg -WUWIYbHap/NNlr8JicH5mLu1NGauntZFr49DTGdrrBN0GX3OoaqpQZQlf5GvhYjZ -ZM40gdWLY/HJ+lmzxMWMT9TKbRDY0OivkmPncKEv4MsozmJTKvFy6dRbpQITw3mL -PpfSo7lJAC5Mu7bSeNPAWDa30pC2tHtxjtoAstdABgqDMLpwD19O3FkWowR5q+6l -hFHi+bk+pzMc2X51PB1zsbpC+7US5p+N+qeo3ebrnncmj3yvh5OSah1NY+2CDWfR -gLyu0LrrGdgBg2lpAHdQvvQJhnQzVArKhItwVmW1TIcze0oNsZV/BOJetLj4lctz -Ih4xUjECgYEA+ZHJLsOMFmtFKWEOgDMrkRkfLpbgf0HuPM17InDLPDTaUeGjyTdM -CSxQiqbIW6g3bzHZ5idRhVYIPMgZ7/71X3Nueif5nNG5PHhiuNq7o1R2iuJ6FKms -p+/G9NsYG7THm2QlanFqp87hyuMkSt08Ugicb1GaOLs7UAcuCfnnF2UCgYEAy/Vw -5ezN5XiC7XxI/XszQRAuBPnHlQLMSAyUvZ6v2oEFK4nP2ysH5VnxGJFgXGIfykec -IdUnFtenTde8gCdueqFT2co0inslxVV8ETmEO8dJnPqnnXBunX0n5D3WqDBmJwXK -D2POa7xeXM5tdOxT61S0mSKVgtCQ9VU38OdHy1ECgYAZedpRncCVIUokGTZDu/V8 -kFXwiZJNK0vIhSlGsMDuWm7W4PO5PJ3UaeOm47OcN6XBAhO+PNFDjS62Fa8gIqSl -o8DpU19VtMr180wQlrOEzsBzGP9hUJjBY+apZBwn5+JgaG6xWPaMPsAp19oCkmbv -8NUXP/tAQ0ygtLrsZchDSQKBgHWcJ6j+H1CGaIFHXNOGWmzXRqIp4pOjlGarko2x -VthZ88BCbLCGJLx1W9h95CIBlzFOj9LWlf7PBjOWBqWjl0pxguegeSGtl38uJyfL -kdvitCkoRMU9kxuPkxRDMGe12QIBjZ3IQLzRV1yO0IFO0alvI+D2F17io+REasio -pTaxAoGBANSMXb+sAnXgyu1hK/fOI5QlKNqBfK/PcwrI0nYwHzIqs5Xs5/k5NLH9 -yZyIcDrrk5DJDWJ63gA0WjSL5oHzo5IOHSAkcMc/H7L/4rNFdYNmjkMaTK3Ms4In -xZcz7HaJ1Rxvh/y/lyDn27hsm30WpNfi6lcawBFHCTYdfyWIXj8P ------END RSA PRIVATE KEY----- diff --git a/examples/digitalidentity/requirements.txt b/examples/digitalidentity/requirements.txt index 5cece086..5a69a26b 100644 --- a/examples/digitalidentity/requirements.txt +++ b/examples/digitalidentity/requirements.txt @@ -56,5 +56,5 @@ werkzeug==3.0.1 # via flask wrapt==1.16.0 # via deprecated -yoti==2.14.2 +yoti==2.15 # via -r requirements.in From cc485cf513cb86eabad6bf7c737a723ef2b6930f Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 17:04:39 +0100 Subject: [PATCH 10/12] Updated referenced libraries --- requirements.txt | 65 +++++++++++------------------------------------- 1 file changed, 15 insertions(+), 50 deletions(-) diff --git a/requirements.txt b/requirements.txt index 090690a4..f2bf632b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,57 +1,22 @@ -# -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: -# -# pip-compile --output-file=requirements.txt requirements.in -# -asn1==2.2.0 - # via -r requirements.in +asn1==2.7.1 certifi==2018.11.29 - # via requests -cffi==1.15.0 - # via - # -r requirements.in - # cryptography +cffi==1.17.1 chardet==3.0.4 - # via requests -cryptography==2.8 - # via - # -r requirements.in - # pyopenssl -deprecated==1.2.13 - # via -r requirements.in +charset-normalizer==3.4.0 +cryptography==43.0.3 +Deprecated==1.2.14 +enum-compat==0.0.3 future==0.18.2 - # via -r requirements.in idna==2.7 - # via requests -iso8601==1.0.2 - # via -r requirements.in -itsdangerous==2.0.1 - # via -r requirements.in +iso8601==0.1.13 +itsdangerous==1.1.0 pbr==1.10.0 - # via -r requirements.in -protobuf==3.19.4 - # via -r requirements.in -pycparser==2.18 - # via cffi -pyopenssl==19.1.0 - # via -r requirements.in -pytz==2021.3 - # via -r requirements.in -pyyaml==5.2 - # via -r requirements.in -requests==2.21.0 - # via -r requirements.in +protobuf==4.21.12 +pycparser==2.22 +pyOpenSSL==19.1.0 +pytz==2022.1 +PyYAML==5.2 +requests==2.32.3 six==1.16.0 - # via - # -r requirements.in - # cryptography - # pyopenssl urllib3==1.24.3 - # via - # -r requirements.in - # requests -wheel==0.37.1 - # via -r requirements.in -wrapt==1.11.2 - # via deprecated +wrapt==1.16.0 From 9a8008c478e23376e6d5f832422e316574b82266 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 24 Oct 2024 17:08:55 +0100 Subject: [PATCH 11/12] Updated referenced libraries --- requirements.txt | 65 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/requirements.txt b/requirements.txt index f2bf632b..090690a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,22 +1,57 @@ -asn1==2.7.1 +# +# This file is autogenerated by pip-compile with python 3.10 +# To update, run: +# +# pip-compile --output-file=requirements.txt requirements.in +# +asn1==2.2.0 + # via -r requirements.in certifi==2018.11.29 -cffi==1.17.1 + # via requests +cffi==1.15.0 + # via + # -r requirements.in + # cryptography chardet==3.0.4 -charset-normalizer==3.4.0 -cryptography==43.0.3 -Deprecated==1.2.14 -enum-compat==0.0.3 + # via requests +cryptography==2.8 + # via + # -r requirements.in + # pyopenssl +deprecated==1.2.13 + # via -r requirements.in future==0.18.2 + # via -r requirements.in idna==2.7 -iso8601==0.1.13 -itsdangerous==1.1.0 + # via requests +iso8601==1.0.2 + # via -r requirements.in +itsdangerous==2.0.1 + # via -r requirements.in pbr==1.10.0 -protobuf==4.21.12 -pycparser==2.22 -pyOpenSSL==19.1.0 -pytz==2022.1 -PyYAML==5.2 -requests==2.32.3 + # via -r requirements.in +protobuf==3.19.4 + # via -r requirements.in +pycparser==2.18 + # via cffi +pyopenssl==19.1.0 + # via -r requirements.in +pytz==2021.3 + # via -r requirements.in +pyyaml==5.2 + # via -r requirements.in +requests==2.21.0 + # via -r requirements.in six==1.16.0 + # via + # -r requirements.in + # cryptography + # pyopenssl urllib3==1.24.3 -wrapt==1.16.0 + # via + # -r requirements.in + # requests +wheel==0.37.1 + # via -r requirements.in +wrapt==1.11.2 + # via deprecated From df96b8f07fbd71c5ac07f762bdd9639eb696ca30 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Thu, 14 Nov 2024 16:34:44 +0000 Subject: [PATCH 12/12] removed unneccessary folder --- examples/yoti_example_flask_di/.env.example | 3 - examples/yoti_example_flask_di/Dockerfile | 12 - examples/yoti_example_flask_di/README.md | 6 - examples/yoti_example_flask_di/app.py | 124 ----- .../yoti_example_flask_di/docker-compose.yml | 12 - .../yoti_example_flask_di/requirements.in | 11 - .../yoti_example_flask_di/requirements.txt | 79 ---- examples/yoti_example_flask_di/settings.py | 5 - .../static/assets/app-store-badge.png | Bin 4077 -> 0 bytes .../static/assets/app-store-badge@2x.png | Bin 8819 -> 0 bytes .../static/assets/company-logo.jpg | Bin 4682 -> 0 bytes .../static/assets/google-play-badge.png | Bin 4957 -> 0 bytes .../static/assets/google-play-badge@2x.png | Bin 11267 -> 0 bytes .../static/assets/icons/address.svg | 3 - .../static/assets/icons/calendar.svg | 5 - .../static/assets/icons/chevron-down-grey.svg | 7 - .../static/assets/icons/document.svg | 3 - .../static/assets/icons/email.svg | 14 - .../static/assets/icons/gender.svg | 5 - .../static/assets/icons/nationality.svg | 3 - .../static/assets/icons/phone.svg | 3 - .../static/assets/icons/profile.svg | 3 - .../static/assets/icons/verified.svg | 6 - .../static/assets/logo.png | Bin 2988 -> 0 bytes .../static/assets/logo@2x.png | Bin 5609 -> 0 bytes .../yoti_example_flask_di/static/index.css | 174 ------- .../yoti_example_flask_di/static/profile.css | 429 ------------------ .../templates/dynamic-share.html | 84 ---- .../templates/index.html | 88 ---- .../templates/profile.html | 160 ------- 30 files changed, 1239 deletions(-) delete mode 100644 examples/yoti_example_flask_di/.env.example delete mode 100644 examples/yoti_example_flask_di/Dockerfile delete mode 100644 examples/yoti_example_flask_di/README.md delete mode 100644 examples/yoti_example_flask_di/app.py delete mode 100644 examples/yoti_example_flask_di/docker-compose.yml delete mode 100644 examples/yoti_example_flask_di/requirements.in delete mode 100644 examples/yoti_example_flask_di/requirements.txt delete mode 100644 examples/yoti_example_flask_di/settings.py delete mode 100755 examples/yoti_example_flask_di/static/assets/app-store-badge.png delete mode 100755 examples/yoti_example_flask_di/static/assets/app-store-badge@2x.png delete mode 100644 examples/yoti_example_flask_di/static/assets/company-logo.jpg delete mode 100755 examples/yoti_example_flask_di/static/assets/google-play-badge.png delete mode 100755 examples/yoti_example_flask_di/static/assets/google-play-badge@2x.png delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/address.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/calendar.svg delete mode 100644 examples/yoti_example_flask_di/static/assets/icons/chevron-down-grey.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/document.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/email.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/gender.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/nationality.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/phone.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/profile.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/icons/verified.svg delete mode 100755 examples/yoti_example_flask_di/static/assets/logo.png delete mode 100755 examples/yoti_example_flask_di/static/assets/logo@2x.png delete mode 100644 examples/yoti_example_flask_di/static/index.css delete mode 100644 examples/yoti_example_flask_di/static/profile.css delete mode 100644 examples/yoti_example_flask_di/templates/dynamic-share.html delete mode 100644 examples/yoti_example_flask_di/templates/index.html delete mode 100644 examples/yoti_example_flask_di/templates/profile.html diff --git a/examples/yoti_example_flask_di/.env.example b/examples/yoti_example_flask_di/.env.example deleted file mode 100644 index d0021fb8..00000000 --- a/examples/yoti_example_flask_di/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -YOTI_SCENARIO_ID=yourScenarioId -YOTI_CLIENT_SDK_ID=yourClientSdkId -YOTI_KEY_FILE_PATH=yourKeyFilePath diff --git a/examples/yoti_example_flask_di/Dockerfile b/examples/yoti_example_flask_di/Dockerfile deleted file mode 100644 index a590e43a..00000000 --- a/examples/yoti_example_flask_di/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM python:3.6.3 -ARG YOTI_SCENARIO_ID -ARG YOTI_CLIENT_SDK_ID -ARG YOTI_KEY_FILE_PATH -RUN if [ "$YOTI_SCENARIO_ID" = "yourScenarioId" ] ; then echo YOTI_SCENARIO_ID not set; exit 1; else echo YOTI_SCENARIO_ID is $YOTI_SCENARIO_ID ; fi -RUN if [ "$YOTI_CLIENT_SDK_ID" = "yourClientSdkId" ] ; then echo YOTI_CLIENT_SDK_ID not set; exit 1; else echo YOTI_CLIENT_SDK_ID is $YOTI_CLIENT_SDK_ID ; fi -RUN if [ "$YOTI_KEY_FILE_PATH" = "yourKeyFilePath" ] ; then echo YOTI_KEY_FILE_PATH not set; exit 1; else echo YOTI_KEY_FILE_PATH is $YOTI_KEY_FILE_PATH ; fi -ADD . /yoti-sdk -WORKDIR /yoti-sdk/examples/yoti_example_flask/ -RUN pip install --no-cache-dir -r /yoti-sdk/requirements.txt && pip install /yoti-sdk -RUN pip install --no-cache-dir -r requirements.txt -CMD ["python", "app.py"] diff --git a/examples/yoti_example_flask_di/README.md b/examples/yoti_example_flask_di/README.md deleted file mode 100644 index 2589ede5..00000000 --- a/examples/yoti_example_flask_di/README.md +++ /dev/null @@ -1,6 +0,0 @@ -#### Flask Profile Example Project - -1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values -1. Install dependencies: `pip install -r requirements.txt` -1. Run `python app.py` -1. Navigate to https://localhost:5000 \ No newline at end of file diff --git a/examples/yoti_example_flask_di/app.py b/examples/yoti_example_flask_di/app.py deleted file mode 100644 index f831c84f..00000000 --- a/examples/yoti_example_flask_di/app.py +++ /dev/null @@ -1,124 +0,0 @@ -# noinspection PyPackageRequirements -import os -from os.path import join, dirname - -from dotenv import load_dotenv -from flask import Flask, render_template, request - -from yoti_python_sdk import Client -from yoti_python_sdk.dynamic_sharing_service.policy import ( - DynamicPolicyBuilder, - SourceConstraintBuilder, -) -from yoti_python_sdk.dynamic_sharing_service import DynamicScenarioBuilder -from yoti_python_sdk.dynamic_sharing_service import create_share_url - -from yoti_python_sdk.digital_identity.client import create_share_session, get_share_receipt, get_share_session, create_share_qr_code, get_share_qr_code - - - -dotenv_path = join(dirname(__file__), ".env") -load_dotenv(dotenv_path) - -from settings import YOTI_SCENARIO_ID, YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH # noqa - -app = Flask(__name__) - - -def save_image(selfie_data): - upload_path = os.path.join(app.root_path, "static", "YotiSelfie.jpg") - fd = open(upload_path, "wb") - fd.write(selfie_data) - fd.close() - - -@app.route("/") -def index(): - return render_template( - "index.html", scenario_id=YOTI_SCENARIO_ID, client_sdk_id=YOTI_CLIENT_SDK_ID - ) - - -@app.route("/dynamic-share") -def dynamic_share(): - client = Client(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) - policy = ( - DynamicPolicyBuilder().with_full_name().with_age_over(18).with_email().build() - ) - scenario = ( - DynamicScenarioBuilder() - .with_policy(policy) - .with_callback_endpoint("/yoti/auth") - .build() - ) - share = create_share_url(client, scenario) - return render_template( - "dynamic-share.html", - yoti_client_sdk_id=YOTI_CLIENT_SDK_ID, - yoti_share_url=share.share_url, - ) - - -@app.route("/source-constraints") -def source_constraints(): - client = Client(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) - constraint = ( - SourceConstraintBuilder().with_driving_licence().with_passport().build() - ) - policy = ( - DynamicPolicyBuilder() - .with_full_name(constraints=constraint) - .with_structured_postal_address(constraints=constraint) - .build() - ) - scenario = ( - DynamicScenarioBuilder() - .with_policy(policy) - .with_callback_endpoint("/yoti/auth") - .build() - ) - share = create_share_url(client, scenario) - return render_template( - "dynamic-share.html", - yoti_client_sdk_id=YOTI_CLIENT_SDK_ID, - yoti_share_url=share.share_url, - ) - - -@app.route("/yoti/auth") -def auth(): - client = Client(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) - activity_details = client.get_activity_details(request.args["token"]) - profile = activity_details.profile - profile_dict = vars(profile) - - context = profile_dict.get("attributes") - context["base64_selfie_uri"] = getattr(activity_details, "base64_selfie_uri") - context["remember_me_id"] = getattr(activity_details, "remember_me_id") - context["parent_remember_me_id"] = getattr( - activity_details, "parent_remember_me_id" - ) - context["receipt_id"] = getattr(activity_details, "receipt_id") - context["timestamp"] = getattr(activity_details, "timestamp") - - # change this number according to the age condition defined in Yoti Hub - age_verified = profile.find_age_over_verification(18) - - # Age verification objects don't have the same properties as an attribute, - # so for this example we had to mock an object with the same properties - if age_verified is not None: - context["age_verified"] = { - "name": "age_verified", - "value": age_verified, - "sources": age_verified.attribute.sources, - "verifiers": age_verified.attribute.verifiers, - } - - selfie = context.get("selfie") - if selfie is not None: - save_image(selfie.value) - return render_template("profile.html", **context) - - -if __name__ == "__main__": - app.run(host="0.0.0.0", ssl_context="adhoc") diff --git a/examples/yoti_example_flask_di/docker-compose.yml b/examples/yoti_example_flask_di/docker-compose.yml deleted file mode 100644 index fbcfc4be..00000000 --- a/examples/yoti_example_flask_di/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3.4' -services: - web: - build: - context: ../../ - dockerfile: examples/yoti_example_flask/Dockerfile - args: - YOTI_SCENARIO_ID: "${YOTI_SCENARIO_ID}" - YOTI_CLIENT_SDK_ID: "${YOTI_CLIENT_SDK_ID}" - YOTI_KEY_FILE_PATH: "${YOTI_KEY_FILE_PATH}" - ports: - - "5000:5000" diff --git a/examples/yoti_example_flask_di/requirements.in b/examples/yoti_example_flask_di/requirements.in deleted file mode 100644 index 89d0b0ee..00000000 --- a/examples/yoti_example_flask_di/requirements.in +++ /dev/null @@ -1,11 +0,0 @@ -click>=7 -cffi>=1.15.0 -flask>=1.0.4 -jinja2>=3.0.3 -pyopenssl>=19.0.0 -python-dotenv>=0.7.1 -requests>=2.20.0 -urllib3>=1.24.2 -yoti>=2.14.0 -werkzeug>=1.0.1 -six==1.16.0 \ No newline at end of file diff --git a/examples/yoti_example_flask_di/requirements.txt b/examples/yoti_example_flask_di/requirements.txt deleted file mode 100644 index 81f2e9f6..00000000 --- a/examples/yoti_example_flask_di/requirements.txt +++ /dev/null @@ -1,79 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile --output-file=requirements.txt requirements.in -# -asn1==2.2.0 - # via yoti -certifi==2018.4.16 - # via requests -cffi==1.15.0 - # via - # -r requirements.in - # cryptography -chardet==3.0.4 - # via requests -click==8.1.2 - # via - # -r requirements.in - # flask -cryptography==3.2 - # via - # pyopenssl - # yoti -deprecated==1.2.10 - # via yoti -flask==1.1.1 - # via -r requirements.in -future==0.16.0 - # via yoti -idna==2.7 - # via requests -iso8601==0.1.13 - # via yoti -itsdangerous==0.24 - # via flask -jinja2==3.0.3 - # via - # -r requirements.in - # flask -markupsafe==2.0.1 - # via jinja2 -protobuf==3.6.0 - # via yoti -pycparser==2.18 - # via cffi -pyopenssl==19.0.0 - # via - # -r requirements.in - # yoti -python-dotenv==0.8.2 - # via -r requirements.in -pytz==2020.4 - # via yoti -requests==2.21.0 - # via - # -r requirements.in - # yoti -six==1.16.0 - # via - # -r requirements.in - # cryptography - # protobuf - # pyopenssl -urllib3==1.24.2 - # via - # -r requirements.in - # requests -werkzeug==1.0.1 - # via - # -r requirements.in - # flask -wrapt==1.12.1 - # via deprecated -yoti==2.14.0 - # via -r requirements.in - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/examples/yoti_example_flask_di/settings.py b/examples/yoti_example_flask_di/settings.py deleted file mode 100644 index f9b14cb6..00000000 --- a/examples/yoti_example_flask_di/settings.py +++ /dev/null @@ -1,5 +0,0 @@ -from os import environ - -YOTI_SCENARIO_ID = environ.get("YOTI_SCENARIO_ID") -YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID") -YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH") diff --git a/examples/yoti_example_flask_di/static/assets/app-store-badge.png b/examples/yoti_example_flask_di/static/assets/app-store-badge.png deleted file mode 100755 index 3ec996cc6288d68279c1d735c9d627c64d8a48c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4077 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 diff --git a/examples/yoti_example_flask_di/static/assets/app-store-badge@2x.png b/examples/yoti_example_flask_di/static/assets/app-store-badge@2x.png deleted file mode 100755 index 84b34068fc22aa74f388f6db1a583ac6592f5420..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8819 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 MlZs4*lPCscyWU2PMP1t zz5I@yb@e?jt|hLv6AkqOJ%D6kS$^Qb3O^_g6pEDBO9&%d5`BbJYkj|(Fp$d47=faSw3D?uRPHLR$0DAYPEHzzmt|2?1=fR_WD0*gq5 z1YqGsAbAl`6%d0-q7YvOSRDu!7$X}y$2v48{6YSDz=A*`Sy++FsNt_K!SjHX7qwx_ zE?u^b7RT5nys*13$E0#d?tN0hXW2@X+H>4HWF4AcKu~DY)@|FRWn>kVlvPyK)b;l1 z8{iF%jI9nHvbH&FYj@)0sngCbu4jCF{RsX6=P!i*9u^+)$Cb#~xa&9K6B2JGrKM+N zX5GDa|H0E|dHDr}e-{fZT27f-2BId zPm9aE5CFMC3x2N<`-vAX%!`GU70JrJ%nQNd4-1l)6}4p-+Xh_=_G4ZfC3aut!0wGn zeNwSba*rjI@3?m>nqNwhyp^_0?F+O2j9AEj#q2AwZ@hW{7ZL$G56KH~U z^q{Z*Owx@TZ@iAl~1j2~U5YRRChXA_(j^Q9i&7Fe4m`n}?1}X0$a5d*nBpAh!o!&yA#mWH! z71hlU5Wq9!KI0bAZsiNAMG&A&-c1WuBQ4m>6Y1D4BGt-B(Pe zfvYa?{oV$v>VI*7gSHD6(eShVesV#VnSsl9K3?14|L}2C3%g|CpvmF0cVmrnfIzn+1kV|Z76;vE_!5C0%Ml1e`4WL3EmC?_GS6psyMBz1 zPf$5m;X^R)t(FXKi|LS_c47IzcVNcu@#C_*XGJC9!X;1jOL+!0GyHlqC^>#9ERoW% z$VocVJg$*_@o(}q+~EDrnTm4_=you%h+O?sF50rK3=2-9j0x+H6Ul+K(Yu`PP2e`Y znOj{for0vL{n=rT(8qq09#}8BCz%lnhuCBs1SD-8VZAXMD~a>m1rTU8u`5nsgnIm& zNgcj2F$mOZ9D=~(=MZ3C*YcfPq5s1H{6c+vd0YF4Y7E+llFWtcZfdlvs2+(*fAISD z<%K~~m5q~Or7Zql3Wlw<)J-|nXL}XrlG?Pwhzs3az?3_rEqU(Rowvs;wQushvt5{} zQdcg?><)N{&wCbnFs`nk`0?%9`#Vma!&^QHjilz zm3B2ktsc|vF^8`FvrkVvd4HFN56#DlKZw^&o_9yC-)GmYCcX(CHRc>bxXLCYt;=bX zFGS^@l+om~NP$^dq@2dBRk8LDZw@-!tD38-&1uPwhEWI1+h#6uDi-qR6mw@&?O9LP zKg(*d8X2Ff2L2cJC&anN=ibxXEYP^=X4;iPS#34&k#EFl9i0;++`kZ7t=?B^7oWBm zRq$l%>xd%(?ZO9T21SX@N>hBJ2?H30J~?;2(?l6phy0dMbE5j>{tj$n)L(t**>w=m zdW>jy>|P+g;J3^DL$dwRRDo_W#l$7#*@O2;WK=BihPNfdm}VW9SxFJGsU)-n5KN;e z!m(ZE2376^`=fK4(Qopocdsx^GTf}Sca%SIJF=je_#toXPjTG9yLWN7&-t`iD|KDI z=$Yg4B*6Jl2R$|Qp5bU|A^!7znv9^m*!7K<;}~Ns(|2i@DwR9Zor+F8WApx5F9khN zjSVB%^pvwld5oIaTSlKsF4W~Vl6##tVlYQ)$ip#PN}P+d2_MKp(I3$+sHsvCWE2GhZ7i}+zy0p{uDDh=(M>*N7j*QFex%50kVcWZYYc8g< zL(zE6fjk7UimBO|&2VLpP&Ad`-b8U89g48b%1AzYbZr<+6{A< zDnMfBk*&zReI^D+RQH`JL!PPAr({Vo&pxkIa_K~?FE?vRiYMzk#S z6swQj5!*+Nef>kTdy@N7L^FKo*0{9SWg?e|yYumZjdOP;9P`ymaT>5I2<)bUIjzKN5$=p@DQ7>18llI-R9ivT*))S$M4v0BL{H zzhypsmd6Y)$;!b#uc6cUBQiH_4{&H;#9P=#_!=0ylT1hvcQ(h{ZZZ;mCDHU=>aX-z zQ)f3P0bd6(tKugnm++Rc4U`E}?T&hpqG^$59f7BVH7;7%XFa58=(I6#C$cZypZ<`0 z>r@8eo}+E==LgTdHQo!}yQoDoF{Fv+^Tc3PI+Jd_YRk_bDY`PmNqQbPpm{B*>nBA?i6-=`7xweRpC&ngMm z2deDqbF#BjjHm9L5Lzl3QgW^$V(AzzlhX-4CR<7iRFp^iwJ3*bfr5Hm%w-*sOMI`+ z5cw3JD<76`YqlycjqOvo)AqWfPKx(&?QSme1$v{ewg`=z6zQ9&B(LtN-g$N~CWJ#( zR(DKy2#fGoeAR22AKNqCukr9ao2ZQW?!pey{M`@I!VaMy>;z*dc`UHdhh+iol(7D3 zk>JsNxE3kTFpF@rN;d|P5WsE^Uw}ZOdm%H@?o9_=sfGO@V4wVHQyrC!NM@xDEQPw4 z(=A(2#0Ut?^1LBV?IJ=T6XT%eBeq=Fg}*QC7vMmR{305yaMc9ao{Tw~6 zBhPMZP!#&DngdujVi6+=FOR(<1y zj6GG)=-uiY%K9Ln-cs19py@!1*q>y&ZBjqdRDvR1)}87h=!}XCAq1OkUhKNJK-w!R z+16i|>#0a>$lm@Wv066TrxIO5Ev&pH#&ss{%;|Xu9O@V*)qQqMKX=NPq}Edws)(p? zxe#75IDo1j9M{}JBnDlPFBMR(edMZMq)PA(a(Z4XIh%`LAd){ZC9I7rP&5WgBlDAOv2g2p&OgDYjr77IkeD=Dt%{AfA0)Oe@p&&ul z$ek9EIZ)Hdeu$zZp3SP4IEM}&tjMs)JRU!Avd zC)5d960t&goZ&Y3Lp_%I){a`RgB?q9!+KQaRl=ygMf8Zj@0f(=7ZvTPRS^|Rq)P$A zxty{Uhc7#R&uZ6%ayMb>7COz;a7UwR7&A{4HC+~jk)K=O(=@dA^1yfjJUUixn|qGMMAwegzS|~Lp&gPAPJaupsTp z#Np@hSBYaM+smou1B@W}05NN69>n33?GX6#59ud`aJRS=Lm5~UQiK2ls{#SBl<}1< z%>MH&>S!*i5coZAZ3*tF<`D?!ZA*raRyYKP=oUKcKj2zM@t$d5s_~8K##J5zUuc-X zpqgP&Gb@;2XTUn`h@;=}Ao3GoyAZ#aPp>8KgQD?i ZenM;Sr^Md!<6%u2&LYxj!8qvczW`OzPx|7fD1xRCodHT?cqnbrPQ>OEw9;1woJ^0xG?T^dePy5kV0Y&_hs9@#Nq{@dRw2 z;;En?AVsA2E?t3y5IUg*2%S)ap_@iBH@`{V-rLO*vP<+5ee?0(*XI4pym`}KAaVv@ zIebdmdTDGmiv}L@z)HbyEbXrFs9aem&0;Q#2KUPY%Y~218Jy;mwgU!RE$t!Wg0;0M zaGw;2lKWdpJLM-oHfgh^RhMRgMS-lMKw;?!(rg|iSu<%HrCDH6Agd{mEFIQ`B>P^P zLw{!VC{}-eX$ruRjDsYDk(ccufByXR{PWLK`SRuI+_`f!XU-hm7AjPTx_9qR0RaKz z8;6s}{w_$5XCcx#-TFJ2Z3VOuBjVCgshWml`!{q zTeN5qMMp2-|t{`>FiwY1x8NfW2@OO`C*0Rskbn>KBD>(;Ft78b_EiWOsFT;8%}3k!2Q;hV?(3-2HO z->WNB$c_LtSgBGa8aZ;LP7nJvYu2RXjg&)f+Z6ATJ~ zO8)xmuL(>Ibqo8?0{0Zi_DqI{w{PD*Wu~{@dW+VrTc-`S6eJO@M@2=^mtTJAeuZ9~ z-M@c7!L}2%sJ0-wfK(J!Nwa6qRz?F8f}!7g?>(9_WeQ!objiVhUJUuCz9ri;8K{p3 z3C|8~`}Xbh$Rm%aK@Vdbo?YF#b!ptVaVp79oH(IQ0q%nubd^Yuc6PfRmldEc!9ZrX zb?cU@KuD^VEnCus3m2$$>(;8CXUv$P-Yo6|K$5LrznJ>iy^wSR83+Gm>SfNX7*sviTJ$h8}j5R|-i8DYOHf$iM#p>0o z)9u^0RT>&N#m}~AS%Z&nf1VOECr%GR+qkhHmcC$Z&a*OUW%L}(j@X&j$OSq@jr$FFh)M`WT;&E z3R20=Lnwd6Dpa>mV=DAu5xRQ(EXCirTHyB$41rK zRHUjM8jDLGKrvBgC@DVS4~DYJgbDY7c9bczgo-< zr)=Z~Q`U36$s4%qveo>_v4#Af=*2wl!gBuCnz3A`LqnGEL6)XjB$K8>-WT$WIbbUl0{(2!+K4V4jnpZKj=$5V@DJ3n8o|$zJ2>T^)2BR#b35; znbY~-bFFUrKI&D1+$Wb0`S|;guQXeL4_&-rr@bfgkWJoH|CBf?U$YFoK6MBUUpSJg zG_ES+-aqu~*N-F@sS*KUim$%e-tU%d9(YZ|LUyi4+sgtSn00>tJpH**5soB0|2 zpf6O9AP|iPNr|eHX_EFb(r@S=ct-PPysWy%WABng!TS39Duc-$`3Rf($+>ek0%-dw zqUIH@lbsS(KGtqgnQAqzMBA5#(}J;!=~%=`ujY-i8U$OR8WHgyIB;M(@jrh2c#X(| z_=tgJM-cg&CDTGF6J9Qu2>?mbYZV9r#>n9!PPl8=E=O=UtH$+q@}4u9j06ehCyd6F zB1|S6Cjb0od-`S|g`D=Gw&i2VZc8CbP9Q%CjJJNi9yM)Woz~9YLi4^@M$xA(WqQ7N z`=tUybA?DRAwiAgwLAF!d(>b4`2n^aQfb$(U)RMRKYpBEc;N+-$`N|-!3Wh|1(Rh2 z$cDhaMPI-E`l|+}Gs3^0Yo0S1B$=p7U%Tu;lBI4mrQ;`3?ZoZev?1DuI+u$fdulZe z`6eZhe?c3)A|a0?siTH7iX?%Hd)z_s;>D>!g9eH#HX}(1kk-86L4pA?k|a=b zQ18G2lSD*BxEmtfIak#S>4vHhFj>)}MbqIMCy?o4lLZxltHa~}O zn@u6yc5^7V4Ux7rlv{>wpea`ZZHpO zSBHb_{uz${=9_N{{h7skV$^xb0-T!I94u;-h3HNV=fs9_;7W3x&!0c9eHlMu+p!cp zIyor97+Sj~s>ndoEynNPzrUkDV;--*`YKDB%L#Ym#*HjxNGuP5CC|$8K=itJfeypJ zh|p20d(|*EZWgbTMeVbcC#4$)<8|!VQBkoDsX}qWsa&}-8*_g9?YH%s6j>_1s82l> z@02CaY7CcdD>j|h+10LH+i^|oU8ixRvY5}FJ*(FxjpnG!Gw9)F>v=tynEa_WnO&rq z^hly6VV8+eOOZ7DCr_TN*I#+%75etuZ*?D127|!Qyi=tYY`UdOmud+U&V$BA*aZax zs8q%}KL7l4ovYD*Mq4LN6~qjW1Bw>qR%_RA`yJb)g>i>n(sqY&$GzLQQ^Ypzd?1`V z9}4F#k=waV)E@rq;4;25=UwJ$Lz!ob6k8zMrSCBRByGL4ZNr(j595lZ3%eZ`m8*d8 z@Nl=+r7A00=cme9TFbL4vhv)k?2pUQL@eb*yW|2$A$@AZp!l z-gqX4Br|Yaw~&;*di8SL=PJpJejr(;gq+*9ZOg(mx^~bPfVBV*b2P#v&6_uO^eGIi zc&ILhwlMn4VC%UggZP3y<|mSj(hPq{GIan{nx#s%WPcl#`1Jf#8BzMNi42?iZImYN%tvS~tnKEfAf7&dH}+E}u3$7xin zqV^v(UP2)K5ajgf)9Qi2`veFBsHDS3D5$o@MEUc%ms~*%4EV|vY%}Cqe541QLnMoY%hG{cPU6Syc)ak1vN1sxSi3E=ER@8}^ zAu%ygBk*7{M8~0S04Q2?1u!fOYs>@c!l+4u(Ht8a>x2$msCRg8u7C$yqC^QjjRy@! z0nCcIO`kqp3m`*6LNZiCa6s_Il`$?M~@R-TKLF{^7IT}re!7W@M_r*ryVTJ8 z+@V{$V=2&{M1kE(($<4#XpAt|s*ro9I#3UWuZDW}?oFajlrRSl9JuTH5GMj7fT_SN z@RR`XC?Opn1_3wJ8z}QVr)QpN$G$i>D5w!>HJiSW5DTUKG_T zUO}MUpLT6JO)m^yK+Oiu%@l(fLt~)`BOn9;BN=a&t3eIb^zp|ZlXzBo8;nxCjgSgZ zjWG24OUbqw@JSntdOS3|)ld-t`ASKRa5F9paxfo^W~dRdVN^vTe+qSC28;t~=gQE) z!Mu%NG5P}Tr=Nc6U@$l-VtZw%F42#n>ad?#C|tO(qC(P|8LUf}E)Gd%?hA~JECK-Y zf|&q`3#Y{s@K}_-<4_ z@6(qGFCRQeWK-M$m`uV*`t2&NL){v7vOD~{orA@k=@wKxn0ubx!Y{_{mlnY~0MBzGWhD9Z=E zPH`{vC+dsw?WQ{rHN_$Y9rJ@Xx^m^pbg1wY#ny9pr|1{oSqd|!!$nL@-gxy=F=r|9 zRvdV)@&}O+Ip=t^nH&QU#tVqUZ2TSFKv*_@2`hAN|7f1TMTq%mWSy>@pbDP&-h! zFwP+fjd&mII@|{%9)CUoP5>m#p+kpUhtOk4ahM8FS+L;@kJc@+oCJ;F)S%1<^9Mj$ z!=pw_4u5_DV?rXs?m~dp+%&fWibUg{%E#=T><@_>{>+>Z~6 z1l!9~`z+!$wWji#2H*0S$NF*Qe5r4%jOi)Y=NNpki8TI&i~%C)=Cz8dQz?e1zt}-e z*UWf1VPEV}7Dq>=oct{kZ`R^PoJ=SKND%|pekqA+4ZTc74kXdOq+_(;CE3kz zek~@FC-n{CFDs7Yrom0v=9AurkO_IzcTU5X(i%pT9e}ME#4IXIkCD$5b)KWwFX0(S zRzpHV-mc{+7}KMhZFvH@`>NUeLGNYUzEF1#vSp1?@98|U3%&$Y8Hr*T>;MUnGX5|N z(l*&;j^54#e_RGd1QM%cs7){X@!as2fhfp7ANeLS-J+W~v_L3;xEPFg82Jcx{uuz- z6NEMpZ}c?qYg+#?kG)IN&DO3(fy_`qYy4Av+svR_I2Hvwr@#_OG9>pxq+#Czivn3m zfh6gywy*qzOZzD+d$#(rD3C^hg|gjmNUw5ofd4Gwk+h4}#-hOeQecmC&`#O~qcRTP zR;`d$S{fJ)63zmP0^U#ng>Cbsy(mrHY(tXCu>z>*?w4l+bkvvYiHeZ^%MHN=BKrYQ zH9>X&0I+n(OTE|dL^#PpgKG3Y4hDFj$*M{82SvPo^#=ddoA<9~5Xm5AmKN;BW=Kp(6z^iHdVGW#!XAWJgPSoyI-jw(! z@GXg2Ej0Z{VI_{0vX-(PU2o@x4*aG-q<^B6jzrq{dC(?9f}AbhZRYVH<6IT>e_AcR z7Ci6Na*i+3jIf@XjmJ$ZpX*HfF=65Xol6dLWhtfF z3Il!b)zlKa=_3}>D9Rqc1XnGj9Q6nERfFXR^CX)FByk*N@$Psg)gC5eGFw6@DE}h@6p;@>qg18n{jS&|NmD!^sle<2t*CTriP$ zv+L^WhK^4CkkD{>z20cdT%I<-m3NmHYt7(QEf$N9!FXbVT1G|``fcv^$9)7UG3|T5 z;VJ?P3l+Kq>Hw8pH7fqZg0fVZ4-XR_ok{I7cXH+*{jN2SuNOYz0d>P}KW|7b7BxiQ z+DTMgf>iwk9Y>goBgz7}O@1H?9aYpzX%;kW)wnsG6sAE2+UT#SW49IL1XS%$BbiC{ zi9@!v2`UL7~M{=GYKGCwZ-pRz0SQPZwR<+2~ zY`p9#B^+po@pEax-r{9MfY4f_6T_hC4t9-&`@_qM)>U}13Z>9_HiAcdi)f6f&kb+3v_MXoW8Q$%tw#RIS>u9{jC1ePo1GL_mZAwPub-iW1r8gX zKm1M|B>qZ5M4tZq+QAF>6KuTaQr)%_^4J|{AczlTC1jtA51&E;=Hl6iD-2hJ9KEyCZ%BqOYB@m#>XNcq0(LbS-|oF?<4NN~42BB9{CHDYP{ppw$0i?0FH!BVT5R5%G$2F%x^2>*-$(0JF z&-XvWDKmu09lH^P=3?Us_J75Re;JEYI``f$qjlaVaBTk`PL2upRrc4ajhcVP{xAeCv3V6!Bi zsK(8j$3}%pHxsA%5I({~U4H?B(Za`ojss-&u7}HSMwUA^-_s+J5Qey&ic~b zs{fhGh999D>dF82e&hKw;Ow}5@+~>BT0SkczV6TE<|L%|jbm^h*-*Nz$J}rVZW-TM z4iXW5c00omD*S`0p1eBO1IS%Jhkt@AHdW*Mm%TRKdPr+-o9wppVt`a>pd8IOpP5Gi zp_;DNIEh{fNS+9KnGB6&UfT)I!=SYE1SBZ2drgMb&Pa+(|HEX!Q<6r#Z7YKT9@)82 zJkYR`u-8e{Q^K`VBvq|2o#@qRs)lGZ(@^2L4hZNY#) z`3u3Z^RHF%M)cXJ7h8>Cb z?WxlJpc(spr@B!SKZ~^>ugfQb$)Pgo1D-;DL;lc=e<+970+-Wc^gOI;v&rXoT2-+D z`?$wGYa98Jwl{^Cl%Cc3ew5Hvf$5__uNO%xMef5tS3jPJq=FJVo$L$~BAD@MR0u)gzLm*FT(-DUODTY~FSXvcEO9 zGfF9G0go)aDOJ#=J6X|IsMQ!s75U845%*4~^|&#-;>owP;$draJm>l+;XlB#9gT#7 zSV!R_-zIu9t)MNh=9Cbmo4}r^#!C8ntZAc3A>)mvEvJ5C!`Za_jj&~e>;AHQ>bNfB zQKw!?HRUqhD0M_~;4X3pH;(GJf1?+6GPX8pPbG2JFW3ZeZ8`jc&(}1&>P9B}LYO;z zJ}1urQR-t!3rchoev97WAMjOljd@z-fr&+xwb+d;PgjzkIV}@m5Vszj`aB>~`AUPe z*#LxF=c_z=WY{MeY#BfeuH}CntqKCNc|FaaCuFnOv$_I0``y9-foIoN{=u`Z}VK0X6{hYB~Hc zW;lQiWVQOS`WRT}0)+|ae1d}pIu1YlI4uo2IieZdeVp431S3o^>pwr;c8$}p!A>o( zIe_n=mXS+xI>$Tua-qa|$d0*sB=_kGdeY1(u!3))7hSL_yCy4LEU!Jd7IRI%#(0eU zm1!cox|Sg*g>=+na9w0rsQE#U-RpdEW^dD9>B*3*hbG!}_D$cPK-QFjI-3gN5M~la z5wyJH7%`~_q~#h9`kwH<c-X8x375V73NI2-CLvp@z7>Q_#Qn`vqlI7J4{bIVT*-FMa2*j}by z^^EvtZXm2JNICr!Pyf@lTm>6>l=>NN!?IrDZ3h5B{?9abV-7;+@}~r*4O7 zqmezC>1Xx=t{ecnWk?pcMN|OOPC5(%yQd6hyLnRtwbTT8Z*TU4P3Gvm0MOca9$TXC z-%#RB8a|ODuZtwMC@wyw>!uf8AxgV^qmr=fR?N|`(@>mZ#08@!xOVB_Shh}Qd4e*J za40!!E5rN~g4?PSffKY1#Q4!j#COq}+Lc-&Jz6Wxsr6Owbi=aTYu@qOdH|YD^jaTlC;v`iiQV-&S z53WgW2&1@0&gw?lnd069MRtQ8v!_-kw6pk% z!1lU9VJ_2}02lGt`t52IpRkx#t&um*J}WIni%yl_rSpM@#3X9KlE_H`#hlB8ycaG% z*DP{TBJOKJRZ?8ele?Gcp6QGQkTcK|LvMWR!%9u&+PPv^ucUktd*|scnUYY3#y6#2 zA@%a>dIkZR(*nPk=@)Q7RsGjsQ9wyU*cq#XSyOLoMTBk6g`&JBd~t%2-^mDRoUVw` zW1m`d=j$ch!eFv_`;4GyL1m^X@vgn z=bz**Z6%n4E(Yji@bR0tT=!M;Qx|0<9IDG-!wOe*w0W3snDsmE(uplYJ>F;*YnE%# zLt!cN*64i`7Ig>~ENQ5Gj++ufTM@J|wV8WU5;}!sduWlr{BD*QmWZt6465U16@4it zx0ggtzvo*oo<~S)WP9xrd)=Qj*K4-*R9)Qrm7TQJ-Cljsc_0c`Uum*GIoMA9ruPB* zf~m!M5JNpYUyjwVGdh^OhfdP<4=fHaaq0OGZQM{l;m2dk=+!}%QO4+O(XQ!u&pmBJ zH_gi6uzU(td@7`zC@%>27gnP;zglWN3t^_rFqq^MokT2-zv3M4&iIZEhkCql9o!ko z)PTz(AZe|nqVS`(!foW0d2~Ms&ISala-H!+@6GU89641n&M7FLzLVNR*;`Vpk|j>N zfFgkF(yYr7V}+=+c7M? zBywC6Y~a-Y#?o(saf*uZIxgTLpR1DqqP{Q4-sMv8)Pikz8Nl$%OHKWvWKW?TZGAY- z|95_J0uXSY^Yoj6@2+H=;eB80<1fnRk_7RJ(2>?kb}qe8KDDd&TBW11y2PV3WfrT8 zajr#13Q4Y%Ol%1?`JmftvK#K>PxD-=We=I1d0AkCRihJA5}*79g=<3I1vYnXiK^?r zQGyYE2>>oYvJ_Zv4)tKQQ=>|>4P0d8()3lhAt#xezKkLyHrMa|h6x1DPJ8}Tv=rNZGzM2 z;g;{1ob=LRH+tktOa@#dJD;}i32>l`VZ^WB$wz&~9vA|VlN7R0ieR#i-|R@jJ~fyCSN2>lwt9K37Mf zL?TWc<)wm6hJv!|nPNr#mgl>3k52z8*qS~hDz>bf}N;K-k zzTbGCrt@VP>Z#U@Jl5P~eye1UnV_BDkA8eIjp+bnsPO)-H5)9tF7g+9PS54%MK&!! zCyk&n%~$R5dy?MSA!C6pqzStpGnPog0IRC6~h?k3-mDqOf)T|Z_w>I zexMat3uDi|PooE?Q&m`gBt2ZH&Y4pC!yEAAYlbChFJrhMouMRK_xyBUY`#NI!(@iE zdmkduI#h=s)A?Reu74J0yutXv{<8(t??VMNtemy!{e zQe+Ufu^TuwCV9H~rQqH%-6KnN;7M~;;zk-C~9E0?qJY>CUd()bb z!_j)i%-W)BY7$Z>Kla+c!Z?pF@iCQbRH*~HbQuh!7EpFvGI8T&+jx@w=-IArBS;w5 z3f}8$tNQ}fFtf{#!HuZ9rDmd;hQBh|Zw~3h-C&Ks=%7jYNmSF<94yL)IrROlh3Te9 zscpD%&e3Na*6$1?rj%qK8vE8QB$Vd5|M)+A#Yj|I>@#R1sx^0NBe^~5tcCTTu}wc( zvPGF^l)-jxIg;lUfmEox0t@C%m`0Wxp9%I_ER#B_)UmJE3u7A`79bqE%D${(KOaX@ z40^4rZsR9W6iFD2cY%~6g=t0^$e>#i^?pwA3=Y4a*&dnz%k%T2!E_ZmCc-|urA3kW z5nmNbNxS_FhFiD(X&It=z}O&j$GQ zD6|-VyRv)`+|zFP10?O#y3Y9#ORhl$^{;4Uh|aqcx_t_G%!tV7QH~A9_6B~x_ZJMY zWyq&kWcK4r<8E_1O4U+2?e{qa_MT>YA2Qu)blh4`;HY4x%}+VdvGp+(FdJ*!A_&(f zK{c;(9oe16DOkw*uMWMA?W3ZdE0&v=Ma328$=Vwm4%TEisJyK6Sh)+187A%)&WRdW z6P`QIbA4;US;&o4MvU;%?-yK8(UV$aqFFu;8b)-)$GnTItV?&p`xiCk-=*S6`5f2A zk?^PGR7nko(W%<@vVFku+f%ucYx*(5kqexp2#LJd1uTcCxn<|M`H2#PlmWg90QxTp z{{X-4oUyOQ}wXx zebk4sallj0@%}{Evr>ixBQ?I|i~B44ion~H`7DPb4q@s;i74Q zA1~F%cJz>EOW}naHh(r8=UJYCx=@R8k|Y%NM-yfmVwZOQBoT?|_h35m;~0ErPxC-A*yX6c)KY>x7b@}Cc)|Ks;4O`H3$jZ5G3=B~pMD0Dxl?>2-ABd#Mr}cD zC*O2v)j8ta1LS<0(S;ZPc$wD4>n`Af^HPzPUlbjWUt$+X72%!j@N#NriulGo0N{SI zb?#yIXGi$FtM?ErAU;1{&QPYN(g^JxY^OmQ?4o^BIIgi@#>-_h%1};N-=NpDs2_|` zo-p7l;?0Px7#B0giv8+%pPAGS0G6wB^E(3 zje;_Q975&&K|62D!2XztmyB=-AVVibBI?^D)%$5a%pgK0@?^M@XTT`tnXW5*aClST zv}7+ny!6`MFbj}ap-GAXy9n2uruS{+rF3IKk0+jjoHIhG^6k^ptNm8ax_QWl6Rs0+f zMMLzsEJe97SW$nEdzi+k+p+VVXZX!hNx?OWl)jddJ|Jg`B*!z(lk$RuyM^H_EK(G9 zk8Mw*PXbrLGQ)T22V8 zEFSt3t9l~kl}9HH2RO&Uq4G8rDqn!94UxbpXs#YE(t!8gm8AdZFBEdwr7NVBfa1Y5 zNsk@rZnMGn{zkLJF&&+H!E`%lPnOI{S5+i~T+~beM9L0_;8LZQPW9kA7iv2X_B-Pd zzJi#0w@bwjTorvs>q&8bUBN@LFxZIT^TU<;?r#Q46V8xBDMIMZVT8;S|_4ya@dfN~> z?mHTg8a7?3k=Hi1^nEj@lOMSeOFSD0R%Q%SY^O!Ey3n{Mf9>T`Tid#` z)B2H!N?k4LJhGu+Q;JBs7IhR+52`tW4?3N$l`TLz^H;g?`y*LuDyK%)Jmw%p&*KbR znihv<@~dtcvb-MKeJ8HM-g$#U?7Xf+e19BXSDQNUU_dNS_J&J@of^c|fTp5mh;GJ= zGYX0+{655nV8uT$n4Pap=lhMGhSF@)V2c*bad#q%*3sSi&D2)7DKd z?~^-5CQbHOM#QGF#b#Zah8e$K3pgg_)drF8t6ki2k&D@%Ki!}VJ)+DkLfv2CEPC!Db zmnQ0UAL^HE(7ouN1Pzu=6LFrzw5Q8I^9p7)AI_!&*cvyhd8e)&C?`{U(nQ&5=hC}u zPQw0PG9NvbD0zmbqx=AAG{1PXRpiW8cXoleuDI}6K~BWie)oj0)}b+qPxw^F;w26K zyp298&7ar6cU68!fLuGyyx(OPIVsSNbc6r`XpHo9w()a1#Z z-P{UH1YD@FPfywE?L@)E07fLN)SQ`^)Jpfe2*SC_S;7_%7=5X)c=btXVt$aD{OEAy zuY!fTY?sEeCHvS&q9AZLbPHNp&n0da#+v~Yt8;~E{G7(#cGD7R1y(%o<5`@ciK+Ga zM+OniHFg*37t}~KAOq$<(AqAP_dlOb?u~FcbkftJ$4>PtU`j9Q5P8V-x?ynASYla+ z6O+XUo%15nKoQ+J+byM331Z`lV~oZbpcdOh2~XB=xKo^Bh#>xGsPO)BuqVYKI}V%@ zT`4BDtqNd#816)sg5cgMfQg1My*4|23jkK{lqH=Hd-Ryy|G<)wbneM)MWQz-FXy$q z`@u?FH5gMDIhW^h{+?2kkd&ae4bc8C31+0xD}JMpmeX`x)P&Oaup}EH+5VC^ffUW; zuFLg-E0sT)+RZmUaLLtox(u=f*~To`%RzLkJ-G+`Q5Z#PX~wwI`|{t!jdA@+USv>? z!H@KB4xsTn%dAY)i6r@Zp)3p`LL)m89e>kZ=Jq)HDUB^Lb8kFRKzmMfg2BjU?K$tm zDzR5{H-kOE$Z1D2);xZbC^AoIxT`Zpt*!vX_2p{kox+&j9z!l-x>)v zn>W5i#W5t4;gvXzKN?M@_Djpp)+BKXObG6!$;e|V?jgxi(#c(vbQl~F+_Hr(XSweY zLPFxcvD5B2larc=#cV&wAch)ZAo}wl2K|&Js!k|DF!kIE<^$rm_2@S<;gf$6=Rq-f z@5w?u-kq+}DI%jc`$_o@EfR>n%;;?>>iTlAsV`SGpXeF-q5ZfN=wuR#)~ zso&=uTAx@L?cCy!6pj&wd<><-z(j_fC%5A3@2Nv4SL>;JX>1w@Y=MsEc65lY@!mwV z5oJ3BkXJ^c8})m8^@GSJG#KWjF}OMjX!kJN$)CD%-UZ;0jPR9rHXmNx`Gs)_IXiWs z@zl7CoDnv{B}n7+?sAl4r=os>8d>~8A7b7ts%XfFOn+2oD+a69|z-qn8NwWWMFH>9ASfD)>#`@ zTMY>LyH6MMG#1}O_u#WGr1E$5LU)GVQi7HJ3$a_BRDTQP8h4ESBa^o8JLw5upLVcb z(>Hl&x}Qmi8D}{PlH<`bDfJ0e==5fNV7ecDU?%p&jlI>4_w3v2V5pARUvQrTfUdq2 z@2LYSq>PJb#`7xdQ#a>=F21UdAE|j!U452p7IL~R4;Z!d^#Og7B-No;QLsC^d16x* zYvc0u=v7>T)dgf%cqq#u#sn-^#eTQbx^OJkI~mb&h2ge}t8`VpbM>85qDY0Q$EDO* z6EVUBO|by@<|JB;kB>U`y4^PLcdL@&bxGa>#Ll=(ysp~gKq_k7+`3ZqKzt_uT*6Z*cT#JPlnS7)}{uFvfo8kWjxnX}z5H2DdiOlV&UPSLJHjH&ON^Ql(`e7RJhQ1GZmXU#ZOqJLscF zj`$0Pe4O9o)n0K{zjbFm=2$eS;kf9*#|mQ}KntZyjl-jeW2#rihljF-;6=p|x4kLl z2S$!EV*eV;imL}8af^yOeS!je)ANo!Pak17YF;%9_@~ege5%@CoRJZO?|<;r@ag3x zaQmYFXzD+6BgKtnDGOY=U&`CMG6-W;L(5iVfL2Zjq_^G9HNj7m@F$c63&=*)tS62j->fIJjwQ|k3IV2 zwLd@SzwDZ!e<1evLx}y?Be*}_z+an`KJOS^){f6c-xBo-N!k%=JrTRh%DPfZM>i7C z-YO@=&Cffu>+7NXh?Lm_Y^o6~s>-7dBAL&l%0(a`bhMNe#Xd<2{KDKEA4oj{>Y*F` zL7b`xBDz1p<0{J&HO>Uop5C&itU2h}wP*B2I_1KayQ4I!ea&{zF1v&Fw#QR)zI%i{ zjzf{;P{(+*iVoSDI_z$E{n@i2Sf*rV^^#FIv){?63{V*&6y?I)bYBm!<@4X8pwuNt zfg~zcI#|q`NjrHVFbudVz_tju%wQ{?GWb?0d3ba^afHZdR3Z}#P1OsAin@}1?aU>K zWE|%GTH2EzHrxX`c6vHzz4cz?z(zyQzv#FMG5Et3fuo!GqCDJR0?-hZ8xkbePB zC`+7#pmbqUyTDw5C)d5x>#&#c#1prrX^A74W(`y&oziL_cqbRuQ8(!ovEJppyS@|D z)#!1=f<+(oLPJu*a6INUj(spZ4d)>&G0AsP?c7$&&Wl5#$y(9C?2WYKRsL5j{2hJ{P+%6}`CIuOS(Z&X6_bEHmP(N3<_`*p( zzJ<%9%owFAZ@`Jp_C!;nNyMnMa;Vq-QZja=-6Z#yTaJp98>Pn1UE^UEtV(3!p$U7O z+M(JK;73Q5R`$ivbgzFfwx#Yc&cxFu-Ayyz{-gy&Z1qw>o&Xw;(xJ4(AP>1zViMqI z?kR+C{bM{Lq8Z?hF-OFX`I#=_{YTjx68Og>%FNGg|CiAJ;L;6tDCsc>{9k2$UEHzv zG+1h(naB57Z~lFtiu(>Heyy767NZ(*J7UH$1r@43MLn4!Zi#u@YHs!B(SKurry zI)e6`0t_a$7jDu&QQz&hsa!41*j1OuGCvNeh$O~V-`n0)_>UaW9az+husbHM0+-SU z5e^9oXcElm!swqfvN2x^;=jmL37G`2O#+VenJusT&|>^NwWx#7L - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/calendar.svg b/examples/yoti_example_flask_di/static/assets/icons/calendar.svg deleted file mode 100755 index 4f6b9bb7..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/calendar.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/chevron-down-grey.svg b/examples/yoti_example_flask_di/static/assets/icons/chevron-down-grey.svg deleted file mode 100644 index 6753becb..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/chevron-down-grey.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/document.svg b/examples/yoti_example_flask_di/static/assets/icons/document.svg deleted file mode 100755 index 4c41271e..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/document.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/email.svg b/examples/yoti_example_flask_di/static/assets/icons/email.svg deleted file mode 100755 index c4582d6e..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/email.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/gender.svg b/examples/yoti_example_flask_di/static/assets/icons/gender.svg deleted file mode 100755 index af5c5772..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/gender.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/nationality.svg b/examples/yoti_example_flask_di/static/assets/icons/nationality.svg deleted file mode 100755 index e57d7522..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/nationality.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/phone.svg b/examples/yoti_example_flask_di/static/assets/icons/phone.svg deleted file mode 100755 index b19cce04..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/phone.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/profile.svg b/examples/yoti_example_flask_di/static/assets/icons/profile.svg deleted file mode 100755 index 5c514fc1..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/profile.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/examples/yoti_example_flask_di/static/assets/icons/verified.svg b/examples/yoti_example_flask_di/static/assets/icons/verified.svg deleted file mode 100755 index 7ca4dbb3..00000000 --- a/examples/yoti_example_flask_di/static/assets/icons/verified.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/examples/yoti_example_flask_di/static/assets/logo.png b/examples/yoti_example_flask_di/static/assets/logo.png deleted file mode 100755 index c60227fabf339e9540e5daac4d2d25e121137752..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2988 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 - - - - - Dynamic Share example - - - - - -
-
-
- Yoti -
- -

We now accept Yoti

- -
-
-
- - - - -
- -
-

The Yoti app is free to download and use:

- -
- - Download on the App Store - - - - get it on Google Play - -
-
-
- - - - - diff --git a/examples/yoti_example_flask_di/templates/index.html b/examples/yoti_example_flask_di/templates/index.html deleted file mode 100644 index dd02e3bc..00000000 --- a/examples/yoti_example_flask_di/templates/index.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - Yoti client example - - - - - - -
-
-
- Yoti -
- -

We now accept Yoti

- -
-
-
- - - - -
- -
-

The Yoti app is free to download and use:

- -
- - Download on the App Store - - - - get it on Google Play - -
-
-
- - - - - - - diff --git a/examples/yoti_example_flask_di/templates/profile.html b/examples/yoti_example_flask_di/templates/profile.html deleted file mode 100644 index 5819341c..00000000 --- a/examples/yoti_example_flask_di/templates/profile.html +++ /dev/null @@ -1,160 +0,0 @@ -{% macro parse_document_images(prop) %} - {% for image in prop.value %} - - {% endfor %} -{% endmacro %} - -{% macro parse_structured_address(prop) %} - - {% for key in prop.value %} - - - - - {% endfor %} -
{{ key }}{{ prop.value[key] }}
-{% endmacro %} - -{% macro parse_document_details(prop) %} - - - - - {% if prop.value.expiration_date %} - - {% endif %} - {% if prop.value.issuing_authority %} - - {% endif %} -
Type{{ prop.value.document_type }}
Issuing Country{{ prop.value.issuing_country }}
Document Number{{ prop.value.document_number }}
Expiration Date{{ prop.value.expiration_date }}
Issuing Authority{{ prop.value.issuing_authority }}
-{% endmacro %} - -{% macro parse_age_verification(prop) %} - - - - - - - - - - - - - -
Check Type{{ prop.value.check_type }}
Age{{ prop.value.age }}
Result{{ prop.value.result }}
-{% endmacro %} - -{% macro attribute(name, icon, prop, prevalue="") %} - {% if prop %} - {% if prop.value %} -
-
-
- - {{ name }} -
-
- -
-
- {% if prop.name == "document_images" %} - {{ parse_document_images(prop) }} - {% elif prop.name == "structured_postal_address" %} - {{ parse_structured_address(prop) }} - {% elif prop.name == "document_details" %} - {{ parse_document_details(prop) }} - {% elif prop.name == "age_verified" %} - {{ parse_age_verification(prop) }} - {% else %} - {{ prevalue }} - {{ prop.value }} - {% endif %} -
-
-
-
S / V
-
Value
-
Sub type
- {% for source in prop.sources %} -
Source
-
{{ source.value }}
-
{{ source.subtype }}
- {% endfor %} - {% for verifier in prop.verifiers %} -
Verifier
-
{{ verifier.value }}
-
{{ verifier.subtype }}
- {% endfor %} -
-
- {% endif %} - {% endif %} -{% endmacro %} - - - - - - - - Yoti client example - - - - - -
-
-
- Powered by - Yoti -
- {% if selfie is not none %} -
- Yoti - -
- {% endif %} - -
- {{ full_name.value }} -
-
- -
- -
-
Attribute
-
Value
-
Anchors
-
- -
-
-
S / V
-
Value
-
Sub type
-
-
- -
- {% if given_names %}{{ attribute("Given names", "yoti-icon-profile", given_names) }}{% endif %} - {% if family_name %}{{ attribute("Family names", "yoti-icon-profile", family_name) }}{% endif %} - {% if phone_number %}{{ attribute("Mobile number", "yoti-icon-phone", phone_number) }}{% endif %} - {% if email_address %}{{ attribute("Email address", "yoti-icon-email", email_address) }}{% endif %} - {% if date_of_birth %}{{ attribute("Date of birth", "yoti-icon-calendar", date_of_birth) }}{% endif %} - {% if age_verified %}{{ attribute("Age verified", "yoti-icon-verified", age_verified, "Age Verification/") }}{% endif %} - {% if postal_address %}{{ attribute("Address", "yoti-icon-address", postal_address) }}{% endif %} - {% if structured_postal_address %}{{ attribute("Structured Address", "yoti-icon-address", structured_postal_address) }}{% endif %} - {% if gender %}{{ attribute("Gender", "yoti-icon-gender", gender) }}{% endif %} - {% if document_images %}{{ attribute("Document Images", "yoti-icon-profile", document_images) }}{% endif %} - {% if document_details %}{{ attribute("Document Details", "yoti-icon-profile", document_details) }} {% endif %} -
- -
-
- - -