如何在 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 文件

  1. const express = require('express'); 
  2.   const app = express(); 
  3.  
  4. app.get('/api/open', function(req, res) { 
  5.   console.log("/api/open"); 
  6.   res.json({ 
  7.     message: "Open Endpoint" 
  8.   }); 
  9. }); 
  10.  
  11. app.get('/api/members-only', function(req, res){ 
  12.   console.log("/api/members-only"
  13.   res.json({ 
  14.     message: 'Members Only Endpoint' 
  15.   }); 
  16. }) 
  17.  
  18. app.get('/api/protected', function(req, res) { 
  19.   console.log("/api/protected"
  20.   res.json({ 
  21.     message: 'Protected Endpoint' 
  22.   }); 
  23. }); 
  24.  
  25. app.listen(3000); 
  26. console.log('Listening on http://localhost:3000'); 

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

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

  1. "dev""nodemon server.js" 

下一步需要安装 nodemon 工具:

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

  1. <html> 
  2.   <head> 
  3.   <style> 
  4.       .hidden { 
  5.         display: none; 
  6.       } 
  7.       label { 
  8.         margin-bottom: 10px; 
  9.         display: block; 
  10.       } 
  11.     </style> 
  12.   </head> 
  13.   <body> 
  14.     <h1>Infoworld: Intro to Auth0</h1> 
  15.     <button id="btn-login" disabled="true" onclick="login()">Log in</button> 
  16.     <button id="btn-logout" disabled="true" onclick="logout()">Log out</button> 
  17.  
  18.     <h2>Fetch Open API</h2> 
  19.     <h3 id="openMessage"></h3> 
  20.     <button onclick="fetchOpenApi()">Open API</button> 
  21.  
  22.     <h2>Fetch Members Only API</h2> 
  23.     <h3 id="moMessage"></h3> 
  24.     <button onclick="fetchMembersOnlyApi()">Members Only API</button> 
  25.  
  26.     <h2>Fetch Protected API</h2> 
  27.     <h3 id="protectedMessage"></h3> 
  28.     <button onclick="fetchProtectedApi()">Protected API</button> 
  29.     <hr> 
  30.     <div class="hidden" id="gated-content"> 
  31.       <p> 
  32.         This content is hidden until user is logged in. 
  33.       </p> 
  34.       <label> 
  35.         Access token: 
  36.         <pre id="ipt-access-token"></pre> 
  37.       </label> 
  38.       <label> 
  39.         User profile: 
  40.         <pre id="ipt-user-profile"></pre> 
  41.       </label> 
  42.     </div> 
  43.   </body> 
  44. </html> 
  45.  
  46. <script> 
  47.   async function fetchOpenApi(){ 
  48.     let result = await fetch("/api/open"); 
  49.     let json = await result.json(); 
  50.     document.getElementById("openMessage").innerHTML = JSON.stringify(json.message); 
  51.   } 
  52.   async function fetchMembersOnlyApi(){ 
  53.     const token = await auth0.getTokenSilently(); 
  54.     let result = await fetch("/api/members-only"); 
  55.     let json = await result.json(); 
  56.     document.getElementById("moMessage").innerHTML = JSON.stringify(json.message); 
  57.   } 
  58.   async function fetchProtectedApi(){ 
  59.     const token = await auth0.getTokenSilently(); 
  60.     let result = await fetch("/api/protected"); 
  61.     let json = await result.json(); 
  62.     document.getElementById("protectedMessage").innerHTML = JSON.stringify(json.message); 
  63.   } 
  64. </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. 添加身份验证依赖项

  1. npm install --save express-jwt jwks-rsa express-jwt-authz 

将checkJwt与必要的导入一起添加到server.js,如清单 4 所示。请注意,您将使用详细信息填充一些元素(在方括号中)。

清单 4. 保护端点

  1. //... 
  2. const jwt = require('express-jwt'); 
  3. const jwtAuthz = require('express-jwt-authz'); 
  4. const jwksRsa = require('jwks-rsa'); 
  5. //... 
  6. const checkJwt = jwt({ 
  7.   secret: jwksRsa.expressJwtSecret({ 
  8.     cache: true
  9.     rateLimit: true
  10.     jwksRequestsPerMinute: 5
  11.     jwksUri: `https://[YOUR SYSTEM API DOMAIN].us.auth0.com/.well-known/jwks.json` 
  12.   }), 
  13.  
  14.   audience: '[THE IDENTIFIER FROM YOUR API]'
  15.   issuer: [`https://[YOUR SYSTEM API DOMAIN].us.auth0.com/`], 
  16.   algorithms: ['RS256'
  17. }); 
  18. var options = { customScopeKey: 'permissions'};  // This is necessary to support the direct-user permissions 
  19. const checkScopes = jwtAuthz([ 'read:protected' ]); 
  20. //... 
  21.  
  22. app.get('/api/members-only', checkJwt, function(req, res){ 
  23.   console.log("/api/members-only"
  24.   res.json({ 
  25.     message: 'Members Only Endpoint' 
  26.   }); 
  27. }) 
  28.  
  29. app.get('/api/protected', checkJwt, checkScopes, function(req, res) { 
  30.   console.log("/api/protected"
  31.   res.json({ 
  32.     message: 'Protected Endpoint' 
  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. 

创建 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。将以下内容添加到文件的标题中:

  1. < script  src ="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></ script > 

现在可以连接 auth.。首先连接登录和注销按钮。它们的处理程序见清单 5。

清单 5. 登录和注销处理程序

  1. const configureClient = async () => { 
  2.         auth0 = await createAuth0Client({ 
  3.           domain: "[YOUR SYSTEM API URL].us.auth0.com"
  4.           client_id: "[YOUR CLIENT ID]"
  5.           audience: "[YOUR API IDENTIFIER]" // The backend api id 
  6.         }); 
  7.       } 
  8. const login = async () => { 
  9.         await auth0.loginWithRedirect({ 
  10.           redirect_uri: "http://localhost:3000" 
  11.         }); 
  12.       }; 
  13.       const logout = () => { 
  14.         auth0.logout({ 
  15.           returnTo: window.location.origin 
  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

2021-10-25 09:00:37

Node.jsJS前端

2020-08-07 10:40:56

Node.jsexpress前端

2021-07-26 05:24:59

Node.js SO_RESUEPORLibuv

2021-06-15 15:03:21

MongoDBNode.jsCRUD

2022-11-17 09:52:12

RHEL 9Node.js

2017-05-10 09:40:57

Ubuntupm2Nginx

2011-10-18 10:17:13

Node.js

2011-09-09 14:23:13

Node.js

2022-08-22 07:26:32

Node.js微服务架构

2021-01-18 08:06:38

Node.js 追踪JSON

2022-09-12 15:58:50

node.js微服务Web

2017-04-24 08:31:26

Node.jsExpress.jsHTTP

2021-05-18 09:01:39

Node.jsJSON文件

2022-08-12 07:01:00

Node.jsXSS脚本

2021-07-15 10:15:52

Node.jsJSON前端

2013-03-28 14:54:36

点赞
收藏

51CTO技术栈公众号