Python >> Tutoriel Python >  >> Python

Ajout de l'authentification Okta à une application Web Flask existante

La mise en place d'un système d'authentification complet peut demander beaucoup de travail si vous avez une application Web Flask existante que vous codez. Okta facilite grandement la mise en place d'un système complet d'authentification des utilisateurs sans trop d'efforts supplémentaires. Dans ce didacticiel, nous prendrons le projet Flask Git Dashboard comme exemple et y ajouterons Okta.

Bibliothèques

Python 3 est requis pour ce tutoriel et 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 fini de cet article de blog est fourni en open source sous la licence MIT sur GitHub sous le répertoire auth-existing-flask-app/finished du blog-code-examplesrepository. Utilisez et abusez du code source pour vos propres applications.

Installation des dépendances

Nous allons commencer avec une application Web Flask existante. Si vous n'avez pas le vôtre que vous modifiez, clonez ce dépôt Git :

git clone [email protected]:fullstackpython/blog-code-examples.git

Ensuite, créez un nouveau Python virtualenv pour ce projet :

python3 -m venv flaskauth

Activer 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.

Accédez au répertoire du projet dans le block-code-examples Gitrepository que vous avez cloné.

cd blog-code-examples/auth-existing-flask-app/start/

Nous pouvons maintenant installer les dépendances pour le projet existant.

pip install -r requirements.txt

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

...
Collecting amqp<3.0,>=2.1.4 (from kombu<5.0,>=4.0.2->Celery==4.1.0->-r requirements.txt (line 4))
  Downloading https://files.pythonhosted.org/packages/7f/cf/12d4611fc67babd4ae250c9e8249c5650ae1933395488e9e7e3562b4ff24/amqp-2.3.2-py2.py3-none-any.whl (48kB)
    100% |████████████████████████████████| 51kB 10.7MB/s 
Collecting six>=1.5 (from python-dateutil->alembic>=0.6->Flask-Migrate==2.2.0->-r requirements.txt (line 2))
  Using cached https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl
Collecting vine>=1.1.3 (from amqp<3.0,>=2.1.4->kombu<5.0,>=4.0.2->Celery==4.1.0->-r requirements.txt (line 4))
  Downloading https://files.pythonhosted.org/packages/10/50/5b1ebe42843c19f35edb15022ecae339fbec6db5b241a7a13c924dabf2a3/vine-1.1.4-py2.py3-none-any.whl
Installing collected packages: click, itsdangerous, Werkzeug, MarkupSafe, Jinja2, Flask, SQLAlchemy, Flask-SQLAlchemy, Mako, python-editor, six, python-dateutil, alembic, Flask-Migrate, billiard, pytz, vine, amqp, kombu, Celery, redis, WTForms
  Running setup.py install for MarkupSafe ... done
  Running setup.py install for SQLAlchemy ... done
  Running setup.py install for Mako ... done
  Running setup.py install for python-editor ... done
  Running setup.py install for alembic ... done
  Running setup.py install for billiard ... done
  Running setup.py install for WTForms ... done
Successfully installed Celery-4.1.0 Flask-1.0.2 Flask-Migrate-2.2.0 Flask-SQLAlchemy-2.3.2 Jinja2-2.10 Mako-1.0.7 MarkupSafe-1.0 SQLAlchemy-1.2.12 WTForms-2.1 Werkzeug-0.14.1 alembic-1.0.1 amqp-2.3.2 billiard-3.5.0.4 click-7.0 itsdangerous-1.1.0 kombu-4.2.1 python-dateutil-2.7.5 python-editor-1.0.3 pytz-2018.7 redis-2.10.6 six-1.11.0 vine-1.1.4

Nous avons besoin de quelques dépendances supplémentaires pour que notre projet fonctionne, flask-oidc et okta :

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

Les dépendances sont maintenant correctement installées dans notre environnement virtuel. Testons l'application pour voir si nous pouvons la faire fonctionner correctement.

export FLASK_APP=flaskdash.py
export FLASK_ENV=development
flask run

Nous devrions voir l'application démarrer avec des valeurs de temps de développement par défaut :

 * Serving Flask app "flaskdash.py" (lazy loading)
 * 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: 203-814-092

Dirigez-vous vers localhost:5000 dans votre navigateur Web et nous devrions voir un tableau de bord de travail en cours :

Il est temps de créer un compte de développeur Okta afin que nous puissions obtenir les informations de configuration appropriées pour notre application.

Okta pour l'authentification

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.

Mise à jour de l'application Flask avec 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.

Mettre à jour config.py le code Flask avec les lignes en surbrillance suivantes.

import os


class Config(object):
    SECRET_KEY = os.getenv('SECRET_KEY') or 'development key'

    # Redis
    REDIS_SERVER = os.getenv('REDIS_SERVER') or 'localhost'
    REDIS_PORT = os.getenv('REDIS_PORT') or 6379
    REDIS_DB = os.getenv('REDIS_DB') or 1
    REDIS_URL = 'redis://{}:{}'.format(REDIS_SERVER, REDIS_PORT)

    # Celery task queue
    CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL') or REDIS_URL
    CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND') or REDIS_URL

    # database settings
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL') or \
      'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)),
      'flaskdash.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    OIDC_CLIENT_SECRETS = "openidconnect_secrets.json"
    OIDC_COOKIE_SECURE = False
    OIDC_CALLBACK_ROUTE = "/oidc/callback"
    OIDC_SCOPES = ["openid", "email", "profile"]
    OIDC_ID_TOKEN_COOKIE_NAME = "oidc_token"

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. FlaskApp 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 FlaskToken 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.

Mettre à jour app/__init__.py avec ces lignes en surbrillance :

import redis
from os import environ
from flask import Flask
from app.utils import make_celery
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_oidc import OpenIDConnect
from okta import UsersClient


app = Flask(__name__, static_url_path='/static')
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db) 

# connect to Redis instance
redis_db = redis.StrictRedis(host=app.config['REDIS_SERVER'],
                             port=app.config['REDIS_PORT'],
                             db=app.config['REDIS_DB'])
celery = make_celery(app)


# 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"))


from app import routes

Nous pouvons maintenant accéder au okta_client dans nos parcours. Ouvrez app/routes.py et mettez à jour les lignes suivantes :

from flask import send_from_directory, render_template
from flask import redirect, g
from app import app, oidc, okta_client


@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('/js/<path:path>')
def send_js(path):
    return send_from_directory('js', path)


@app.route('/css/<path:path>')
def send_css(path):
    return send_from_directory('css', path)


@app.route("/")
def dashboard():
    return render_template('dashboard.html')


@app.route("/repositories")
@oidc.require_login
def repositories():
    return render_template('repositories.html')


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


@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 /dashboard route en allant sur localhost:5000/repositories. 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.

Pour désauthentifier votre utilisateur, accédez à localhost:5000/logout. Lorsque vous reviendrez à localhost:5000/repositories, vous devrez maintenant vous authentifier à nouveau.

Et maintenant ?

Nous avons configuré une application Flask existante pour utiliser Okta pour l'authentification des utilisateurs et la gestion des identités via l'API Okta.

Ensuite, vous pouvez essayer l'un des didacticiels suivants pour ajouter d'autres fonctionnalités à l'application Flask :

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

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.