Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Previous part: Main app startup code
The app URL path /api/v1/getcode for the API generates a JSON response that contains an alphanumerical code and a timestamp.
First, the @app.route decorator tells Flask that the get_code function handles requests to the /api/v1/getcode URL.
@app.route('/api/v1/getcode', methods=['GET'])
def get_code():
Next, the app calls the third-party API, the URL of which is in number_url, providing the access key that it retrieves from the key vault in the header.
headers = {
'Content-Type': 'application/json',
'x-functions-key': access_key
}
r = requests.get(url = number_url, headers = headers)
if (r.status_code != 200):
return "Could not get you a code.", r.status_code
The example third-party API is deployed to the serverless environment of Azure Functions. The x-functions-key property in the header is how Azure Functions expects an access key to appear in a header. For more information, see Azure Functions HTTP trigger - Authorization keys. If calling the API fails for any reason, the code returns an error message and the status code.
Assuming that the API call succeeds and returns a numerical value, the app then constructs a more complex code using that number plus some random characters (using its own random_char function).
data = r.json()
chars1 = random_char(3)
chars2 = random_char(3)
code_value = f"{chars1}-{data['value']}-{chars2}"
code = { "code": code_value, "timestamp" : str(datetime.utcnow()) }
The code variable here contains the full JSON response for the app's API, which includes the code value and a timestamp. An example response would be {"code":"ojE-161-pTv","timestamp":"2020-04-15 16:54:48.816549"}.
Before it returns that response, however, it writes a message in the storage queue using the Queue client's send_message method:
queue_client.send_message(code)
return jsonify(code)
Processing queue messages
Messages stored in the queue can be viewed and managed through the Azure portal, with the Azure CLI command az storage message get or with Azure Storage Explorer. The sample repository includes a script (test.cmd and test.sh) to request a code from the app endpoint and then check the message queue. There's also a script to clear the queue using the az storage message clear command.
Typically, an app like the one in this example would have another process that asynchronously pulls messages from the queue for further processing. As mentioned previously, the response generated by this API endpoint might be used elsewhere in the app with two-factor user authentication. In that case, the app should invalidate the code after a certain period of time, for example 10 minutes. A simple way to do this task would be to maintain a table of valid two-factor authentication codes, which are used by its user sign-in procedure. The app would then have a simple queue-watching process with the following logic (in pseudo-code):
pull a message from the queue and retrieve the code.
if (code is already in the table):
remove the code from the table, thereby invalidating it
else:
add the code to the table, making it valid
call queue_client.send_message(code, visibility_timeout=600)
This pseudo-code employs the send_message method's optional visibility_timeout parameter, which specifies the number of seconds before the message becomes visible in the queue. Because the default timeout is zero, messages initially written by the API endpoint become immediately visible to the queue-watching process. As a result, that process stores them in the valid code table right away. The process queues the same message again with the timeout, so that it receives the code again 10 minutes later, at which point it removes it from the table.
Implementing the main app API endpoint in Azure Functions
The code shown previously in this article uses the Flask web framework to create its API endpoint. Because Flask needs to run with a web server, such code must be deployed to Azure App Service or to a virtual machine.
An alternate deployment option is the serverless environment of Azure Functions. In this case, all the startup code and the API endpoint code would be contained within the same function that's bound to an HTTP trigger. As with App Service, you use function application settings to create environment variables for your code.
One piece of the implementation that becomes easier is authenticating with Queue Storage. Instead of obtaining a QueueClient object using the queue's URL and a credential object, you create a queue storage binding for the function. The binding handles all the authentication behind the scenes. With such a binding, your function is given a ready-to-use client object as a parameter. For more information and example code, see Connect Azure Functions to Azure Queue Storage.
Next steps
Through this tutorial, you learned how apps authenticate with other Azure services using managed identity, and how apps can use Azure Key Vault to store any other necessary secrets for third-party APIs.
The same pattern demonstrated here with Azure Key Vault and Azure Storage applies with all other Azure services. The crucial step is that you assign the correct role for the app within that service's page on the Azure portal, or through the Azure CLI. (See How to assign Azure roles). Be sure to check the service documentation to see whether you need to configure any other access policies.
Always remember that you need to assign the same roles and access policies to any service principal you're using for local development.
In short, having completed this walkthrough, you can apply your knowledge to any number of other Azure services and any number of other external services.
One subject that we haven't touched upon in this tutorial is authentication of users. To explore this area for web apps, begin with Authenticate and authorize users end-to-end in Azure App Service.