flask logo
python logo

Flask Authentication By Example

Updated on August 30, 2023
Photo of Juan Cruz Martinez
Juan Cruz MartinezStaff Developer Advocate

This 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:

COMMAND
git clone -b starter [email protected]:auth0-developer-hub/web-app_flask_python_hello-world.git

Once you clone the repo, make web-app_flask_python_hello-world your current directory:

COMMAND
cd web-app_flask_python_hello-world

Next, to work with the project we will create a virtual environment:

COMMAND
python3 -m venv venv

Activate the virtual environment:

MACOS
WINDOWS
LINUX
source venv/bin/activate

Install the project dependencies as follows:

COMMAND
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:

COMMAND
flask --app app run --port 4040

You'll see the following output on the command line:

* Serving Flask app 'app'
* Debug mode: off
WARNING: 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:4040
Press 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:

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:

Name
Auth0 Flask Web Application
Application Type
Regular Web Applications
Regular Web Applications

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:

Allowed Callback URLs
http://localhost:4040/callback

The above value is the URL that Auth0 can use to redirect your users after they successfully log in.

Allowed Logout URLs
http://localhost:4040

The above value is the URL that Auth0 can use to redirect your users after they log out.

Allowed Web Origins
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.

You can also use custom domains to allow Auth0 to do the authentication heavy lifting for you without compromising your branding experience.

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:

Auth0 application settings to enable user authentication

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:

For security, these configuration values are stored in memory and only used locally. They are gone as soon as you refresh the page! As an extra precaution, you should use values from an Auth0 test application instead of a production one.

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:

.config
[AUTH0]
CLIENT_ID=AUTH0-CLIENT-ID
CLIENT_SECRET=AUTH0-CLIENT-SECRET
DOMAIN=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.

This page does not store the value of the Auth0 Client Secret. Not even in memory. 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. The Auth0 Client Secret protects your resources by only granting authentication-related credentials in the form of tokens to requestors if they're authorized.

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:

requirements.txt
flask>=2.2.2
authlib>=1.2.0
requests>=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__.py
decorators.py
views.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:

auth/views.py
from flask import Blueprint, current_app
from authlib.integrations.flask_client import OAuth
from config import config
auth_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:

app.py
import json
from flask import Flask, render_template
from config import config # 👈 new code
from webapp.views import webapp_bp
from auth.views import auth_bp # 👈 new code
def 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'), 404
def create_app():
"""
Configuration of the app
"""
app = Flask(__name__)
app.secret_key = config["WEBAPP"]["SECRET_KEY"] # 👈 new code
app.jinja_env.filters['to_pretty_json'] = to_pretty_json
app.register_error_handler(404, page_not_found)
app.register_blueprint(auth_bp, url_prefix='/') # 👈 new code
app.register_blueprint(webapp_bp, url_prefix='/')
return app
if __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 as name, nickname, and picture.

  • email: This scope value requests access to the email and email_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.

The concepts about API scopes or permissions are better covered in an Auth0 API tutorial such as "Use TypeScript to Create a Secure API with Node.js and Express: Role-Based Access Control".

Create the Auth0 callback view

With the Auth0 configuration implemented you'll need to create a view to handle the authentication callback.

auth/views.py
from flask import Blueprint, redirect, session, current_app # 👈 Updated code
from authlib.integrations.flask_client import OAuth
from config import config
auth_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 not
return 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:

COMMAND
touch webapp/templates/navigation/desktop/nav_bar_buttons.html

Next, fill it up with the following content:

webapp/templates/navigation/desktop/nav_bar_buttons.html
<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:

COMMAND
touch webapp/templates/navigation/mobile/nav_bar_buttons.html

Next, fill it up with the following content:

webapp/templates/navigation/mobile/nav_bar_buttons.html
<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:

webapp/templates/navigation/desktop/nav_bar.html
<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:

webapp/templates/navigation/mobile/mobile_nav_bar.html
<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:

auth/views.py
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.

Universal Login Page

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:

webapp/templates/navigation/desktop/nav_bar_buttons.html
<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.

webapp/views.py
# ... 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:

webapp/templates/navigation/desktop/nav_bar_buttons.html
<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.

webapp/views.py
# ... existing code
# 👇 new code
from 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.

webapp/templates/navigation/desktop/nav_bar_buttons.html
<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.

webapp/templates/navigation/desktop/nav_bar_tabs.html
{% macro tab(label='', path='') %}
<a
href="{{ 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:

