Python (Flask)

Table of Contents:

Prerequisites
Flask SDK Installation
Run the Flask Demo App
Creating your own Flask App

Prerequisites

Creating a MIRACL Trust app involves two basic steps:

  1. Register/create an app in the portal and obtain credentials (this is detailed in the 'Register / Create a New App' menu section)
  2. Configure your app using an SDK, making use of these credentials

It is recommended that you first create a demo/test app with a redirect uri of http://127.0.0.1:5000/login in the portal, to enable you to run the SDK demo app and test the SDK functionality.

In the portal, when you create a new app, you will be issued with the Client ID and Client Secret credentials that you need to specify when building your app with the SDK. Note that your Client Secret will only be issued to you once so it must be grabbed when first displayed:

client secret

Client ID can then be copied at any time from the app settings screen:

app settings page

Note that you can control the Login Methods available (QR Code requires customer usage of the mobile app. While Browser Login enables logging in within the desktop browser, without the mobile app)

When creating your app in the portal, you also must specify a redirect_uri endpoint which comes from the url where you are hosting your app:

add app

The SDK will automatically create the redirect uri by appending '/login' to your url. You, of course, are in charge of what the base url of your app is. So it could be http://testapp.com/login, http://12.34.567.89:8080/login or http://127.0.0.1:5000/login as above. Note, however, that the MFA server will not accept 'localhost' as a base url.

Flask SDK Installation

  1. The SDK has certain system package dependencies which need to be installed. For Ubuntu 14.04:

    sudo apt-get install build-essential (for compiling code in dependencies)
    sudo apt-get install python-dev or sudo apt-get install python3-dev (depending on python version used)
    sudo apt-get install libssl-dev
    sudo apt-get install libffi-dev
    sudo apt-get install python-setuptools or sudo apt-get install python3-setuptools (depending on python version used)

    Flask is also required for the sample app.

  2. You can now download or clone the SDK from:

    https://github.com/miracl/maas-sdk-python

  3. Before installation edit the credentials of the sample app:

For security, the client ID and secret for a genuine app should not be stored in clear text in a config file. This has only been done here for simple demo purposes. For a production scenario, the client ID and secret should be programmatically accessed via an encrypted API

cd samples

Open miracl.json:

  {
  "client_id": "CLIENT_ID",
  "secret": "SECRET",
  "redirect_uri": "REDIRECT_URI"
  }

Substitute CLIENT_ID, CLIENT_SECRET with your correct values.

Note the fact that flask_sample.py contains the line app.run(port=5000) means that the app will be listening on http://127.0.0.1:5000 and means that the redirect_uri value of http://127.0.0.1:5000/login will function correctly. You can of course change this, but remember it must match what has been entered in your app details screen in the portal.

  1. Open the templates/index.html file and, towards the end of the file, make sure the mpad script has the correct url in order to communicate with the authentication server (note that it begins with 'mcl.cdn.mpin.io'):

    <script src="https://mcl.cdn.mpin.io/mpad/mpad.js" data-authurl="{{ auth_url }}" data-element="btmpin"></script>
  2. Install the SDK

    Return to the root folder:

    cd ..

    And run:

    sudo python setup.py install

    Note that this is a python 3 SDK. If using python 2.7 it may be necessary to comment out the from __future__ import unicode_literals line at the top of setup.py.

Run the Flask demo app

  1. Return to the sample directory:

    cd samples

    And run the server script:

    python flask_sample.py

    The app will now be running on http://127.0.0.1:5000 (or whichever port you have set!)

    image1

    From here you will be prompted to:

    1. Register
    2. Key in your identity
    3. Confirm your identity (by email activation)
    4. Create a 4-digit PIN:
    5. Login

    Once logged in you will be greeted by the refresh/logout page:

    image2

Creating your own Flask App

What follows is a detailed breakdown of the structure and code used in creating the sample app, which will give you a broad understanding of how to begin creating your own app. Note that the snippets of code which follow can be put to use, and you can also find the full files (complete with commented code) in the samples folder.

