Python >> Tutoriel Python >  >> Python

Comment ajouter une authentification utilisateur aux applications Flask avec Okta

L'authentification des utilisateurs est une fonctionnalité de base des applications Web permettant aux utilisateurs de créer et d'accéder à leurs propres comptes. Malheureusement, l'authentification n'est pas toujours facile à configurer et il existe de nombreuses façons d'implémenter de manière incorrecte les fonctionnalités de connexion et de déconnexion.

Ce didacticiel explique comment utiliser le service d'authentification d'identité sécurisé appelé Okta, qui est gratuit pour un maximum de 1 000 comptes d'utilisateurs actifs, afin de gérer facilement les données des utilisateurs dans les applications Flask.

Nos outils

Python 3 est fortement recommandé pour la création d'applications et ce didacticiel a été construit avec Python 3.7, bien que les versions antérieures de Python 3 devraient également fonctionner correctement. En plus de Python 3.x, nous utiliserons également :

  • Flask Web Framework version 1.0.2
  • Flask-OIDC où OIDC signifie "OpenID Connect". Il prend en charge l'utilisation d'OpenIDConnect dans les applications Flask.
  • Bibliothèque d'assistance Okta Python
  • Un compte développeur Okta gratuit

Tout le code de cet article de blog est fourni en open source sous la licence MIT sur GitHub sous le répertoire flask-auth-okta du référentiel blog-code-examples. Utilisez et abusez du code source des applications que vous souhaitez créer.

Installation des dépendances

Créez un nouvel environnement virtuel Python pour ce projet :

python3 -m venv flaskauth

Activez l'environnement virtuel avec le activate script :

. ./flaskauth/bin/activate

L'invite de commande devrait changer après l'activation :

N'oubliez pas que vous devrez activer le virtualenv dans chaque fenêtre de terminal où vous souhaitez utiliser les dépendances contenues dans ce virtualenv.

Nous pouvons maintenant installer Flask et les dépendances Okta.

pip install flask>=1.0.2 flask-oidc>=1.4.0 okta==0.0.4

Recherchez une sortie similaire à la suivante pour confirmer que les dépendances ont été installées avec succès :

...
Collecting idna<2.8,>=2.5 (from requests>=2.5.3->okta)
  Downloading https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl (58kB)
    100% |████████████████████████████████| 61kB 16.6MB/s 
Collecting urllib3<1.24,>=1.21.1 (from requests>=2.5.3->okta)
  Downloading https://files.pythonhosted.org/packages/bd/c9/6fdd990019071a4a32a5e7cb78a1d92c53851ef4f56f62a3486e6a7d8ffb/urllib3-1.23-py2.py3-none-any.whl (133kB)
    100% |████████████████████████████████| 143kB 14.0MB/s 
Installing collected packages: MarkupSafe, Jinja2, click, itsdangerous, Werkzeug, flask, pyasn1, pyasn1-modules, rsa, httplib2, six, oauth2client, flask-oidc, chardet, certifi, idna, urllib3, requests, python-dateutil, okta
  Running setup.py install for MarkupSafe ... done
  Running setup.py install for itsdangerous ... done
  Running setup.py install for httplib2 ... done
  Running setup.py install for flask-oidc ... done
  Running setup.py install for okta ... done
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 certifi-2018.8.24 chardet-3.0.4 click-6.7 flask-1.0.2 flask-oidc-1.4.0 httplib2-0.11.3 idna-2.7 itsdangerous-0.24 oauth2client-4.1.3 okta-0.0.4 pyasn1-0.4.4 pyasn1-modules-0.2.2 python-dateutil-2.7.3 requests-2.19.1 rsa-4.0 six-1.11.0 urllib3-1.23

Nous avons installé nos dépendances Flask et Okta requises, alors passons à la création de l'application Flask.

Création d'une application Flask de base

La première étape avant d'ajouter l'authentification à notre application Flask consiste à écrire des fonctions d'échafaudage. L'authentification s'accrochera à ces fonctions, telles que signin et signout , pour s'assurer que le processus d'authentification fonctionne correctement.

Créez un répertoire pour votre projet nommé thundercats . Pourquoi thundercats ?Pourquoi pas Thundercats ?

