如何使用 Okta 将用户身份验证添加到 Flask 应用程序

用户身份验证是 Web 应用程序中的一项基本功能,因此人们可以创建和访问自己的帐户。不幸的是,身份验证并不总是很容易设置,并且有很多方法可以错误地实现登录和注销功能。

本教程将介绍如何使用名为 Okta 的安全身份验证服务(最多可免费使用 1,000 个活跃用户帐户)轻松处理 Flask 应用程序中的用户数据。

我们的工具

强烈建议使用 Python 3 构建应用程序,本教程是使用 Python 3.7 构建的,尽管早期版本的 Python 3 也应该可以正常工作。除了 Python 3.x,我们还将使用:

  • Flask 网络框架版本 1.0.2
  • Flask-OIDC,其中 OIDC 代表“OpenID Connect”。它支持在 Flask 应用程序中使用 OpenIDConnect。
  • Okta Python 帮助程序库
  • 一个免费的 Okta 开发者帐户

这篇博文中的所有代码都是在 GitHub 上的 blog-code-examples 存储库的 flask-auth-okta 目录下的 MIT 许可下作为开源提供的。使用和滥用您想要构建的应用程序的源代码。

安装依赖

为此项目创建一个新的 Python virtualenv:

python3 -m venv flaskauth

使用activate脚本激活虚拟环境:

. ./flaskauth/bin/activate

激活后命令提示符应该改变:

激活 flaskauth virtualenv。

请记住,您必须在每个要使用此 virtualenv 中包含的依赖项的终端窗口中激活 virtualenv。

现在我们可以安装 Flask 和 Okta 依赖项了。

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

查找类似于以下内容的输出以确认依赖项已成功安装:

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

我们安装了所需的 Flask 和 Okta 依赖项,让我们开始构建 Flask 应用程序吧。

创建一个基本的 Flask 应用

向我们的 Flask 应用程序添加身份验证之前的第一步是编写一些脚手架函数。身份验证将挂接到这些函数,例如 signinsignout,以确保身份验证过程正常工作。

为您的项目创建一个名为 thundercats 的目录。为什么 thundercats?为什么 Thundercats?

thundercats 中直接创建一个名为 app.py 的文件,初始内容如下:

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

我们可以使用以下命令运行我们的 Flask 应用程序:

set FLASK_APP=app.py
flask run

在您的网络浏览器中转到 localhost:5000,您应该看到:

正在运行的 Flask 应用程序的简单版本。

现在去我们位于 localhost:5000/lair/ 的“隐藏巢穴”。最终这个页面应该需要身份验证才能访问,但现在它似乎没有任何登录挑战:

应该隐藏在登录页面后面的 Flask 应用程序的一部分。

太棒了,我们的基本应用已启动并运行,让我们开始使用身份验证功能吧。

身份验证即服务

前往 Okta 开发者注册页面。

Okta 开发人员注册登录页面。

注册一个新帐户或登录您现有的帐户。

Okta 开发人员注册流程。

关于 Okta 开发人员注册流程的有趣之处在于,现在您应该检查您的电子邮件以完成您的帐户创建。查找像这样的电子邮件:

Okta 注册电子邮件。

单击“登录”按钮并使用电子邮件中的临时密码登录开发者帐户。设置新密码和挑战问题。然后选择一张图片以匹配您的帐户登录过程。

Okta 完成帐户创建。

单击“创建帐户”按钮,您将被带到 Okta 开发人员仪表板。

Okta 开发人员仪表板。

找到下图所示的“Org URL”。

Okta 组织 URL 值。

我们将在我们的秘密凭证文件中使用该 URL,以便我们的 Flask 网络应用程序可以正确连接到 Okta 服务。

在名为openidconnect_secrets.json 的项目目录中创建一个新文件,内容如下:

{
  "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"
    ]
  }
}

将四个 {{ OKTA_ORG_URL }} 占位符替换为在仪表板中找到的 Org URL 值。在继续学习本教程时,我们将用实际值填充其余的占位符。根据我的开发人员仪表板组织 URL,我的openidconnect_secret.json 文件当前具有以下值。请记住,您的 URL 值会有所不同!

{
  "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"
    ]
  }
}

好的,太棒了,我们已经设置了 Okta 帐户,因此我们可以将身份验证代码添加到我们的 Flask 应用程序中。

将 Flask 连接到 Okta

我们需要将我们的 Flask 代码连接到我们的新 Okta 帐户。在 Flask 应用程序中包含帐户凭据等变量的推荐方法是通过配置处理,因此我们将在我们的帐户中使用它。

使用以下突出显示的行更新 Flask 代码。

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

我们首先添加三个导入行,一个用于从环境变量中提取值,接下来的两个导入可以在我们的应用程序中使用 OpenIDConnect 和 Okta。