To start developing your own app, create a project folder within the maas-sdk-python root folder. The basic steps involved, as per the sample app, are:

  • Create a main web page with the login button (templates/index.html in the sample)
  • Create a credentials file (miracl.json in the sample)
  • Create a main server script to contain the code for dealing with the Miracl Client object and communicate with the platform (flask_sample.py in the sample)
  1. Create the main web page and the login button

Note that the use of col-md divs here assumes use of the bootstrap framework, as does the use of the {% %} tags to receive flash messages in the standard bootstrap categories of 'success', 'info' and 'danger'.

In the templates/index.html file, you will see that a javascript is used which can enable the prerollid functionality, which enables capturing the user's email address to bake it into the QR code. Ultimately, it will then be pre-populated in the user's phone app, saving them the trouble of entering their email address twice and enhancing the user experience:


<script type="application/javascript">
    $(document).ready(function(){
        $("input[type=email]").keyup(function(){
            var email = $("input[type=email]").val();
            $("#btmpin").attr("data-prerollid", email);
        });
    });
</script>

A macro login block is created for this which can then be used at any stage:


{% macro login_block() -%}
    <div class="col-md-12">
        <div class="col-md-8" id="example-email">
            <div class="form-group">
                <label for="example-email-input" class="col-xs-2 col-form-label">Log In As:</label>
                <div class="col-xs-10">
                    <input class="form-control" type="email" id="example-email-input" placeholder="email address">
                </div>
            </div>
        </div>
        <div id="btmpin"></div>
    </div>
{%- endmacro %}

Then listen for messages coming from your main script:


<div class="row">
    <div class="col-md-12">
        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                {% for category, message in messages %}
                    <div class="alert alert-{{ category }}">
                        {{ message }}
                    </div>
                {% endfor %}
            {% endif %}
        {% endwith %}
    </div>
</div>

Then an if retry check listens for the retry message coming from your main script. The retry message means that an attempted login has failed (as will be seen later) and your script will have provided an authorization url. In this case a login block is created:


{% if retry %}
    {% if auth_url %}
        {{ login_block() }}
    {% endif %}
{% endif %}

Then, within the if not retry section, the if is_authorized check is to listen for the is_authorized message, in which case display refresh or logout buttons which initiate a redirect to the "/refresh" and "/logout" endpoints:


{% if not retry %}
    <div class="row">
        {% if is_authorized %}
            <div class="col-md-4">
                <b>E-mail:</b> {{ email }}<br/>
                <b>User ID:</b> {{ user_id }}<br/>
            </div>
            <div class="col-md-4"></div>
            <div class="col-md-4">
                <a href="/refresh" class="btn btn-primary action">Refresh user data</a>
                <a href="/logout" class="btn btn-primary action">Log out</a>
            </div>
        {% else %}
            {% if auth_url %}
                {{ login_block() }}
            {% endif %}
        {% endif %}
    </div>
{% endif %}

The last else section of the above snippet then makes use of the macro login block explained above, which utilises the prerollid functionality and creates a btmpin div to contain the login button.

Finally, just before the closing </body> tag, include the script for the login button (note that it begins with 'mcl.cdn.mpin.io'):

<script src="https://mcl.cdn.mpin.io/mpad/mpad.js" data-authurl="{{ auth_url }}" data-element="btmpin"></script>

This makes use of the mpad.js library to send the authentication information to the server:

The parameters passed in this script are:

  • data-element: the login button ID (corresponds with <div id="btmpin">)
  • data-authurl: the authorization URL (this passes the client_id and redirect_uri to the authentication server). Each SDK has a 'Get Authorization Request URL' method for obtaining this.
  1. Create a Credentials file

For security, the client ID and secret for a genuine app should not be stored in clear text in a config file. This has only been done here for simple demo purposes. For a production scenario, the client ID and secret should be programmatically accessed via an encrypted API

In your project folder, create a json file (miracl.json in the sample) to store the credentials for your app:

{
"client_id": "CLIENT_ID",
"secret": "CLIENT_SECRET",
"redirect_uri": "REDIRECT_URI"
}

