diff --git a/README.md b/README.md index 44f1ec85..fc7a206f 100644 --- a/README.md +++ b/README.md @@ -247,31 +247,12 @@ To run the Flask or Django container: ### Running Locally -1. Both example applications utilise the env variables described in [Configuration](#configuration), make sure they are accessible. -1. Ensure pip is up to date: `easy_install --upgrade pip` -1. Ensure setuptools and wheel are up to date: `python -m pip install --upgrade setuptools wheel` +#### Follow instructions in the README for each example: -#### Flask - -1. Change directories to the Flask project: `cd examples/yoti_example_flask` -1. Install dependencies: `pip install -r requirements.txt` -1. Run `python app.py` -1. Navigate to https://localhost:5000 - -#### Django - -1. You will need Python 3+ to run the Django example -1. Change directories to the Django project: `cd examples/yoti_example_django` -1. Install dependencies: `pip install -r requirements.txt` -1. Apply migrations before the first start by running: `python manage.py migrate` -1. Run: `python manage.py runsslserver 0.0.0.0:5000` -1. Navigate to https://localhost:5000 - -#### AML Example - -1. Change directories to the AML folder: `cd examples/aml` -1. Install requirements with `pip install -r requirements.txt` -1. Run: `python app.py` +* [Profile - Django](examples/yoti_example_django) +* [Profile - Flask](examples/yoti_example_flask) +* [AML](examples/aml) +* [Doc Scan](examples/doc_scan) ## Running the Tests diff --git a/examples/aml/README.md b/examples/aml/README.md new file mode 100644 index 00000000..b5bafaa3 --- /dev/null +++ b/examples/aml/README.md @@ -0,0 +1,5 @@ +### AML Example Project + +1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values +1. Install requirements with `pip install -r requirements.txt` +1. Run: `python app.py` \ No newline at end of file diff --git a/examples/doc_scan/.env.example b/examples/doc_scan/.env.example new file mode 100644 index 00000000..950e65a9 --- /dev/null +++ b/examples/doc_scan/.env.example @@ -0,0 +1,3 @@ +# Required Keys +YOTI_CLIENT_SDK_ID=yourClientSdkId +YOTI_KEY_FILE_PATH=yourKeyFilePath diff --git a/examples/doc_scan/.gitignore b/examples/doc_scan/.gitignore new file mode 100644 index 00000000..612424a3 --- /dev/null +++ b/examples/doc_scan/.gitignore @@ -0,0 +1 @@ +*.pem \ No newline at end of file diff --git a/examples/doc_scan/README.md b/examples/doc_scan/README.md new file mode 100644 index 00000000..9bb59371 --- /dev/null +++ b/examples/doc_scan/README.md @@ -0,0 +1,8 @@ +# Doc Scan Example + +## Running the example + +1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values +1. Install the dependencies with `pip install -r requirements.txt` +1. Start the server `flask run --cert=adhoc` +1. Visit `https://localhost:5000` \ No newline at end of file diff --git a/examples/doc_scan/__init__.py b/examples/doc_scan/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/doc_scan/app.py b/examples/doc_scan/app.py new file mode 100644 index 00000000..963d4140 --- /dev/null +++ b/examples/doc_scan/app.py @@ -0,0 +1,153 @@ +import base64 +from io import BytesIO + +import yoti_python_sdk +from filetype import filetype +from flask import Flask, Response, render_template, request, send_file, session +from yoti_python_sdk.doc_scan import ( + DocScanClient, + RequestedDocumentAuthenticityCheckBuilder, + RequestedFaceMatchCheckBuilder, + RequestedLivenessCheckBuilder, + RequestedTextExtractionTaskBuilder, + SdkConfigBuilder, + SessionSpecBuilder, +) +from yoti_python_sdk.doc_scan.exception import DocScanException + +from .settings import YOTI_APP_BASE_URL, YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH + +app = Flask(__name__) +app.secret_key = "someSecretKey" + + +def create_session(): + """ + Creates a Doc Scan session + + :return: the create session result + :rtype: CreateSessionResult + """ + doc_scan_client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) + + sdk_config = ( + SdkConfigBuilder() + .with_allows_camera_and_upload() + .with_primary_colour("#2d9fff") + .with_secondary_colour("#FFFFFF") + .with_font_colour("#FFFFFF") + .with_locale("en-GB") + .with_preset_issuing_country("GBR") + .with_success_url("{url}/success".format(url=YOTI_APP_BASE_URL)) + .with_error_url("{url}/error".format(url=YOTI_APP_BASE_URL)) + .build() + ) + + session_spec = ( + SessionSpecBuilder() + .with_client_session_token_ttl(600) + .with_resources_ttl(90000) + .with_user_tracking_id("some-user-tracking-id") + .with_requested_check(RequestedDocumentAuthenticityCheckBuilder().build()) + .with_requested_check( + RequestedLivenessCheckBuilder() + .for_zoom_liveness() + .with_max_retries(1) + .build() + ) + .with_requested_check( + RequestedFaceMatchCheckBuilder().with_manual_check_fallback().build() + ) + .with_requested_task( + RequestedTextExtractionTaskBuilder().with_manual_check_always().build() + ) + .with_sdk_config(sdk_config) + .build() + ) + + return doc_scan_client.create_session(session_spec) + + +@app.route("/") +def index(): + try: + result = create_session() + except DocScanException as e: + return render_template("error.html", error=e.text) + + session["doc_scan_session_id"] = result.session_id + + iframe_url = "{base_url}/web/index.html?sessionID={session_id}&sessionToken={session_token}".format( + base_url=yoti_python_sdk.YOTI_DOC_SCAN_API_URL, + session_id=result.session_id, + session_token=result.client_session_token, + ) + + return render_template("index.html", iframe_url=iframe_url) + + +@app.route("/success") +def success(): + doc_scan_client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) + + session_id = session.get("doc_scan_session_id", None) + + try: + session_result = doc_scan_client.get_session(session_id) + except DocScanException as e: + return render_template("error.html", error=e.text) + + return render_template("success.html", session_result=session_result) + + +@app.route("/error") +def error(): + error_message = "An unknown error occurred" + + if request.args.get("yotiErrorCode", None) is not None: + error_message = "Error Code: {}".format(request.args.get("yotiErrorCode")) + + return render_template("error.html", error=error_message) + + +@app.route("/media") +def media(): + media_id = request.args.get("mediaId", None) + if media_id is None: + return Response(status=404) + + doc_scan_client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) + + base64_req = request.args.get("base64", "0") + + session_id = session.get("doc_scan_session_id", None) + if session_id is None: + return Response("No session ID available", status=404) + + try: + retrieved_media = doc_scan_client.get_media_content(session_id, media_id) + except DocScanException as e: + return render_template("error.html", error=e.text) + + if base64_req == "1" and retrieved_media.mime_type == "application/octet-stream": + decoded = base64.b64decode(retrieved_media.content) + info = filetype.guess(decoded) + + buffer = BytesIO() + buffer.write(decoded) + buffer.seek(0) + + return send_file( + buffer, + attachment_filename="media." + info.extension, + mimetype=info.mime, + as_attachment=True, + ) + + return Response( + retrieved_media.content, content_type=retrieved_media.mime_type, status=200 + ) + + +if __name__ == "__main__": + app.run() diff --git a/examples/doc_scan/requirements.in b/examples/doc_scan/requirements.in new file mode 100644 index 00000000..d42ad858 --- /dev/null +++ b/examples/doc_scan/requirements.in @@ -0,0 +1,5 @@ +flask>=1.1.2 +python-dotenv>=0.13.0 +yoti>=2.11.2 +filetype>=1.0.7 +pyopenssl>=19.1.0 \ No newline at end of file diff --git a/examples/doc_scan/requirements.txt b/examples/doc_scan/requirements.txt new file mode 100644 index 00000000..052d6cf6 --- /dev/null +++ b/examples/doc_scan/requirements.txt @@ -0,0 +1,34 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile --output-file=requirements.txt requirements.in +# +asn1==2.2.0 # via yoti +certifi==2020.4.5.1 # via requests +cffi==1.14.0 # via cryptography +chardet==3.0.4 # via requests +click==7.1.2 # via flask +cryptography==2.9.2 # via pyopenssl, yoti +deprecated==1.2.6 # via yoti +filetype==1.0.7 # via -r requirements.in +flask==1.1.2 # via -r requirements.in +future==0.18.2 # via yoti +idna==2.9 # via requests +iso8601==0.1.12 # via yoti +itsdangerous==1.1.0 # via flask +jinja2==2.11.2 # via flask +markupsafe==1.1.1 # via jinja2 +protobuf==3.11.3 # via yoti +pycparser==2.20 # via cffi +pyopenssl==19.1.0 # via -r requirements.in, yoti +python-dotenv==0.13.0 # via -r requirements.in +requests==2.23.0 # via yoti +six==1.14.0 # via cryptography, protobuf, pyopenssl +urllib3==1.25.9 # via requests +werkzeug==1.0.1 # via flask +wrapt==1.12.1 # via deprecated +yoti==2.11.2 # via -r requirements.in + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/examples/doc_scan/settings.py b/examples/doc_scan/settings.py new file mode 100644 index 00000000..57f78723 --- /dev/null +++ b/examples/doc_scan/settings.py @@ -0,0 +1,15 @@ +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) + +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://localhost:5000") diff --git a/examples/doc_scan/static/images/favicon.png b/examples/doc_scan/static/images/favicon.png new file mode 100644 index 00000000..7dc99bcd Binary files /dev/null and b/examples/doc_scan/static/images/favicon.png differ diff --git a/examples/doc_scan/static/images/logo.svg b/examples/doc_scan/static/images/logo.svg new file mode 100644 index 00000000..425e32c5 --- /dev/null +++ b/examples/doc_scan/static/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/doc_scan/static/style.css b/examples/doc_scan/static/style.css new file mode 100644 index 00000000..8ae73f4b --- /dev/null +++ b/examples/doc_scan/static/style.css @@ -0,0 +1,8 @@ + +body { + padding-top: 4.5rem; +} + +table td:first-child { + width: 30%; +} \ No newline at end of file diff --git a/examples/doc_scan/templates/error.html b/examples/doc_scan/templates/error.html new file mode 100644 index 00000000..0070f8e7 --- /dev/null +++ b/examples/doc_scan/templates/error.html @@ -0,0 +1,9 @@ +{% include "layout/header.html" %} +
{{ error }}
+