Skip to main content

Sample Server

This page includes an example of how you can implement your SDK app's backend server in Python using Flask.

Important Security Note:

When deploying a backend server for your app that uses the Entrupy SDK, you should store user information in a secure format with passwords salted and hashed. This example uses a simple JSON file (USERS_FILE) to store usernames/passwords for demonstration purposes ONLY. You must implement proper, secure user authentication and storage for any production system. Do NOT store user passwords in plaintext!

Environment Variables:

To run this example, you will need to set the following environment variables:

  • ENTRUPY_PUBLIC_API_TOKEN: Provided by Entrupy when you sign up for SDK access.
  • USERS_FILE: The path to a JSON file describing the users who can log in. The format should be:
    {
    "username1": {
    "email": "username1@testemail.com",
    "password": "<your password here>" // REMINDER: DO NOT USE PLAINTEXT PASSWORDS!
    }
    }

Example Code (sample_partner_server_implementation.py):

from flask import Flask, jsonify, request
import requests
import os
import random
import string
import sys
import re
import json

ENTRUPY_PUBLIC_API_URL = 'https://api.entrupy.com'
ENTRUPY_PUBLIC_API_TOKEN = os.environ['ENTRUPY_PUBLIC_API_TOKEN']
USERS_FILE = os.environ['USERS_FILE']

application = Flask(__name__)

"""
When deploying a backend server for your app that uses the entrupy SDK, you should
store user information in a secure format with passwords salted and hashed. This
example uses a simple JSON file to store usernames / passwords to make the example
easy to configure and test but you must write your own code to implement logins
properly.

To run this example you will need to set the following environment variables:
- ENTRUPY_PUBLIC_API_TOKEN: Given to you by Entrupy when you sign up to use the SDK
- USERS_FILE: The path to a JSON file describing the users who can
login to this server and use the Entrupy SDK as part of your organization,
formatted as follows:
{
"username1": {
"email": "username1@testemail.com",
"password": <your password here>
}
}
Do NOT store user passwords in plaintext!
"""

with open(USERS_FILE, 'r') as f:
USERS = json.load(f)
TOKENS = {}
USER_TOKENS = {}


def generate_token():
# Creates a 32-char long random token including letters, numbers,
# and special characters.
readable_characters = re.sub('[O01Il]', '', string.ascii_lowercase+string.ascii_uppercase+string.digits+'%@#^&')
return ''.join(random.choice(readable_characters) for _ in range(32))


@application.route("/login", methods=['POST'])
def login_route():
"""
This is the route that your SDK app uses to login to your backend server
"""
print("enter: /login")
j = request.get_json() or {}
assert 'username' in j and 'password' in j, f"Invalid login {j}"
if j['username'] not in USERS or USERS[j['username']]['password'] != j['password']:
print('bad username/pass')
return {"authorized": False}, 401

# Generate a random token for the user to use with all responses
print('generating token')
while True:
token = generate_token()
# ensure we don't generate a token that already exists
if token not in TOKENS:
break

# only allow a single valid token for the user at a time -
# this step could be skipped depending on your security model
if j['username'] in USER_TOKENS:
del TOKENS[USER_TOKENS[j['username']]]

USER_TOKENS[j['username']] = token
TOKENS[token] = j['username']

print('logged in')
return {"token": token}, 200


@application.route("/authorize-entrupy-sdk-user", methods=['POST'])
def authorize_user_route():
"""
Your SDK app posts an authorization request to this route.
This route then verifies the app's token and proxies the authorization request to
Entrupy's backend to sign it.
(This can be done on your own servers with some additional configuration)
The signed authorization request is then returned to your app.

Your app can then use the signed authorization request to login to the
Entrupy app server (app-endpoint) so that it can begin using other SDK methods
to create authentications, view history, etc.
"""
print("enter: /authorize-entrupy-sdk-user")
auth_header = request.headers['Authorization']
token = auth_header[6:]
if not auth_header.startswith('Token ') or token not in TOKENS:
print("Bad auth header", auth_header)
return {"error": "unauthorized"}, 401

print("Valid auth header, token matches")

username = TOKENS[token]

j = request.get_json() or {}
sdk_authorization_request = j['sdk_authorization_request']

# Sign the authorization request from the app by contacting Entrupy's API
res = requests.post(f'{ENTRUPY_PUBLIC_API_URL}/v2/integrations/authorize-user',
headers={
'Authorization': f'Token {ENTRUPY_PUBLIC_API_TOKEN}',
'Api-Version': '2.0',
},
json={
'unique_user_id': username,
'sdk_authorization_request': sdk_authorization_request,
# email is used on certificates and for contacting customers
# you can also provide first_name/last_name if desired for
# certificates; emails are displayed as a fallback
'email': USERS[username]['email'],
},
timeout=10,
)

if res.status_code not in {200, 409}:
# 409 indicates you attempted to change user information,
# but it will return a valid signed authorization request.
print("Error from public API!", res, res.json())
return {"error": True}, res.status_code

res_d = res.json()

print(f"Successfully logged in for user {username} with Entrupy username {res_d['username']}")

return {"signed_authorization_request": res_d['signed_authorization_request']}, 200