Substitute the correct values for your app, as discussed in 'Prerequisites' above.

  1. Create a main server script

In the sample, the main .py server script has the following main elements:

  1. Libraries to import

    from __future__ import unicode_literals
    from flask import Flask, redirect, session, request, render_template, flash
    from miracl_api import MiraclClient
    import logging
    import json

    The sample also then initialises logging and message handling:

    logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s: %(message)s')
  2. Read Credentials

    def read_configuration():
    """
    :return: Dictionary representing configuration file
    """
    config_file = open("miracl.json")
    config = json.load(config_file)
    config_file.close()
    return config

    These are then used to initialize a new Miracl CLient:

    app = Flask(__name__)
    
    miracl = MiraclClient(**read_configuration())
  3. Managing the Authorization Flow

    At the default "/" endpoint, an .is_authorized check is made to see if the user is already authenticated, in which case an access token is returned and the .get_email and .get_user_id methods are used to retrieve and display the user information. In the final else section, if the user is not authenticated, the .get_authorization_request_url method is used to send the authorization url to the template and enable creation of the login button:

    @app.route("/")
    def hello():
      # Check if user is authorized
      authorized = miracl.is_authorized(session)
    
      # Prepare model for template
      context = {
          'is_authorized': authorized
      }
    
      if authorized:
          # Fill model with user data
          context['email'] = miracl.get_email(session)
          context['user_id'] = miracl.get_user_id(session)
      else:
          # Fill model with auth url for login button
          context['auth_url'] = miracl.get_authorization_request_url(session)
      # Render and display template
      return render_template('index.html', **context)

    Important Note on User Management

    In terms of managing your users, it is important to note that, in the process of providing a secure login solution, the service has also registered your users with a confirmed user email and user ID. Once a user has been authenticated it is possible to make use of the above .get_email and .get_user_id methods to return string values.

    This removes a considerable amount of pain from the process of managing users and databases!

    For example, if you have a SQL user database and you want to make a check to see if a user is present, or needs added as a new user, then it is possible to make use of the above methods.

    Another example would be if you want to present a web form to capture more information that is needed to provide a user with access to your product features. Here you can use the above methods to prepopulate the ID and email address field, and you do not need to initiate a 'verify by email' process, as this has already been done by the service.


    At the callback route (the "/login" redirect uri), to complete the login process after the user has interacted with the login button, the user validation is made with the .validate_authorization method:

    @app.route("/login")
    def login():
      if miracl.validate_authorization(session,
                                       request.query_string.decode()) is not None:
          flash('Successfully logged in!', 'success')
          return redirect("/")
    
      flash('Login failed!', 'danger')
    
      return render_template('index.html', retry=True,
                             auth_url=miracl.get_authorization_request_url(
                                 session))  

    If the validation succeeds, a success message is sent to the template and a redirect back to the main "/" endpoint is made.

    Otherwise a danger message is sent along with retry=True and the auth_url, to enable the template to render the login button.

    Using the miracl.clearUserInfo() method, the refresh functionality is made available at the "/refresh" endpoint:

    @app.route("/refresh")
    def refresh():
      miracl.clear_user_info(session)
      return redirect("/")

    Note that refresh clears userInfo from the session, but leaves the access token unchanged. This means that userInfo can be requested without performing a fresh authorization.

    Using the miracl.clearUserInfo() method, the logout functionality is made available at the "/logout" endpoint:

    @app.route("/logout")
    def logout():
      miracl.clear_user_info(session, including_auth=True)
      flash('User logged out!', 'info')
      return redirect("/")

    Note that the true parameter means that the access token is also cleared, so a fresh authorization will be required.

    Before the end of the script the sample provides a secret key used to sign cookies for session storage.

    # Flask app configuration - secret key used to sign cookies for session storage
    app.secret_key = "ReplaceWithValidSecret"

    And in the final section, the listening port is set:

    # If this is startup file
    if __name__ == "__main__":
      # Set debug flag for Flask app
      app.debug = True
      # and launch it on specified port
      app.run(host='0.0.0.0',port=5000)  

Top