Dans le thundercats créer directement un fichier nommé app.py avec le contenu initial suivant :

# imports for Flask
from flask import Flask, Response


app = Flask(__name__)


@app.route("/lair")
def lair():
    return Response("Thundercats (supposed to be hidden) lair.")


@app.route("/")
def landing_page():
    return Response("Thundercats, Thundercats, hoooooooooooo!")

Nous pouvons exécuter notre application Flask en utilisant la commande suivante :

set FLASK_APP=app.py
flask run

Accédez à localhost:5000 dans votre navigateur Web et vous devriez voir :

Allez maintenant dans notre "repaire caché" sur localhost:5000/lair/. Éventuellement, cette page devrait nécessiter une authentification pour y accéder, mais pour l'instant elle apparaît sans aucun défi de connexion :

Génial, notre application de base est opérationnelle, passons à la fonctionnalité d'authentification.

Auth-as-a-Service

Rendez-vous sur la page d'inscription des développeurs Okta.

Ouvrez un nouveau compte ou connectez-vous à votre compte existant.

La partie intéressante du flux d'inscription des développeurs Okta est que vous devez maintenant vérifier votre courrier électronique pour terminer la création de votre compte. Recherchez un e-mail comme celui-ci :

Cliquez sur le bouton "Connexion" et connectez-vous au compte développeur en utilisant le mot de passe temporaire trouvé dans l'e-mail. Définissez un nouveau mot de passe et une question de défi. Choisissez ensuite une image correspondant à votre processus de connexion à votre compte.

Cliquez sur le bouton "Créer un compte" et vous serez redirigé vers le tableau de bord du développeur Okta.

Recherchez "l'URL de l'organisation" comme indiqué dans l'image suivante.

Nous allons utiliser cette URL dans notre fichier d'informations d'identification secrètes afin que notre application Web Flask puisse se connecter correctement au service Okta.

Créez un nouveau fichier dans votre répertoire de projet nommé openidconnect_secrets.json avec le contenu suivant :

{
  "web": {
    "client_id": "{{ OKTA_CLIENT_ID }}",
    "client_secret": "{{ OKTA_CLIENT_SECRET }}",
    "auth_uri": "{{ OKTA_ORG_URL }}/oauth2/default/v1/authorize",
    "token_uri": "{{ OKTA_ORG_URL }}/oauth2/default/v1/token",
    "issuer": "{{ OKTA_ORG_URL }}/oauth2/default",
    "userinfo_uri": "{{ OKTA_ORG_URL }}/oauth2/default/userinfo",
    "redirect_uris": [
      "http://localhost:5000/oidc/callback"
    ]
  }
}

Remplacez les quatre {{ OKTA_ORG_URL }} espaces réservés avec la valeur de l'URL de l'organisation trouvée dans votre tableau de bord. Nous remplirons le reste des espaces réservés avec des valeurs réelles au fur et à mesure que nous avancerons dans le didacticiel. Monopenidconnect_secret.json Le fichier aurait actuellement les valeurs suivantes basées sur l'URL de mon tableau de bord de développeur.N'oubliez pas que vos valeurs d'URL seront différentes !

{
  "web": {
    "client_id": "{{ OKTA_CLIENT_ID }}",
    "client_secret": "{{ OKTA_CLIENT_SECRET }}",
    "auth_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/authorize",
    "token_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/token",
    "issuer": "https://dev-860408.oktapreview.com/oauth2/default",
    "userinfo_uri": "https://dev-860408.oktapreview.com/oauth2/default/userinfo",
    "redirect_uris": [
      "http://localhost:5000/oidc/callback"
    ]
  }
}

Très bien, nous avons configuré notre compte Okta afin que nous puissions ajouter le code d'authentification à notre application Flask.

Connecter Flask à Okta

Nous devons connecter notre code Flask à notre nouveau compte Okta. La méthode recommandée pour inclure des variables telles que les informations d'identification de compte dans une application Flask consiste à gérer la configuration, nous l'utiliserons donc dans notre compte.

Mettez à jour le code Flask avec les lignes en surbrillance suivantes.

