【51CTO.com快译】基于云计算的身份验证和授权平台(有时称为 IDaaS 或身份即服务)是云工具的一个不断扩展的领域,原因很容易理解。应用程序安全性困难且容易出错,几乎每个项目都需要云计算。将大部分工作转移到专门且经过验证的服务上的能力很有吸引力。
Auth0是一个身份验证和授权服务(以及开源软件)新兴提供商。在本文中,您将看到如何将 Auth0 登录功能合并到一个具有 Node.js/Express 后端的应用程序中,服务于一个直接的 JS 前端,然后使用经过身份验证的用户信息(通过 JWT)来显示/隐藏UI 信息并保护 RESTful 端点。
创建一个简单的 Node.js/Express 应用
首先,设置一个简单的 Express 应用程序。首先从命令行输入npm init开始。也可以任意命名项目名称。请注意,此示例应用程序旨在以简单和精简的方式突出显示安全元素,因此忽略了许多生产所需的功能,如错误处理和配置文件。
接下来,通过运行npm install Express从运行init的同一目录安装Express。
在您选择的代码编辑器中,在根目录中添加一个 server.js 文件,并将以下内容粘贴其中。
基本 server.js 文件
- const express = require('express');
- const app = express();
- app.get('/api/open', function(req, res) {
- console.log("/api/open");
- res.json({
- message: "Open Endpoint"
- });
- });
- app.get('/api/members-only', function(req, res){
- console.log("/api/members-only")
- res.json({
- message: 'Members Only Endpoint'
- });
- })
- app.get('/api/protected', function(req, res) {
- console.log("/api/protected")
- res.json({
- message: 'Protected Endpoint'
- });
- });
- app.listen(3000);
- console.log('Listening on http://localhost:3000');
这段代码概述了主要是:三个 API 端点,一个开放,一个需要主动登录,一个需要登录和特定权限。
现在将开发脚本添加到 package.json 文件的脚本部分:
- "dev": "nodemon server.js"
下一步需要安装 nodemon 工具:
- npm install -g nodemon
然后,使用npm run dev运行简单服务器,并在 localhost:3000/api/open 查看 API 响应。
提供静态文件
接着使用 express.static 从 /public/index.html 为客户端提供服务。该文件包含所有 HTML 和 JS,以便于理解所有内容,因此,Auth0文档称之为单页应用程序 (SPA)。我们的客户端将调用上面定义的后端 API。
在server.js中的app.listen行之前,添加以下行:app.use(express.static(join(__dirname, "public")));
意思是指示Node.js在/public中提供文件。然后创建文件 /public/index.html 并将以下的内容放入其中。
步骤三:开始 index.html
- <html>
- <head>
- <style>
- .hidden {
- display: none;
- }
- label {
- margin-bottom: 10px;
- display: block;
- }
- </style>
- </head>
- <body>
- <h1>Infoworld: Intro to Auth0</h1>
- <button id="btn-login" disabled="true" onclick="login()">Log in</button>
- <button id="btn-logout" disabled="true" onclick="logout()">Log out</button>
- <h2>Fetch Open API</h2>
- <h3 id="openMessage"></h3>
- <button onclick="fetchOpenApi()">Open API</button>
- <h2>Fetch Members Only API</h2>
- <h3 id="moMessage"></h3>
- <button onclick="fetchMembersOnlyApi()">Members Only API</button>
- <h2>Fetch Protected API</h2>
- <h3 id="protectedMessage"></h3>
- <button onclick="fetchProtectedApi()">Protected API</button>
- <hr>
- <div class="hidden" id="gated-content">
- <p>
- This content is hidden until user is logged in.
- </p>
- <label>
- Access token:
- <pre id="ipt-access-token"></pre>
- </label>
- <label>
- User profile:
- <pre id="ipt-user-profile"></pre>
- </label>
- </div>
- </body>
- </html>
- <script>
- async function fetchOpenApi(){
- let result = await fetch("/api/open");
- let json = await result.json();
- document.getElementById("openMessage").innerHTML = JSON.stringify(json.message);
- }
- async function fetchMembersOnlyApi(){
- const token = await auth0.getTokenSilently();
- let result = await fetch("/api/members-only");
- let json = await result.json();
- document.getElementById("moMessage").innerHTML = JSON.stringify(json.message);
- }
- async function fetchProtectedApi(){
- const token = await auth0.getTokenSilently();
- let result = await fetch("/api/protected");
- let json = await result.json();
- document.getElementById("protectedMessage").innerHTML = JSON.stringify(json.message);
- }
- </script>
完成上述操作,就可以转到 localhost:3000/,将看到一个基本的 HTML 页面,其中包含点击三个端点的三个按钮。在此阶段,如果单击按钮,这三个按钮都将返回结果,因为安全端点尚不安全。此外,登录和注销按钮尚未执行任何操作,页面底部的内容仍处于隐藏状态。
保护端点
现在,还需要一个 Auth0 帐户,该帐户可免费满足基本的使用。当注册账户时,Auth0 将为用户创建一个默认的“系统 API”。这是一个的特殊 API,每个用户只有一个,并让用户访问 Auth0 平台。公钥(在本例中为 RS256 的 jwks)通过此 API 公开。
接下来,将在 Auth0 系统中创建一个 API。单击“CreateAPI”按钮,这将打开如图 1 所示的屏幕。
图 1. 创建一个 Auth0 API
图 1. Auth0 API创建界面
对于name领域,可以使用任何名字。对于identifier,应该使用一个 URL,但不必公开 URL — 它只是一个在代码中引用的标识符。当然,在实际项目中,则需使用实际域名。对于表单上的最后一个字段,可以将算法保留为 RS256。
使用 Auth0 API
RS256公钥托管在 URL 中,格式为 https://[your_domain].auth0.com/.well-known/jwks.json。可以通过单击它旁边的“设置”找到新 API 的详细信息。注意,您现在提供的标识符的格式为 https://[your_domain].us.auth0.com/api/v2/,很快就会看到这两个 URL 正在运行。
下一步要做的就是定义权限。在这种情况下,需要访问之前创建的受保护端点所需的权限。在设置页面中,选择“权限”选项卡。创建一个read:protected权限并点击“添加”按钮。
使用 Express auth 中间件
使用 Express 中间件来强制执行权限策略。继续安装步骤三中的依赖项,其中分别包括 Express JWT(JSON Web 令牌)、JSON Web 密钥和 Express JWT 授权扩展。请记住,JWT 是一个带有身份验证信息的加密令牌。它将用于前端、后端和 Auth0 平台之间的通信。
清单 3. 添加身份验证依赖项
- npm install --save express-jwt jwks-rsa express-jwt-authz
将checkJwt与必要的导入一起添加到server.js,如清单 4 所示。请注意,您将使用详细信息填充一些元素(在方括号中)。
清单 4. 保护端点
- //...
- const jwt = require('express-jwt');
- const jwtAuthz = require('express-jwt-authz');
- const jwksRsa = require('jwks-rsa');
- //...
- const checkJwt = jwt({
- secret: jwksRsa.expressJwtSecret({
- cache: true,
- rateLimit: true,
- jwksRequestsPerMinute: 5,
- jwksUri: `https://[YOUR SYSTEM API DOMAIN].us.auth0.com/.well-known/jwks.json`
- }),
- audience: '[THE IDENTIFIER FROM YOUR API]',
- issuer: [`https://[YOUR SYSTEM API DOMAIN].us.auth0.com/`],
- algorithms: ['RS256']
- });
- var options = { customScopeKey: 'permissions'}; // This is necessary to support the direct-user permissions
- const checkScopes = jwtAuthz([ 'read:protected' ]);
- //...
- app.get('/api/members-only', checkJwt, function(req, res){
- console.log("/api/members-only")
- res.json({
- message: 'Members Only Endpoint'
- });
- })
- app.get('/api/protected', checkJwt, checkScopes, function(req, res) {
- console.log("/api/protected")
- res.json({
- message: 'Protected Endpoint'
- });
- });
概括地说,上面的内容是创建了一个 Express 中间件checkJwt,它将检查有效的 JSON Web 令牌。被配置为使用之前创建的 Auth0 API 中的信息。
请注意,issuer和jwksUri指向您的系统 API 帐户,该帐户是在您注册时为您创建的。同样,每个用户有一个系统 API 帐户,而不是每个 API。此帐户提供密钥(在本例中为 JSON Web 密钥集)以对特定 API 的身份验证信息进行标记。
访问群体字段将引用您创建的API的标识符,而不是系统API帐户。
最后,请注意,还有checkScopes应用于受保护的端点。这将检查read:protected(读取:受保护)权限。
检查你的进度
此时,如果返回浏览器并单击“Members Only API”(或“Protected API”)按钮,服务器将响应错误:
UnauthorizedError: No authorization token was found.
创建 Auth0 客户端应用程序
像创建一个 Auth0 API 来为后端应用程序建模一样,您现在将创建并配置安全端点的客户端或使用者。同样,Auth0 将它们称为 SPA(它们过去被称为“客户端”,现在仍然在一些 Auth0 文档中)。转到 Auth0 仪表板,然后在左侧菜单中选择“应用程序 -> 应用程序”,就在配置服务器时使用的 API 链接上方。
现在选择“创建应用程序”按钮。给它起个名字(也许称它为“客户端”以区别于后端应用程序)并确保选择“SPA”作为类型。点击“创建”。
然后通过从列表中选择来打开客户端应用程序。在这里,可以找到设置测试应用程序客户端所需的信息:域和客户端 ID。记下这些信息;后面会使用到。
在应用程序设置中配置回调、注销和 Web 源 URL
但是,如图 2 所示,将开发应用程序的 localhost 地址 (http://localhost:3000) 添加到允许的回调中。这让 Auth0 知道它可以将您的开发 URL 用于这些目标。
图 2. 将 localhost 添加到客户端配置
图 2. 将 localhost 添加到客户端配置
建立客户端身份验证
接着,返回应用程序代码,并在index.html中将Auth0 SDK添加到客户端。在这种情况下,我们将使用 CDN。将以下内容添加到文件的标题中:
- < script src ="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></ script >
现在可以连接 auth.。首先连接登录和注销按钮。它们的处理程序见清单 5。
清单 5. 登录和注销处理程序
- const configureClient = async () => {
- auth0 = await createAuth0Client({
- domain: "[YOUR SYSTEM API URL].us.auth0.com",
- client_id: "[YOUR CLIENT ID]",
- audience: "[YOUR API IDENTIFIER]" // The backend api id
- });
- }
- const login = async () => {
- await auth0.loginWithRedirect({
- redirect_uri: "http://localhost:3000"
- });
- };
- const logout = () => {
- auth0.logout({
- returnTo: window.location.origin
- });
- };
对于清单 5,首先使用前面提到的设置信息配置 Auth0 客户端。再次注意,域字段指的是每个用户一个系统 API。
两个处理程序都依赖于之前导入的 Auth0 库。如果应用此选项并刷新应用程序,则可以单击“登录”按钮并重定向到 Auth0 登录页面。这个页面是“通用登录”入口(Auth0 也支持集成一个“锁箱”组件)。注意,它自动支持用户名/密码和社交登录。
基于身份验证显示和隐藏内容
清单 6 对 index.html 进行了一些更多的脚本更改,以实现显示/隐藏功能。
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】