如何在 Node.js 和 Express 中使用 Auth0

译文
开发
了解如何将Auth0登录功能添加到Node.js/Express应用程序,并使用经过身份验证的用户信息显示/隐藏UI信息和安全API。

 [[418735]]

【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'); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

这段代码概述了主要是:三个 API 端点,一个开放,一个需要主动登录,一个需要登录和特定权限。

现在将开发脚本添加到 package.json 文件的脚本部分:

"dev""nodemon server.js" 
  • 1.

下一步需要安装 nodemon 工具:

npm install -g nodemon 
  • 1.

然后,使用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> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.

完成上述操作,就可以转到 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 
  • 1.

将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' 
  }); 
}); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

概括地说,上面的内容是创建了一个 Express 中间件checkJwt,它将检查有效的 JSON Web 令牌。被配置为使用之前创建的 Auth0 API 中的信息。

请注意,issuerjwksUri指向您的系统 API 帐户,该帐户是在您注册时为您创建的。同样,每个用户有一个系统 API 帐户,而不是每个 API。此帐户提供密钥(在本例中为 JSON Web 密钥集)以对特定 API 的身份验证信息进行标记。

访问群体字段将引用您创建的API的标识符,而不是系统API帐户。

最后,请注意,还有checkScopes应用于受保护的端点。这将检查read:protected(读取:受保护)权限。

检查你的进度

此时,如果返回浏览器并单击“Members Only API”(或“Protected API”)按钮,服务器将响应错误:

UnauthorizedError: No authorization token was found. 
  • 1.

创建 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 > 
  • 1.

现在可以连接 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 
        }); 
      }; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

对于清单 5,首先使用前面提到的设置信息配置 Auth0 客户端。再次注意,域字段指的是每个用户一个系统 API。

两个处理程序都依赖于之前导入的 Auth0 库。如果应用此选项并刷新应用程序,则可以单击“登录”按钮并重定向到 Auth0 登录页面。这个页面是“通用登录”入口(Auth0 也支持集成一个“锁箱”组件)。注意,它自动支持用户名/密码和社交登录。

基于身份验证显示和隐藏内容 

清单 6 对 index.html 进行了一些更多的脚本更改,以实现显示/隐藏功能。

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

 

责任编辑:梁菲 来源: InfoWord
相关推荐

2021-07-30 11:20:53

JavaScriptNode.jsWeb Develop

2020-08-05 08:31:51

SSL TLSNode.js

2021-07-03 17:43:03

Node.jsNode变量

2014-07-11 14:16:15

AbsurdJSExpress

2020-08-07 10:40:56

Node.jsexpress前端

2021-10-25 09:00:37

Node.jsJS前端

2021-07-26 05:24:59

Node.js SO_RESUEPORLibuv

2021-06-15 15:03:21

MongoDBNode.jsCRUD

2017-05-10 09:40:57

Ubuntupm2Nginx

2022-11-17 09:52:12

RHEL 9Node.js

2011-09-09 14:23:13

Node.js

2011-10-18 10:17:13

Node.js

2022-08-22 07:26:32

Node.js微服务架构

2021-01-18 08:06:38

Node.js 追踪JSON

2017-04-24 08:31:26

Node.jsExpress.jsHTTP

2022-09-12 15:58:50

node.js微服务Web

2013-03-28 14:54:36

2022-08-28 16:30:34

Node.jsDocker指令

2021-05-18 09:01:39

Node.jsJSON文件

2022-08-12 07:01:00

Node.jsXSS脚本
点赞
收藏

51CTO技术栈公众号