Identity for Flask¶
Prerequisite¶
Create a hello world web project in Flask.
Here we assume the project’s main file is named app.py.
Configuration¶
Install dependency by
pip install identity[flask]Add an
app_config.pyfile containing the following:# Tells the Flask-session extension to store sessions in the filesystem SESSION_TYPE = "filesystem" # In production, your setup may use multiple web servers behind a load balancer, # and the subsequent requests may not be routed to the same web server. # In that case, you may either use a centralized database-backed session store, # or configure your load balancer to route subsequent requests to the same web server # by using sticky sessions also known as affinity cookie. # [1] https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies/ # [2] https://azure.github.io/AppService/2016/05/16/Disable-Session-affinity-cookie-(ARR-cookie)-for-Azure-web-apps.html # [3] https://learn.microsoft.com/en-us/azure/app-service/configure-common?tabs=portal#configure-general-settings
Create an instance of the
identity.flask.Authobject, and assign it to a global variable inside yourapp.py:import os from flask import Flask from identity.flask import Auth import app_config app = Flask(__name__) app.config.from_object(app_config) auth = Auth( app, # Instruction for these settings is available in this project's README file. # https://github.com/rayluo/identity?tab=readme-ov-file#scenarios-supported os.getenv('CLIENT_ID'), client_credential=os.getenv('CLIENT_SECRET'), redirect_uri= # Recommended to register and use a redirect_uri. # It looks like http://localhost:5000/redirect for local development, # or https://your_website.com/redirect for your production. # If absent, Identity library will fall back to a Device Code mode. os.getenv('REDIRECT_URI'), ..., # See below on how to feed in the authority url parameter )
Tip
We recommend storing settings in environment variables. The snippet above read data from environment variables.
Initializing Auth object differently based on Identity Provider type
Its authority URL looks like
Initialize Auth() object like this
Microsoft Entra ID
https://login.microsoftonline.com/tenantAuth(…, authority=url, …)
Microsoft Entra External ID
https://contoso.ciamlogin.com/contoso.onmicrosoft.comMicrosoft Entra External ID with Custom Domain
https://contoso.com/tenantAuth(…, oidc_authority=url, …)
Azure AD B2C
N/A
Auth(…, b2c_tenant_name=”contoso”, b2c_signup_signin_user_flow=”susi”)
Sign In and Sign Out¶
In your web project’s
app.py, decorate some views with theidentity.flask.Auth.login_required()decorator. It will automatically trigger sign-in.@app.route("/") @auth.login_required def index(*, context): user = context['user'] ...
In your web project’s any template that you see fit, add this URL to present the logout link:
<a href="{{ url_for('identity.logout') }}">Logout</a>
Web app that logs in users and calls a web API on their behalf¶
Decorate your token-consuming views using the same
identity.flask.Auth.login_required()decorator, this time with a parameterscopes=["your_scope_1", "your_scope_2"].Then, inside your view, the token will be readily available via
context['access_token']. For example:@app.route("/call_api") @auth.login_required(scopes=["your_scope_1", "your_scope_2"]) def call_api(*, context): api_result = requests.get( # Use access token to call a web api "https://your_api.example.com", headers={'Authorization': 'Bearer ' + context['access_token']}, timeout=30, ) ...
All of the content above are demonstrated in this Flask web app sample.
API reference¶
- class identity.flask.Auth(app: Flask | None, *args, post_logout_view: callable | None = None, **kwargs)¶
A long-live identity auth helper for a Flask web project.
- __init__(app: Flask | None, *args, post_logout_view: callable | None = None, **kwargs)¶
Initialize the Auth class for a Flask web application.
- Parameters:
app¶ (Flask) –
It can be a Flask app instance, or
None.If your app object is globally available, you may pass it in here. Usage:
# In your app.py app = Flask(__name__) auth = Auth(app, authority=..., client_id=..., ...)
2. But if you are using Application Factory pattern, your app is not available globally, so you need to pass
Nonehere, and callAuth.init_app()later, inside or after your app factory function. Usage:# In your auth.py auth = Auth(app=None, authority=..., client_id=..., ...) # In your other blueprints or modules from auth import auth bp = Blueprint("my_blueprint", __name__) @bp.route("/") @auth.login_required def my_view(*, context): ... # In your app.py from auth import auth import my_blueprint def build_app(): app = Flask(__name__) auth.init_app(app) app.register_blueprint(my_blueprint.bp) return app app = build_app()
post_logout_view¶ (callable) – Optional. If not provided, the user will be redirected to the root URL of the app. If provided, it shall be the view (which is a function) that will be redirected to, after the user has logged out.
It also passes extra parameters to
identity.pallet.PalletAuth.
- get_edit_profile_url()¶
A helper to get the URL for Microsoft Entra B2C’s edit profile page.
You can pass this URL to your template and render it there.
- init_app(app)¶
Initialize the auth helper with your app instance.
- login_required(function=None, /, *, scopes: List[str] | None = None)¶
A decorator that ensures the user to be logged in, and optinoally also have consented to a list of scopes.
A user not meeting the requirement(s) will be brought to the login page. For already logged-in user, the view will be called with a keyword argument named “context” which is a dict containing the user object.
Usage:
@app.route("/") @auth.login_required def index(*, context): return render_template( 'index.html', user=context["user"], # User is guaranteed to be present # because we decorated this view with @login_required )
- Parameters:
scopes¶ (list[str]) –
A list of scopes that your app will need to use. When present, the context will also contain an “access_token”, “token_type”, and likely “expires_in” and “refresh_token”.
Usage:
@app.route("/call_api") @auth.login_required(scopes=["scope1", "scope2"]) def call_an_api(*, context): api_result = requests.get( # Use access token to call an api "https://example.com/endpoint", headers={'Authorization': 'Bearer ' + context['access_token']}, timeout=30, ) ...