title | description | ms.date | ms.topic | ms.custom |
---|---|---|---|---|
Walkthrough, Part 7: Authenticate Python apps with Azure services |
An examination of the code for the main app API endpoint, which uses the third-party API endpoint and writes a message to Azure Queue Storage. |
02/20/2024 |
conceptual |
devx-track-python |
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.
:::code language="python" source="~/../python-integrated-authentication/main_app/app.py" range="44-45":::
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.
:::code language="python" source="~/../python-integrated-authentication/main_app/app.py" range="46-54":::
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).
:::code language="python" source="~/../python-integrated-authentication/main_app/app.py" range="56-60":::
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:
:::code language="python" source="~/../python-integrated-authentication/main_app/app.py" range="64-66":::
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 will receive the code again 10 minutes later, at which point it removes it from the table.
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.
Through this tutorial, you've 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.