# imports for both Flask and Okta connection
from os import environ
from flask import Flask, Response
from flask_oidc import OpenIDConnect
from okta import UsersClient


app = Flask(__name__)
# secret credentials for Okta connection
app.config["OIDC_CLIENT_SECRETS"] = "openidconnect_secrets.json"
app.config["OIDC_COOKIE_SECURE"] = False
app.config["OIDC_CALLBACK_ROUTE"] = "/oidc/callback"
app.config["OIDC_SCOPES"] = ["openid", "email", "profile"]
app.config["SECRET_KEY"] = environ.get("SECRET_KEY")
app.config["OIDC_ID_TOKEN_COOKIE_NAME"] = "oidc_token"
# instantiate OpenID client to handle user session
oidc = OpenIDConnect(app)
# Okta client will determine if a user has an appropriate account
okta_client = UsersClient(environ.get("OKTA_ORG_URL"),
                          environ.get("OKTA_AUTH_TOKEN"))


@app.route("/lair")
def lair():
    return Response("Thundercats (supposed to be hidden) lair.")


@app.route("/")
def landing_page():
    return Response("Thundercats, Thundercats, hoooooooooooo!")

Nous ajoutons d'abord trois lignes d'importation, une pour extraire les valeurs des variables d'environnement, et les deux importations suivantes pour permettre d'utiliser OpenIDConnect et Okta dans notre application.

Le reste du nouveau code définit les valeurs de configuration de l'application Flask qui peuvent être utilisées pour instancier les clients OpenID Connect et Okta.

  • OIDC_CLIENT_SECRETS :l'emplacement du fichier de secrets OpenID Connect
  • OIDC_COOKIE_SECURE :permet le mode de développement pour tester la connexion et l'enregistrement des utilisateurs sans SSL. Votre application doit le définir sur True dans une application de production.
  • OIDC_CALLBACK_ROUTE  :URL dans l'application Web pour gérer les connexions des utilisateurs
  • OIDC_SCOPES  :quelles données demander à propos de l'utilisateur lorsqu'il se connecte. Notre application demande les informations de base sur l'e-mail, le nom et le profil
  • SECRET_KEY :il s'agit d'un paramètre Flask pour sécuriser les sessions. La clé ne doit jamais être rendue publique ou les sessions utilisateur de votre application Web seront compromises.

Où obtenons-nous ces valeurs de configuration d'application ? Nous devons les obtenir à partir de notre compte Okta, alors retournez au tableau de bord pour créer une nouvelle application OpenID Connect.

Les applications OpenID Connect utilisent un ID client et un secret client à la place des noms d'utilisateur et mots de passe traditionnels. L'ID client et le secret client indiqueront à votre serveur d'autorisation de reconnaître votre application. Appuyez sur le bouton "Ajouter une application".

Sur le nouvel écran de l'application, choisissez "Web" puis appuyez sur "Suivant".

Sur la page suivante, il existe de nombreuses options de configuration, mais seules quelques valeurs doivent être renseignées avant de pouvoir obtenir nos informations d'identification. Définissez les valeurs suivantes sur le Name , Base URIs et Login redirect URIs propriétés :

  1. ThunderFlaskCats pour Name
  2. http://localhost:5000 pour Base URIs
  3. http://localhost:5000/oidc/callback pour Login redirect URIs

Ce sont les trois valeurs que vous devez remplir pour l'instant, alors enregistrez l'application pour la créer.

Sur la page suivante, faites défiler vers le bas pour trouver votre client et vos clés secrètes.

Copiez et collez l'ID client et le secret client dans les lignes en surbrillance suivantes pour remplacer le {{ OKTA_CLIENT_ID }} et{{ OKTA_CLIENT_SECRET }} espaces réservés.

{
  "web": {
    "client_id": "{{ OKTA_CLIENT_ID }}",
    "client_secret": "{{ OKTA_CLIENT_SECRET }}",
    "auth_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/authorize",
    "token_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/token",
    "issuer": "https://dev-860408.oktapreview.com/oauth2/default",
    "userinfo_uri": "https://dev-860408.oktapreview.com/oauth2/default/userinfo",
    "redirect_uris": [
      "http://localhost:5000/oidc/callback"
    ]
  }
}

