Flask Authentication By Example
Updated on August 30, 2023This Flask guide will help you learn how to secure a Flask web application using token-based authentication. You'll learn how to integrate Auth0 by Okta with Flask to implement the following security features:
- How to add user login, sign-up, and logout to Flask applications.
- How to get user profile information to personalize Flask views.
- How to protect Flask views from unauthorized access.
- How to make API calls from Flask to request data from a protected API.
This guide uses the Authlib library, which provides developers with a high-level API to handle OAuth and OpenID Connect servers from a low level specification implementation to integration with the major Python frameworks.
How Does Auth0 Work?
With the help of Auth0 by Okta, you don't need to be an expert on identity protocols, such as OAuth 2.0 or OpenID Connect, to understand how to secure your web application stack.
You first integrate your Flask application with Auth0. Your application will then redirect users to an Auth0 customizable login page when they need to log in. Once your users log in successfully, Auth0 redirects them back to your Flask app, returning JSON Web Tokens (JWTs) with their authentication and user information.
Get the Starter Application
We have created a starter project to help you learn Flask security concepts through hands-on practice. You can focus on building Flask views and services to secure your application.
Start by cloning the web-app_flask_python_hello-world
repository on its starter
branch:
Once you clone the repo, make web-app_flask_python_hello-world
your current directory:
cd web-app_flask_python_hello-world
Next, to work with the project we will create a virtual environment:
python3 -m venv venv
Activate the virtual environment:
source venv/bin/activate
Install the project dependencies as follows:
pip install -r requirements.txt
This starter Flask project offers a functional application with views and services to hydrate the user interface. It is important to notice that the service implemented in the starter project simulates the external API by placing the responses directly on the code. Later on the guide, you'll integrate this Flask application with a real API server using a backend technology of your choice.
Execute this command to run your Flask application on port 4040:
flask --app app run --port 4040
You'll see the following output on the command line:
* Serving Flask app 'app'* Debug mode: offWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.* Running on http://127.0.0.1:4040Press CTRL+C to quit
Now you should visit http://localhost:4040/
to access the running application.
Configure Flask with Auth0
Follow these steps to get started with the Auth0 Identity Platform quickly:
Sign up and create an Auth0 application
A free account also offers you:
- Auth0 Universal Login for Web, iOS & Android.
- Up to 2 social identity providers like Google, GitHub, and Twitter.
- Up to 3 Actions, Rules, & Hooks to customize and extend Auth0's capabilities.
During the sign-up process, you create something called an Auth0 Tenant, representing the product or service to which you are adding authentication.
Once you sign in, Auth0 takes you to the Dashboard. In the left sidebar menu, click on "Applications".
Then, click the "Create Application" button. A modal opens up with a form to provide a name for the application and choose its type. Use the following values:
Auth0 Flask Web Application
Click the "Create" button to complete the process. Your Auth0 application page loads up.
In the next step, you'll learn how to help Flask and Auth0 communicate.
What's the relationship between Auth0 Tenants and Auth0 Applications?
Let's say that you have a photo-sharing Flask app called "Flasktagram". You then would create an Auth0 tenant called flasktagram
. From a customer perspective, Flasktagram is that customer's product or service.
Now, say that Flasktagram is available on three platforms: web as a single-page application and Android and iOS as a native mobile application. If each platform needs authentication, you need to create three Auth0 applications to provide the product with everything it needs to authenticate users through that platform.
Flasktagram users belong to the Auth0 Flasktagram tenant, which shares them across its Auth0 applications.
Create a communication bridge between Flask and Auth0
When using the Auth0 Identity Platform, you don't have to build login forms. Auth0 offers a Universal Login Page to reduce the overhead of adding and managing authentication.
How does Universal Login work?
Your Flask application will redirect users to Auth0 whenever they trigger an authentication request. Auth0 will present them with a login page. Once they log in, Auth0 will redirect them back to your Flask application. For that redirection to happen securely, you must specify in your Auth0 Application Settings the URLs to which Auth0 can redirect users once it authenticates them.
As such, click on the "Settings" tab of your Auth0 Application page, locate the "Application URIs" section, and fill in the following values:
http://localhost:4040/callback
The above value is the URL that Auth0 can use to redirect your users after they successfully log in.
http://localhost:4040
The above value is the URL that Auth0 can use to redirect your users after they log out.
http://localhost:4040
Scroll down and click the "Save Changes" button.
Do not close this page yet. You'll need some of its information in the next section.
Add the Auth0 configuration variables to Flask
From the Auth0 Application Settings page, you need the Auth0 Domain and Client ID values to allow your Flask application to use the communication bridge you created.
What exactly is an Auth0 Domain, an Auth0 Client ID and a Auth0 Client Secret?
Domain
When you created a new Auth0 account, Auth0 asked you to pick a name for your tenant. This name, appended with auth0.com
, is your Auth0 Domain. It's the base URL that you will use to access the Auth0 APIs and the URL where you'll redirect users to log in.
Client ID
Each application is assigned a Client ID upon creation, which is an alphanumeric string, and it's the unique identifier for your application (such as q8fij2iug0CmgPLfTfG1tZGdTQyGaTUA
). You cannot modify the Client ID. You will use the Client ID to identify the Auth0 Application to which the Flask application needs to connect.
Warning: Another critical piece of information present in the "Settings" is the Client Secret. This secret protects your resources by only granting tokens to requestors if they're authorized. Think of it as your application's password, which must be kept confidential at all times. If anyone gains access to your Client Secret, they can impersonate your application and access protected resources.
Head back to your Auth0 application page and click on the "Settings" tab.
Locate the "Basic Information" section and follow these steps to get the Auth0 Domain, Auth0 Client ID, and Auth0 Client Secret values:
When you enter a value in the input fields present on this page, any code snippet that uses such value updates to reflect it. Using the input fields makes it easy to copy and paste code as you follow along.
As such, enter the "Domain" and "Client ID" values in the following fields to set up your Flask application in the next section:
These variables let your Flask application identify as an authorized party to interact with the Auth0 authentication server.
Now, create a new .config
file under the Flask project directory as follows:
[AUTH0]CLIENT_ID=AUTH0-CLIENT-IDCLIENT_SECRET=AUTH0-CLIENT-SECRETDOMAIN=AUTH0-DOMAIN[WEBAPP]SECRET_KEY=
Generate a string for SECRET_KEY
using openssl rand -hex 32
from your shell.
Head back to the "Settings" tab of your Auth0 application page in the Auth0 Dashboard to get the value for CLIENT_SECRET
.
Locate the "Client Secret" field, copy its value, and paste it as the CLIENT_SECRET
configuration value in the .config
file.
Set Up Auth0
To integrate your Flask application with Auth0 we'll use the Authlib library, which will handle all the OAuth and OpenID Connect for us.
Next, let's install and integrate the library into the application.
Install dependencies
The starter
branch already provides all the requirements you need to run this project, but make sure you have the following three libraries installed in your environment:
flask>=2.2.2authlib>=1.2.0requests>=2.28.2
You should now run pip install -r requirements.txt
from your shell to make these dependencies available to your project.
Initialize Auth0 configuration
So far our application registers one blueprint that registers the application views.
To set up Auth0 we'll register a new blueprint that will take care of all auth
views and logic.
Start by creating a new directory at the root of your project named auth
and add the following empty files inside of that directory:
__init__.pydecorators.pyviews.py
Now we can start by setting up authlib
and register a new Auth0 OAuth provider by adding the following code to the auth/views.py
file:
from flask import Blueprint, current_appfrom authlib.integrations.flask_client import OAuthfrom config import configauth_bp = Blueprint('auth', __name__)auth0_config = config['AUTH0']oauth = OAuth(current_app)domain = auth0_config["DOMAIN"]client_id = auth0_config["CLIENT_ID"]client_secret = auth0_config["CLIENT_SECRET"]oauth.register("auth0",client_id=client_id,client_secret=client_secret,client_kwargs={"scope": "openid profile email",},server_metadata_url=f'https://{domain}/.well-known/openid-configuration')
Once we provide authlib
with the Auth0 server details such as the client_id
, client_secrent
, scope
, and server_metadata_url
, the library is fully set up and we can start creating the views we need to handle the authentication requests with the Auth0 Authentication Server.
You'll have to register the newly created blueprint to host all the authentication views as well as setting the application key we previously configured:
import jsonfrom flask import Flask, render_templatefrom config import config # 👈 new codefrom webapp.views import webapp_bpfrom auth.views import auth_bp # 👈 new codedef to_pretty_json(obj: dict) -> str:return json.dumps(obj, default=lambda o: o.__dict__, indent=4)def page_not_found(e):return render_template('404.html'), 404def create_app():"""Configuration of the app"""app = Flask(__name__)app.secret_key = config["WEBAPP"]["SECRET_KEY"] # 👈 new codeapp.jinja_env.filters['to_pretty_json'] = to_pretty_jsonapp.register_error_handler(404, page_not_found)app.register_blueprint(auth_bp, url_prefix='/') # 👈 new codeapp.register_blueprint(webapp_bp, url_prefix='/')return appif __name__ == '__main__':app = create_app()app.run(host='0.0.0.0')
What about using scopes?
A property that you are configuring too for your OmniAuth-Auth0
strategy is the scope
property. Here you're providing the OpenID Connect Scopes: openid profile email
.
-
openid
: This scope informs the Auth0 Authorization Server that the Client is making an OpenID Connect (OIDC) request to verify the user's identity. OpenID Connect is an authentication protocol. -
profile
: This scope value requests access to the user's default profile information, such asname
,nickname
, andpicture
. -
email
: This scope value requests access to theemail
andemail_verified
information.
The details of the OpenID Connect Scopes go into the ID Token. However, you can define custom API scopes to implement access control. You'll identify those custom scopes in the calls that your client applications make to that API. Auth0 includes API scopes in the access token as the scope
claim value.
Create the Auth0 callback view
With the Auth0 configuration implemented you'll need to create a view to handle the authentication callback.
from flask import Blueprint, redirect, session, current_app # 👈 Updated codefrom authlib.integrations.flask_client import OAuthfrom config import configauth_bp = Blueprint('auth', __name__)auth0_config = config['AUTH0']oauth = OAuth(current_app)domain = auth0_config["DOMAIN"]client_id = auth0_config["CLIENT_ID"]client_secret = auth0_config["CLIENT_SECRET"]oauth.register("auth0",client_id=client_id,client_secret=client_secret,client_kwargs={"scope": "openid profile email",},server_metadata_url=f'https://{domain}/.well-known/openid-configuration')# 👇 new code@auth_bp.route("/callback", methods=["GET", "POST"])def callback():"""Callback redirect from Auth0"""token = oauth.auth0.authorize_access_token()session["user"] = token# The app assumes for a /profile path to be available, change here if it's notreturn redirect("/profile")# 👆 new code
After the user attempts to log in, either if it was successful or not, Auth0 will redirect them to the /callback
endpoint and the router will call the view that matches with it in the application.
In the callback
view we authenticate the user coming from the request
, store the user token in session
and redirect the user to the profile
endpoint.
In case there's an error, we'll respond with a 400
error status code.
Create the Login Button
You have created the callback
view to handle a login flow in your app. Now you need to create a Login Button that will trigger the authentication flow.
Let's create a new template that will handle the authentication buttons in the Nav Bar:
touch webapp/templates/navigation/desktop/nav_bar_buttons.html
Next, fill it up with the following content:
<div class="nav-bar__buttons"><a href="{{ url_for('auth.login') }}" class="button__login" >Log In</a></div>
We'll create an additional file for the mobile view:
touch webapp/templates/navigation/mobile/nav_bar_buttons.html
Next, fill it up with the following content:
<div class="mobile-nav-bar__buttons"><a href="{{ url_for('auth.login') }}" class="button__login" >Log In</a></div>
In both templates you are creating a button labeled "Log In". When the users click on this button they will make a GET request to /login
endpoint.
This endpoint is not defined yet, but you'll implement it in the next section.
But before we do that, let's add the new buttons to the nav bar.
For the Desktop template, make the following changes:
<div class="nav-bar__container"><nav class="nav-bar">{% include 'navigation/desktop/nav_bar_brand.html' %}{% include 'navigation/desktop/nav_bar_tabs.html' %}{% include 'navigation/desktop/nav_bar_buttons.html' %} <!-- 👈 new code --></nav></div>
And similarly for mobile:
<div class="mobile-nav-bar__container"><nav id="mobile-nav-bar" class="mobile-nav-bar">{% include 'navigation/mobile/mobile_nav_bar_brand.html' %}{% include 'navigation/mobile/menu_button.html' %}<div id="mobile-menu" class="mobile-nav-bar__menu mobile-nav-bar__menu--closed">{% include 'navigation/mobile/mobile_nav_bar_tabs.html' %} <!-- 👈 new code --></div></nav></div>
Register the login
view
For the user to be able to authenticate we need to define the login
view as follows:
from flask import Blueprint, redirect, session, url_for, current_app # 👈 Updated code# ... existing code# 👇 new code@auth_bp.route("/login")def login():"""Redirects the user to the Auth0 Universal Login (https://auth0.com/docs/authenticate/login/auth0-universal-login)"""return oauth.auth0.authorize_redirect(redirect_uri=url_for("auth.callback", _external=True))# 👆 new code# ... existing code
When the user now presses the "Log In" button from the UI, the login
view will redirect the user to the Auth0 Universal Login Page, where the user will be able to authenticate. After completion Auth0 will redirect the user to the callback
endpoint completing the authentication process.
Create the Sign-Up Button
Users are able to log in, but they also need to be able to Sign Up.
Let's open the webapp/templates/navigation/desktop/nav_bar_buttons.html
and add a new button:
<div class="nav-bar__buttons"><a href="{{ url_for('auth.signup') }}" class="button__sign-up">Sign Up</a> <!-- 👈 new code --><a href="{{ url_for('auth.login') }}" class="button__login" >Log In</a></div>
The same button needs to be added to the mobile view.
Register the signup
view
Similarly to when we register the login
view, we need to implement the signup
view. The process only differs in that we will pass an additional parameter to Auth0, hinting the service to start the process on the Sign Up page.
# ... existing code# 👇 new code@auth_bp.route("/signup")def signup():"""Redirects the user to the Auth0 Universal Login (https://auth0.com/docs/authenticate/login/auth0-universal-login)"""return oauth.auth0.authorize_redirect(redirect_uri=url_for("auth.callback", _external=True),screen_hint="signup")# 👆 new code# ... existing code
When the user presses the "Sign Up" button will be redirected to a sign-up page instead of a login page.
Create the Log Out Button
It's time to let our users log out and exit their sessions.
Let's open the webapp/templates/navigation/desktop/nav_bar_buttons.html
and add a new button:
<div class="nav-bar__buttons"><a href="{{ url_for('auth.logout') }}" class="button__logout">Log Out</a> <!-- 👈 new code --><a href="{{ url_for('auth.signup') }}" class="button__sign-up">Sign Up</a><a href="{{ url_for('auth.login') }}" class="button__login" >Log In</a></div>
The same button needs to be added to the mobile view.
Register the logout
view
Same as with the previous steps we need to create a view and assign the view to the URL mappings.
# ... existing code# 👇 new codefrom urllib.parse import quote_plus, urlencode# 👆 new code# ... existing code# 👇 new code@auth_bp.route("/logout")def logout():"""Logs the user out of the session and from the Auth0 tenant"""session.clear()return redirect("https://" + domain+ "/v2/logout?"+ urlencode({"returnTo": url_for("webapp.home", _external=True),"client_id": client_id,},quote_via=quote_plus,))# 👆 new code# ... existing code
The logout
view code is somewhat different from login
and signup
as it doesn't depend on authlib
, but rather it cleans the session to log out the user from the Flask application and it performs a redirect to Auth0 to log out the user from that side as well. If we don't perform the Auth0 log out, and the user presses the "Sign In" button again, it won't be necessary to provide any credentials, and the authentication flow will continue automatically with the Auth0 current authenticated user.
Render Content Based on Authentication
Because you are storing the user's ID Token in the session, you'll use this value to decide whether a user is authenticated or not.
Render authentication buttons conditionally
Let's update the desktop view to conditionally render the buttons depending on the authentication status.
<div class="nav-bar__buttons">{% if session.user %} <!-- 👈 new code --><a href="{{ url_for('auth.logout') }}" class="button__logout">Log Out</a>{% else %} <!-- 👈 new code --><a href="{{ url_for('auth.signup') }}" class="button__sign-up">Sign Up</a><a href="{{ url_for('auth.login') }}" class="button__login" >Log In</a>{% endif %} <!-- 👈 new code --></div>
You should apply these changes to the mobile view as well.
Render navigation tabs conditionally
Similarly, only authenticated users are allowed to access the Protected
and Admin
views, so we hide the tabs from the nav bar appropriately.
{% macro tab(label='', path='') %}<ahref="{{ path }}"class="nav-bar__tab {% if request.path == path %}nav-bar__tab--active{% endif %}">{{ label }}</a>{% endmacro %}<div class="nav-bar__tabs">{{ tab(label="Profile", path=url_for('webapp.profile')) }}{{ tab(label="Public", path=url_for('webapp.public')) }}{% if session.user %} <!-- 👈 new code -->{{ tab(label="Protected", path=url_for('webapp.protected')) }}{{ tab(label="Admin", path=url_for('webapp.admin')) }}{% endif %} <!-- 👈 new code --></div>
You should also apply these changes to the mobile view.
Secure your Flask views
Now that the navigation tabs will render conditionally, you'll need to make sure that only authenticated users have access to certain views.
Secure the profile, protected and admin views
To protect our endpoints, we'll create a new decorator that validates the user session, and if no session is found it will redirect the users automatically to the login
view. If, on the contrary, there's an existing session we'll continue with the default method.
Head to the auth/decorators.py
file and add the following code:
from flask import redirect, session, url_forfrom functools import wrapsclass AuthError(Exception):def __init__(self, error, status_code):self.error = errorself.status_code = status_codedef requires_auth(f):"""Use on routes that require a valid session, otherwise it aborts with a 403"""@wraps(f)def decorated(*args, **kwargs):if session.get('user') is None:return redirect(url_for('auth.login'))return f(*args, **kwargs)return decorated
Now, adding one line of code is all that is needed now to require a user to be authenticated to access a view.
from auth.decorators import requires_auth # 👈 new code# ... existing code@webapp_bp.route("/profile")@requires_auth # 👈 new codedef profile(request):template = loader.get_template('profile/index.html')# ... existing code@webapp_bp.route("/protected")@requires_auth # 👈 new codedef protected(request):template = loader.get_template('protected/index.html')# ... existing code@webapp_bp.route("/admin")@requires_auth # 👈 new codedef admin(request):template = loader.get_template('admin/index.html')
Retrieve User Profile Information
After a user successfully logs in, Auth0 sends an ID token to your Flask web application. Authentication systems, such as Auth0, use ID Tokens in token-based authentication to cache user profile information and provide it to a client application. The caching of ID tokens can improve the performance and responsiveness of your Flask application.
Because during the authentication routine we are storing the tokens and information processed by authlib
, we have direct access to the user information from Auth0 in session
.
Let's process that information in a view to build the template that shows the user information on the screen.
# ... existing code@webapp_bp.route("/profile")@requires_authdef profile():"""Unprotected endpoint which displays your profile if you are logged in, otherwise it prompts the user to log in"""# 👇 updated codereturn render_template('profile.html', user_profile=session.get('user').get("userinfo"))# 👆 updated code# ... existing code
Authentication Beyond Passwords: Try Passkeys Today
So far, you have seen how a user can sign up or log in to your application with a username and password. However, you can free your users from having to remember yet another password by allowing them to use passkeys as a new way to log in.
Passkeys are a phishing-resistant alternative to traditional authentication factors, such as the username/password combo, that offer an easier and more secure login experience to users.
You don't have to write any new code to start using passkeys in your application. You can follow the "Authentication with Passkeys" lab to learn how to enable passkeys in your Auth0 tenant and learn more about this emerging technology. Once you complete that optional lab, you can come back to this guide to continue learning about how to access protected API resources on behalf of a user from your application.
Integrate Your Flask Application with a Protected API Server
This section focuses on showing you how to get an access token in your Flask Web App and how to use it to make API calls to protected API endpoints.
When you use Auth0, you delegate the authentication process to a centralized service. Auth0 provides you with functionality to log in and log out users from your Flask application. However, your application may need to access protected resources from an API.
You can also protect an API with Auth0. There are multiple API quickstarts to help you integrate Auth0 with your backend platform.
When you use Auth0 to protect your API, you also delegate the authorization process to a centralized service that ensures only approved client applications can access protected resources on behalf of a user.
Retrieve an Access Token from Auth0
Thanks to authlib
there's not a lot we need to do as we are already storing the information processed by the library. That information not only contains user information, the ID Token, but also the access token needed by the application.
To retrieve the access token we are storing in session you can simply do:
access_token = request.session.get('user').get('access_token')
How can you make secure API calls from a Flask web app?
Instead of creating an API from scratch to test the authentication and authorization flow between the client and the server, you can pair this client application with an API server that matches the technology stack you use at work. The Flask "Hello World" client application that you have been building up can interact with any of the "Hello World" API server samples from the Auth0 Developer Center.
Pick an API code sample in your preferred backend framework and language from the list below and follow the instructions on the code sample page to set it up. Once you complete the sample API server setup, please return to this page to learn how to integrate that API server with your Flask application.
Call a Protected API from your Flask Web App
Add external API config variables
Once you have set up the API server code sample, you should have created an Auth0 Audience value. Store that value in the following field so that you can use it throughout the instructions presented on this page easily:
Now, open your .env
file and add the following variables to it:
[AUTH0]CLIENT_ID=AUTH0-CLIENT-IDCLIENT_SECRET=AUTH0-CLIENT-SECRETDOMAIN=AUTH0-DOMAINAUDIENCE=AUTH0-AUDIENCE[WEBAPP]SECRET_KEY=API_SERVER_URL=http://localhost:6060
You are using AUTH0_AUDIENCE
to add the value of your Auth0 API Audience so that your Flask web client application can request resources from the API that such audience value represents.
Let's understand better what the AUTH0_AUDIENCE
and API_SERVER_URL
values represent.
The API_SERVER_URL
is the URL where your sample API server listens for requests. In production, you'll change this value to the URL of your live server.
Your Flask application must pass an Access Token when it calls a target API to access protected resources. You can request an access token in a format that the API can verify by passing the audience
during the authentication request in your login
view.
The value of the Auth0 Audience must be the same for both the Flask web client application and the API server you decided to set up.
Why is the Auth0 Audience value the same for both apps? Auth0 uses the value of the audience
proprierty to determine which resource server (API) the user is authorizing your Flask application to access. It's like a phone number. You want to ensure that your Flask application "texts the right API".
Next, as already mentioned, we need to pass the audience
value during the login
and signup
requests.
# ... existing code@auth_bp.route("/login")def login():"""Redirects the user to the Auth0 Universal Login (https://auth0.com/docs/authenticate/login/auth0-universal-login)"""return oauth.auth0.authorize_redirect(redirect_uri=url_for("auth.callback", _external=True),audience=auth0_config["AUDIENCE"] # 👈 new code)@auth_bp.route("/signup")def signup():"""Redirects the user to the Auth0 Universal Login (https://auth0.com/docs/authenticate/login/auth0-universal-login)"""return oauth.auth0.authorize_redirect(redirect_uri=url_for("auth.callback", _external=True),screen_hint="signup",audience=auth0_config["AUDIENCE"] # 👈 new code)# ... existing code
Update the message service to call an external API
Until now, the message service would have "hardcoded" message values for each of the three type of messages (public, protected, and admin).
Now you'll update the service to communicate with an external API.
The changes include removing the text messages for API calls, and the provision of an access token during those calls.
Completely replace the message_service.py
file with the new content below:
import requestsfrom config import configdef make_request(path, access_token=None):url = '{}{}'.format(config["WEBAPP"]["API_SERVER_URL"], path)if access_token is None:headers = {}else:headers = {'authorization': 'Bearer {}'.format(access_token)}r = requests.get(url, headers=headers)return r.json()class MessageService:def public_message(self):return make_request('/api/messages/public')def protected_message(self, access_token: str):return make_request('/api/messages/protected', access_token)def admin_message(self, access_token: str):return make_request('/api/messages/admin', access_token)
The library requests
handles most of the load in the code above, we simply specify the URLs we want to target and wether we have or not an access token for the call.
Note that the public
endpoint does not require an access token to function, and thus it should not be a parameter in the function.
Since your changes now affected the signatures of the functions protected_message
and admin_message
to expect a parameter access_token
we need to provide it during their usage.
Head on to views.py
to fix those calls.
# ...existing code@webapp_bp.route("/protected")@requires_authdef protected():"""Unprotected endpoint which displays your profile if you are logged in, otherwise it prompts the user to log in"""access_token = session.get('user').get('access_token') # 👈 new codereturn render_template('protected.html', message=MessageService().protected_message(access_token)) # 👈 updated code@webapp_bp.route("/admin")@requires_authdef admin():"""Unprotected endpoint which displays your profile if you are logged in, otherwise it prompts the user to log in"""access_token = session.get('user').get('access_token') # 👈 new codereturn render_template('admin.html', message=MessageService().admin_message(access_token)) # 👈 updated code# ...existing code
You are getting the Access Token from the session
, which you previously stored after the user authenticated in the callback
method. Note you can safely use session.get('user')
here because of the @requires_auth
decorator, which guarantees the user has authenticated before accessing this view. Otherwise, the MessageService
will pass a None
Access Token to the API
and you'll get a { message: Permission Denied }
error from the External API.
Conclusion
You have implemented user authentication in Flask to identify your users, get user profile information, and control the content that your users can access by protecting views and API resources.
This guide covered the most common authentication use case for a Flask Web application: simple login and logout. However, Auth0 is an extensible and flexible identity platform that can help you achieve even more. If you have a more complex use case, check out the Auth0 Architecture Scenarios to learn more about the typical architecture scenarios we have identified when working with customers on implementing Auth0.
We'll cover advanced authentication patterns and tooling in future guides, such as using a pop-up instead of redirecting users to log in, adding permissions to ID tokens, using metadata to enhance user profiles, and much more.