Microsoft Azure Auth
Implementing Microsoft Azure Single Sign-On (SSO) Auth in Reflex app.
Tom Gotsman
·
Authentication is a critical requirement for most internal company web applications. While building features and functionality is exciting, ensuring secure access control often becomes a necessary next step. For organizations already using Microsoft's ecosystem, this blog post will guide you through setting up Reflex authentication (SSO) with Microsoft Azure.
Variables to Set
Before we begin, you will need the following imports and to set the following variables:
The Microsoft Authentication Library (MSAL) for Python library enables you to sign in users or apps with Microsoft identities.
The values you should get from your Azure portal / SSO team at your company are client_id
, client_secret
, and tenant_id
. These values are unique to your application and company.
It is recommended to retrive these values from environment variables or from a configuration file (they are just hardcoded for the example for simplicity).
Next we have to set the sso_app
variable, which is the client application that will be used to authenticate users.
Getting to the Azure Sign-In Page
Our State
class has three state variables _access_token
, _flow
, and _token
.
The _access_token
is the token that allows you to interact with Microsoft applications and access data from Microsoft that is relevant to you. The _flow
variable is used to initiate the authentication flow, and the _token variable stores the the decoded token and claims that is returned from Microsoft. All of these variables are backend variables and therefore the user cannot change these via the UI. /docs/vars/base-vars/
When we land on the home page, and we are not logged in, instantly the require_auth
event handler is called, which checks if the _token
variable is empty. As we are not logged in yet, the _token
variable is empty, and the redirect_sso
event handler is called.
This event handler initiates the authentication flow and redirects the user to the Microsoft sign-in page. It also sets the redirect_uri
to be the port serving the current page, using self.router.page.host
, plus /callback
. The redirect_uri
is where you want to re-direct microsoft to do the auth (i.e. the app page that takes the azure info and parses out who you are before setting your tokens) You can learn more about state.router
and how it works here.
Validate the Login
Once the user is finished signing in on the Microsoft sign-in page, they are redirected back to the redirect_uri
that we set earlier. This is the /callback
page.
This page has an on_load
event trigger that calls the callback
event handler when the page is loaded. The callback
event handler retrieves the query parameters from the URL and uses them to authenticate the user, by setting the _access_token
and the _token
.
Finally the user is redirected to the login_redirect
page, which in this example is the home page. You are now logged in.
Logging into the App
Now that we have all our tokens set correctly, when we redirect back to the home page, again the require_auth
event handler is called. This time, the _token
variable is not empty and so the page renders.
The page contains an rx.cond
component that checks if the user is authenticated. It runs the check_auth
computed var, which returns True
if the _token
variable is not empty and False
otherwise.
If the user is authenticated, the auth_view
component is rendered. If the user is not authenticated, the unauth_view
component is rendered. The auth_view
component displays a welcome message with the user's name from the token
computed var, which returns the _token
backend variable.
Logging Out
Lastly we have the logout functionality. When the user goes to the /logout
page the logout
event handler is called. This event handler clears the _token
variable and redirects the user to the Microsoft logout page. This event handler could also easliy be put on the on_click
event of a button.
Putting it all together
The full code is shown below and in this git repo. In this final code we have broken the app down into two different pages, with our state class in azure_auth/azure_auth/auth/core.py
and the pages in azure_auth/azure_auth/azure_auth.py
.
Overall the final workflow is as follows:
- The user lands on the home page (localhost:3000) while not logged in.
- This runs the
require_auth
event handler and then theredirect_sso
event handler. - This redirects the user to the Microsoft sign-in page.
- After the user completes the Microsoft sign-in, they are redirected back to the
/callback
page based on theredirect_uri
. - The
callback
event handler is called, which parses out info sent back from Microsoft and validates it and stores it in_token
and_access_token
. - The user is then redirected back to the home page based on the
login_redirect
. - The
require_auth
event handler is called again, but this time the user is authenticated and the page renders. - As the
check_auth
computed var inside of therx.cond
returnsTrue
, theauth_view
component is rendered, displaying the user's name. - On logout the
logout
event handler setsself._token
to an empty dictionary and redirects to Microsoft logout page which forces the everything about the login state to be invalid.