Enregistrez le fichier et assurez-vous de le garder hors du contrôle de version car ces valeurs secrètes doivent rester secrètes.

Il nous reste une étape dans le tableau de bord du développeur Okta avant de mettre à niveau notre application Flask avec le code d'authentification :créer un jeton d'authentification API. Accédez à l'onglet API.

Cliquez sur le bouton "Créer un jeton".

Nommez le jeton ThunderFlaskCatsToken et copiez-le. Enregistrez le jeton dans un endroit sûr car nous ne pourrons plus y accéder via le tableau de bord. Nous allons utiliser ce jeton lors de la définition du OKTA_AUTH_TOKEN variable d'environnement dans la section suivante de ce didacticiel.

Bon, nous avons enfin toute la configuration du service Okta et les jetons dans notre openidconnect_secret.json fichier dont nous avons besoin pour terminer notre application.

Protéger le repaire

Notre configuration est définie, alors mettez à jour le app.py fichier avec les lignes en surbrillance suivantes :

# imports for both Flask and Okta connection
from os import environ
from flask import Flask, Response, redirect, g, url_for
from flask_oidc import OpenIDConnect
from okta import UsersClient


app = Flask(__name__)
# secret credentials for Okta connection
app.config["OIDC_CLIENT_SECRETS"] = "openidconnect_secrets.json"
app.config["OIDC_COOKIE_SECURE"] = False
app.config["OIDC_CALLBACK_ROUTE"] = "/oidc/callback"
app.config["OIDC_SCOPES"] = ["openid", "email", "profile"]
app.config["SECRET_KEY"] = environ.get("SECRET_KEY")
app.config["OIDC_ID_TOKEN_COOKIE_NAME"] = "oidc_token"
# instantiate OpenID client to handle user session
oidc = OpenIDConnect(app)
# Okta client will determine if a user has an appropriate account
okta_client = UsersClient(environ.get("OKTA_ORG_URL"),
                          environ.get("OKTA_AUTH_TOKEN"))


@app.before_request
def before_request():
    if oidc.user_loggedin:
        g.user = okta_client.get_user(oidc.user_getfield("sub"))
    else:
        g.user = None


@app.route("/lair")
@oidc.require_login
def lair():
    return Response("Thundercats (supposed to be hidden) lair.")


@app.route("/")
def landing_page():
    return Response("Thundercats, Thundercats, hoooooooooooo!")


@app.route("/login")
@oidc.require_login
def login():
    return redirect(url_for(".lair"))


@app.route("/logout")
def logout():
    oidc.logout()
    return redirect(url_for(".landing_page"))

Les nouvelles lignes en surbrillance ci-dessus vérifient si un utilisateur est connecté ou non avant chaque demande. Si un itinéraire nécessite un utilisateur connecté en raison du @oidc.require_login décorateur, l'utilisateur sera alors redirigé vers la page de connexion. Nous avons également ajouté des itinéraires sous /login et /logout pour permettre de se connecter et de se déconnecter de l'application.

Définissez trois variables d'environnement afin que notre application puisse les utiliser lors de son exécution. Assurez-vous que les espaces réservés ORG_URL et AUTH_TOKEN sont définis avec votre valeur d'URL d'organisation réelle et votre jeton d'authentification à partir du tableau de bord du développeur Okta.

Sur la ligne de commande, exécutez les commandes suivantes, en veillant à remplacer toutes les valeurs d'espace réservé par vos propres jetons et URL :

# this tells Flask we want to run the built-in server in dev mode
export FLASK_ENV=development
# make sure to use a very long random string here that cannot be guessed
export SECRET_KEY='a very long string with lots of numbers and letters'
# this is the same Org URL found on your developer dashboard
# for example, https://dev-860408.oktapreview.com
export OKTA_ORG_URL='ORG_URL'
# this is the API authentication token we created
export OKTA_AUTH_TOKEN='AUTH_TOKEN'

Maintenant, relancez l'application Flask :

set FLASK_APP=app.py
flask run

Vous devriez être en forme si le serveur de développement démarre avec une sortie comme celle-ci :