其余的新代码设置了可用于实例化 OpenID Connect 和 Okta 客户端的 Flask 应用程序配置值。

  • OIDC_CLIENT_SECRETS:OpenID Connect secrets文件所在位置
  • OIDC_COOKIE_SECURE:允许测试用户登录的开发模式和没有 SSL 的注册。您的应用程序必须在生产应用程序中将此设置为 True
  • OIDC_CALLBACK_ROUTE:Web 应用程序中用于处理用户登录的 URL
  • OIDC_SCOPES:用户登录时要请求的用户数据。我们的应用程序请求基本的电子邮件、姓名和个人资料信息
  • ### :这是一个 Flask 设置,用于确保会话安全。密钥绝不能公开,否则您的 Web 应用程序用户会话将受到损害。

我们从哪里获得这些应用程序配置值?我们需要从我们的 Okta 帐户获取它们,因此返回仪表板以创建一个新的 OpenID Connect 应用程序。

在 Okta 开发人员仪表板上选择应用程序。

OpenID Connect 应用程序使用客户端 ID 和客户端密码代替传统的用户名和密码。客户端 ID 和客户端密码将告诉您的授权服务器识别您的应用程序。按“添加应用程序”按钮。

单击添加应用程序按钮。

在新的应用程序屏幕上选择“Web”,然后按“下一步”。

选择网络应用程序。

在下一页上有许多配置选项,但在我们获得凭据之前我们只需要填写几个值。将以下值设置为 NameBase URIsLogin redirect URIs 属性:

  1. ThunderFlaskCats 用于 Name
  2. http://localhost:5000 用于 ###
  3. http://localhost:5000/oidc/callback for Base URIs

设置应用程序配置值。

这是您现在需要填写的三个值,因此请保存应用程序以创建它。

在下一页向下滚动以找到您的客户端和密钥。

保存客户端凭据供以后使用。

将客户端 ID 和客户端密码复制并粘贴到以下突出显示的行中,以替换 {{ OKTA_CLIENT_ID }}{{ OKTA_CLIENT_SECRET }} 占位符。

{
  "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"
    ]
  }
}

保存文件并确保将其置于版本控制之外,因为这些秘密值需要保密。

在使用身份验证代码升级我们的 Flask 应用程序之前,我们在 Okta 开发人员仪表板中还有一个步骤:创建 API 身份验证令牌。转到 API 选项卡。

单击仪表板中的 API 选项卡。

点击“创建令牌”按钮。

创建身份验证令牌以访问 Okta。

将令牌命名为 ThunderFlaskCatsToken 并复制它。将令牌保存在安全的地方,因为我们将无法再次通过仪表板访问它。我们将在本教程的下一节中设置 OKTA_AUTH_TOKEN 环境变量时使用此标记。

好的,我们的 openidconnect_secret.json 文件中终于有了完成应用程序所需的所有 Okta 服务配置和令牌。

保护巢穴

我们的配置已设置,因此使用以下突出显示的行更新 app.py 文件:

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

上面新突出显示的行检查用户是否在每个请求之前登录。如果路由由于 @oidc.require_login 装饰器而需要登录用户,则用户将被重定向到登录页面。我们还在 /login/logout 下添加了路由,以便可以登录和退出应用程序。

设置三个环境变量,以便我们的应用程序在运行时可以使用它们。确保占位符 ORG_URLAUTH_TOKEN 设置为来自 Okta 开发人员仪表板的实际组织 URL 值和身份验证令牌。

在命令行上运行以下命令,确保用您自己的标记和 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'

现在重新运行 Flask 应用程序:

set FLASK_APP=app.py
flask run

如果开发服务器启动时输出如下,你应该处于良好状态:

(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

在您尚未登录 Okta 帐户的浏览器中前往 localhost:5000(网络浏览器的隐身窗口效果很好)。

隐身模式下的登陆页面。

当我们尝试通过转到 localhost:5000/lair 转到 /lair 路由时,让我们测试重定向功能。我们被重定向到 Oktalogin 页面。

在隐身模式下被重定向。

输入您的 Okta 开发人员用户名和密码以登录到您的应用程序。出于开发目的,这可以很好地用于测试,但显然在生产应用程序中,您将创建其他帐户供用户登录。

登录后进入老巢网址。

让我们在我们的应用程序中再做一点调整,以解决在成功完成本教程的身份验证代码时明显缺乏兴奋感的问题。更新突出显示的两行以匹配下面代码块中的内容:

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

刷新巢穴页面。

带有新 GIF 的巢穴页面。

好吧,这只是好一点点!转到 localhost:5000/logout 以对您的用户进行身份验证。当您再次访问 localhost:5000/lair 时,您现在必须重新进行身份验证。

现在怎么办?

我们刚刚构建了一个示例 Flask 应用程序,通过 Okta API 进行用户身份验证。

接下来尝试按照以下教程将其他功能添加到您的 Flask 应用程序中:

  • 使用 Python 和 Flask 响应 SMS 文本消息
  • 如何将托管监控添加到 Flask Web 应用程序
  • 在 Docker 容器中开发和运行 Flask 应用程序

您还可以通过阅读完整堆栈 Python 目录页面来确定您的 Python 项目中接下来要编码的内容。

有问题吗?通过 Twitter@fullstackpython 或@mattmakai 与我联系。我也在 GitHub 上,用户名是 mattmakai。

这篇文章有问题吗?在 GitHub 上创建此页面的源代码并提交拉取请求。

赞(0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