auth/decorators.py
from flask import redirect, session, url_for
from functools import wraps
class AuthError(Exception):
def __init__(self, error, status_code):
self.error = error
self.status_code = status_code
def 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.

webapp/views.py
from auth.decorators import requires_auth # 👈 new code
# ... existing code
@webapp_bp.route("/profile")
@requires_auth # 👈 new code
def profile(request):
template = loader.get_template('profile/index.html')
# ... existing code
@webapp_bp.route("/protected")
@requires_auth # 👈 new code
def protected(request):
template = loader.get_template('protected/index.html')
# ... existing code
@webapp_bp.route("/admin")
@requires_auth # 👈 new code
def 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.

webapp/views.py
# ... existing code
@webapp_bp.route("/profile")
@requires_auth
def profile():
"""
Unprotected endpoint which displays your profile if you are logged in, otherwise it prompts the user to log in
"""
# 👇 updated code
return 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.

A form modal giving you information on how a passkey works and the option to create a passkey

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.

actix-web
rust
Actix Web/Rust API:Authorization Code Sample
Code sample of a simple Actix Web server that implements token-based authorization using Auth0.
aspnet-core
csharp
ASP.NET Core Code Sample:Web API Authorization
Code sample of a simple ASP.NET Core server that implements token-based authorization using Auth0.
aspnet-core
csharp
ASP.NET Core v5 Code Sample:Web API Authorization
Code sample of a simple ASP.NET Core v5.0 server that implements token-based authorization using Auth0.
django
python
Django/Python API:Authorization Code Sample
Code sample of a simple Django server that implements token-based authorization using Auth0.
express
javascript
Express.js Code Sample:Basic API Authorization
Code sample of a simple Express.js server that implements token-based authorization using Auth0.
express
typescript
Express.js/TypeScript Code Sample:Basic API Authorization
Code sample of a simple Express.js server built with TypeScript that implements token-based authorization using Auth0.
fastapi
python
FastAPI/Python Code Sample:Basic API Authorization
Code sample of a simple FastAPI server that implements token-based authorization using Auth0.
flask
python
Flask/Python API:Authorization Code Sample
Code sample of a simple Flask server that implements token-based authorization using Auth0.
laravel
php
Laravel/PHP Code Sample:Basic API Authorization with Auth0 Laravel SDK
Code sample of a simple Laravel server that implements token-based authorization using the Auth0 Laravel SDK.
laravel
php
Laravel/PHP Code Sample:Basic API Authorization with Auth0 PHP SDK
Code sample of a simple Laravel server that implements token-based authorization using the Auth0 PHP SDK.
lumen
php
Lumen Code Sample:Basic API Authorization
Code sample of a simple Lumen server that implements token-based authorization using Auth0.
nestjs
typescript
NestJS Code Sample:Basic API Authorization
Code sample of a simple NestJS server that implements token-based authorization using Auth0.
phoenix
elixir
Phoenix/Elixir API:Authorization Code Sample
Code sample of a simple Phoenix server that implements token-based authorization using Auth0.
rails
ruby
Ruby on Rails API:Authorization Code Sample
Code sample of a simple Rails server that implements authorization using Auth0.
spring
java
Spring Code Sample:Basic API Authorization
Java code sample that implements token-based authorization in a Spring Web API server to protect API endpoints, using Spring Security and the Okta Spring Boot Starter.
spring
java
Spring Functional Code Sample:Basic API Authorization
Java code sample that implements token-based authorization in a Spring Web API server to protect API endpoints, following a functional approach.
spring-webflux
java
Spring WebFlux Code Sample:Basic API Authorization
Java code sample that implements token-based authorization in a Spring WebFlux API server to protect API endpoints, using Spring Security and the Okta Spring Boot Starter.
standard-library
golang
Golang Code Sample:Basic API Authorization
Code sample of a simple Golang server that implements token-based authorization using Auth0.
symfony
php
Symfony Code Sample:Basic API Authorization
Code sample of a simple Symfony server that implements token-based authorization using Auth0.

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:

.config
[AUTH0]
CLIENT_ID=AUTH0-CLIENT-ID
CLIENT_SECRET=AUTH0-CLIENT-SECRET
DOMAIN=AUTH0-DOMAIN
AUDIENCE=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.

auth/views.py
# ... 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:

services/message_service
import requests
from config import config
def 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.

webapp/views.py
# ...existing code
@webapp_bp.route("/protected")
@requires_auth
def 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 code
return render_template('protected.html', message=MessageService().protected_message(access_token)) # 👈 updated code
@webapp_bp.route("/admin")
@requires_auth
def 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 code
return 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.