(flaskauth)$ flask run
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 415-920-546

Rendez-vous sur localhost:5000 dans un navigateur où vous n'êtes pas déjà connecté à votre compte Okta (une fenêtre de navigation privée de votre navigateur Web fonctionne très bien).

Testons la fonctionnalité de redirection lorsque nous essayons d'accéder au /lair route en allant sur localhost:5000/lair. Nous sommes redirigés vers la page Oktalogin.

Entrez votre nom d'utilisateur et votre mot de passe de développeur Okta pour vous connecter à votre application. À des fins de développement, cela fonctionnera correctement pour les tests, mais il est évident que dans une application de production, vous créerez d'autres comptes auxquels les utilisateurs pourront se connecter.

Modifions encore un peu notre application pour corriger le manque flagrant d'excitation à remplir avec succès le code d'authentification pour ce didacticiel. Mettez à jour les deux lignes en surbrillance pour qu'elles correspondent à ce qui se trouve dans le bloc de code ci-dessous :

# imports for both Flask and Okta connection
from os import environ
from flask import Flask, Response, redirect, g, url_for
from flask_oidc import OpenIDConnect
from okta import UsersClient


app = Flask(__name__)
# secret credentials for Okta connection
app.config["OIDC_CLIENT_SECRETS"] = "openidconnect_secrets.json"
app.config["OIDC_COOKIE_SECURE"] = False
app.config["OIDC_CALLBACK_ROUTE"] = "/oidc/callback"
app.config["OIDC_SCOPES"] = ["openid", "email", "profile"]
app.config["SECRET_KEY"] = environ.get("SECRET_KEY")
app.config["OIDC_ID_TOKEN_COOKIE_NAME"] = "oidc_token"
# instantiate OpenID client to handle user session
oidc = OpenIDConnect(app)
# Okta client will determine if a user has an appropriate account
okta_client = UsersClient(environ.get("OKTA_ORG_URL"),
                          environ.get("OKTA_AUTH_TOKEN"))


@app.before_request
def before_request():
    if oidc.user_loggedin:
        g.user = okta_client.get_user(oidc.user_getfield("sub"))
    else:
        g.user = None


@app.route("/lair")
@oidc.require_login
def lair():
    thundercats_lair = '<html><head><title>Thundercats, hoooo!</title></head><body><h1>Thundercats now hidden lair.</h1><iframe src="https://giphy.com/embed/ahXtBEbHiraxO" width="480" height="273" frameBorder="0" class="giphy-embed" allowFullScreen></iframe><p><a href="https://giphy.com/gifs/retro-cartoons-thundercats-ahXtBEbHiraxO">via GIPHY</a></p></body></html>'
    return Response(thundercats_lair)


@app.route("/")
def landing_page():
    return Response("Thundercats, Thundercats, hoooooooooooo!")


@app.route("/login")
@oidc.require_login
def login():
    """Force user to login and then redirect them to the lair.
    """
    return redirect(url_for(".lair"))


@app.route("/logout")
def logout():
    oidc.logout()
    return redirect(url_for(".landing_page"))

Actualisez la page du repaire.

Bon c'est juste un peu mieux ! Allez sur localhost:5000/logout pour désauthentifier votre utilisateur. Lorsque vous allez à nouveau sur localhost:5000/lair, vous devrez maintenant vous authentifier à nouveau.

Et maintenant ?

Nous venons de créer un exemple d'application Flask avec authentification de l'utilisateur via l'API Okta.

Ensuite, essayez les didacticiels suivants pour ajouter d'autres fonctionnalités à votre application Flask :

  • Répondre aux SMS avec Python et Flask
  • Comment ajouter une surveillance hébergée aux applications Web Flask
  • Développer et exécuter des applications Flask dans des conteneurs Docker

Vous pouvez également déterminer ce qu'il faut coder ensuite dans votre projet Python en lisant la page de table des matières Full Stack Python.

Des questions? Contactez-moi via Twitter@fullstackpythonor @mattmakai. Je suis également sur GitHub avec le nom d'utilisateur mattmakai.

Quelque chose ne va pas avec ce message ? La source de cette page sur GitHuband soumet une pull request.