保护服务器客户凭据可能很棘手,OAuth 2.0是将用户身份验证卸载到其他服务的绝佳方法,但如果没有用户进行身份验证会怎样?在本文中,草根SEO将向您展示如何在用户的上下文之外使用OAuth 2.0,也就是所谓的客户端凭据流。

您可以使用第三方服务为您管理授权,而不是为您的客户(其他服务器)存储和管理API密钥。这种方式的工作方式是API客户端向OAuth服务器发送请求API令牌的请求。然后,该令牌将从API客户端连同其请求一起发送到您的API服务。获得客户端令牌后,您可以验证其有效性,而无需存储有关客户端的任何信息。

OAuth 2.0客户端凭据流验证的工作原理

验证您收到API服务的令牌的一种方法是将令牌转发到OAuth服务器以询问它是否有效。此方法的缺点是发送到服务器的每个API请求都需要发送到OAuth服务器的请求,这会增加您响应客户端所需的时间。另一种方法是使用称为本地验证的东西,这是一种由JSON Web Tokens(JWT)推广的策略。JWT以未加密的,机器可读的JSON包含您的声明(客户端数据)。

使用本地验证模式验证API令牌(JWT)时,您可以使用math来验证:

您的API正在接收的令牌未被篡改您的API正在接收的令牌尚未过期令牌中编码的某些JSON数据是您期望的那些

这怎么样安全?你可能想知道。JWT包含三个部分:标题,有效负载和签名。标头和有效负载是简单的base64编码字符串,可以轻松解密和读取。签名使用标头中列出的算法以及私钥来创建标头和有效负载的散列。如果没有私钥,则无法重新创建哈希,但可以使用公钥进行验证。

在某种程度上,这就像驾驶执照或护照。锻造起来相当困难,但有人可以很容易地看到它,看看你的名字,出生日期和其他信息。您可以扫描条形码,用黑灯测试,或查找水印以帮助验证其有效性。

虽然概念类似,但有效的JWT实际上难以伪造。拥有足够技能的人可以创建令人信服的驾驶执照,但如果没有私钥,可能需要一台现代计算机才能强制使用有效的JWT签名。代币也应该有一个到期日。虽然可配置,但可靠的默认值是一小时。这意味着如果客户端需要向API服务器发出新请求,则客户端需要每60分钟请求一个新令牌。如果令牌被泄露,这是一个额外的安全层。谁知道?也许有一台量子计算机可以在几个小时内重建签名。

既然您已了解OAuth 2.0客户端凭据流的基础知识,那么让我们构建一个使用Client Credentials和Okta的Node API。

Okta是什么?

简而言之,我们使身份管理比您习惯的更容易,更安全,更具可扩展性。Okta是一种API服务,允许您创建,编辑和安全存储用户帐户和用户帐户数据,并将它们与一个或多个应用程序连接。我们的API使您能够:

验证并授权您的用户

存储有关您用户的数据

执行基于密码和社交登录

使用多重身份验证保护您的应用程序

以及更多!查看我们的产品文档以获取更多信息

注册一个永远免费的开发者帐户,当您完成后,回过头来了解有关在Node中构建安全API的更多信息!

创建基本节点API

为了开始,我将向您展示如何在Node中创建基本API。Node在名为的文件中保留依赖项列表以及其他元数据。package.json

假设您已经安装了Node,请为您的API服务器创建一个新文件夹。然后,您可以使用为您npm生成一个。该命令将提示您输入一些信息,但您可以继续按下默认值。

package.jsonnpm initEnter$ mkdir client-credentials-flow$ cd client-credentials-flow$ git init$ npm init

在Node中启动和运行API服务器的最快方法是使用Express。您可以使用该命令将Express添加为依赖项。这将创建一个名为express 的文件夹,以及它所依赖的任何内容,然后您的应用可以使用这些文件夹。为了使开发更快,您还可以添加一个名为dev的依赖项,只要您进行代码更改,它就会重新启动服务器。要添加dev依赖项,请使用标志:。npm install [email protected] –savenode_modulesnodemon-Dnpm install -D [email protected]

构建Node应用程序时,通常要忽略将node_modules文件夹存储在git仓库中。您可以通过添加node_modules到您的.gitignore文件来实现。

echo node_modules >> .gitignore

软件包管理器还将包含一个文件(例如或),以便在下载另一台机器(带或)时,下载相同的版本。这有助于防止服务器之间的任何不一致,并使您不会想知道为什么某些东西在您的机器上运行,而不是在生产中。确保将该文件提交到您的git repo:package-lock.jsonyarn.locknode_modulesnpm installyarn

$ git add .$ git commit -m "Adding package files."

您还可以将脚本添加到文件夹以运行这些命令。使用该命令创建一个脚本(告诉它运行你的as中列出的脚本,默认情况下是。你也想用命令创建一个脚本。命令行依赖,比如,在里面运行的路径中一个节点脚本。你现在可以用或运行这些命令。你的文件现在应该是这样的:

package.jsonstartnode..package.jsonmainindex.jsdevnodemon *.js node .nodemonnpm startnpm run devpackage.jsonpackage.json{  "name": "client-credentials-flow",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "dev": "nodemon *.js node .",    "start": "node .",    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "",  "license": "ISC",  "dependencies": {    "express": "^4.16.3"  },  "devDependencies": {    "nodemon": "^1.17.5"  }}

现在为最基本的“Hello World”快递应用:

index.jsconst express = require('express')const app = express()app.get('/', (req, res) => res.send('Hello World!'))const port = process.env.PORT || 3000app.listen(port, () => console.log(`Listening on port ${port}`))

要启动它,请键入npm run dev终端窗口。您可以在我们进行更改时保持此运行,并且它将自动重新启动以反映新的更改。现在转到您的浏览器(或在命令行上),您应该看到回显。http://localhost:3000curl http://localhost:3000Hello World!

注册您的Node API的OAuth 2.0提供程序

现在来保护应用程序。这是您需要设置OAuth 2.0服务的地方。Okta是一种基于云的服务,允许开发人员轻松安全地存储OAuth 2.0令牌,用户帐户和用户数据,然后将其与一个或多个应用程序连接。Okta还提供了许多语言的库,包括Node,使开发人员可以非常轻松地将API集成到各种各样的应用程序中。

您可以使用Okta快速轻松地设置服务器到服务器身份验证。如果您还没有帐户,请注册免费的Okta Developer帐户。注册后,您将获得一个独特的Okta Org URL(例如)和一封用于激活新帐户的电子邮件。https://{yourOktaDomain}

您需要两个部分才能使客户端到服务器的身份验证工作:授权服务器和测试客户端/应用程序。

创建授权服务器

授权服务器是客户端可以请求在API服务器上使用令牌的地方。在Okta仪表板内,单击标题中的API选项卡,然后选择Authorization Servers选项卡。单击“ 添加授权服务器”,然后为您的服务器提供有用的名称和说明。本Audience应该是将要消耗的令牌服务器的绝对路径。

创建授权服务器后,您将需要一个供客户访问的范围。单击“ 范围”选项卡并添加范围。您可以拥有其中的许多内容,这些内容可以帮助定义正在使用的API的哪些部分,甚至是谁正在使用它。

既然您有一个范围,您还需要指定一些规则来说明谁有权访问它。单击“ 访问策略”选项卡并创建新策略。目前,只允许访问All clients。然后单击“ 添加规则”并为其命名。由于这是仅适用于客户端凭证,删除其他许可类型用于作用在代表用户(的Authorization Code,Implicit和Resource Owner Password),因此只有授权的类型是Client Credentials。除此之外,现在只使用默认设置。

返回“ 设置”选项卡,记下发卡行。这是客户端用于请求令牌的地址,以及您的API服务器将用于验证这些令牌是否有效的地址。

创建一个测试客户端

在Okta仪表板中,单击顶部标题中的“ 应用程序 ”。应用程序也称为客户端,因此您可以在此处创建测试客户端。单击“ 添加应用程序”,然后选择“ 服务(机器到机器)”。它需要的唯一信息是名称,所以你可以使用类似的东西Test Client。这将为您提供客户的凭据(在此测试用例中,就是您)。

保护您的Node API

你现在拥有了拼图的所有部分,所以只有经过身份验证的用户才能获得心爱的“Hello World”欢迎信息,其他人都会收到错误消息。

安全存储您的凭证

您需要安全地存储您的凭据。这样做的一种方法是在本地保存一个未存储在git中的文件(如果你的代码是开源的,那么这个文件特别有用,但无论如何都是一件好事)。这也允许您为多个应用程序(例如开发和生产环境)使用相同的代码。

继续,.env从授权服务器创建包含颁发者的文件,并从测试应用程序创建客户端凭据。确保将其添加到您的.gitignore文件中,这样就不会将其添加到您的git repo : . 您的文件应如下所示:echo .env >> .gitignore.env

.ENVISSUER=https://{yourOktaDomain}/oauth2/abcdefg1234567DEFAULT_SCOPE=such_scopeTEST_CLIENT_ID=client-idTEST_CLIENT_SECRET=client-secret

代码读取.env文件的快捷方法是使用名为的库dotenv。你可以安装它npm install dotenv。然后添加到第一行。您希望它是第一个运行的东西,以便您的其余代码可以访问这些环境变量。require(‘dotenv’).config()index.js

验证客户端请求

Okta提供了一个Node库来帮助验证JSON Web令牌。当它第一次看到验证令牌的请求时,它将通过您的授权服务器获取公钥Okta。然后默认情况下它将保持这些键一小时,尽管这是可配置的。如果有令牌无法验证,它将与Okta一起查看是否有新密钥可供使用。如果仍然无法验证它,库将抛出错误。你可以安装包。npm install @okta/[email protected]

您需要提供包含JWT的包。您可以告诉客户如何提供令牌,这可以通过多种方式完成。通常的做法是Authorization在HTTP(s)请求中使用标头,通常看起来像。修改您的端点以查找令牌并使用Okta进行验证。Bearer MG9h…NhOq==/

index.jsconst OktaJwtVerifier = require('@okta/jwt-verifier')const oktaJwtVerifier = new OktaJwtVerifier({  issuer: process.env.ISSUER,})app.get('/', async (req, res) => {  try {    const { authorization } = req.headers    if (!authorization) throw new Error('You must send an Authorization header')   const [authType, token] = authorization.split(' ')    if (authType !== 'Bearer') throw new Error('Expected a Bearer token')    await oktaJwtVerifier.verifyAccessToken(token)    res.json('Hello World!')  } catch (error) {    res.json({ error: error.message })  }})

再试一次。这次您将收到一条错误消息,因为您未经过身份验证。http://localhost:3000

如果你不熟悉相对较新的语法,这对你来说可能有点奇怪。这里发生的是函数标记为,因此它将始终返回a 。当它看到关键字时,函数的其余部分将暂停,直到响应返回。与此同时,主线程被释放以供其他JavaScript代码执行。async/awaitasyncPromiseawait

在此示例中,verifyAccessToken如果无法立即验证令牌,则向Okta发送请求。如果你setInterval在代码中的其他地方,那么代码仍然可以在等待Okta的响应时执行。

当verifyAccessToken完成时,它会如果令牌是无效抛出一个错误。因此,如果它超过该行而不抛出错误,则可以安全地假设客户端已经过验证,并且您可以发送“Hello World”消息。如果您想了解有关客户端的更多信息,可以从验证者那里获得响应。const jwt = await oktaJwtVerifier.verifyAccessToken(token)

测试您的安全API

您现在可以看到在没有正确身份验证的情况下在浏览器中出现错误,但我没有向您显示您仍然可以正确验证自己。为了从授权服务器获取令牌,您可以编写一个简单的Node脚本。本机节点request的使用有点繁琐,因此您可以使用该库,这将允许您继续使用promises和良好的语法。您还需要将字符串转换为base64。request-promiseasync/awaitbtoa

npm install [email protected] [email protected]test.jsrequire('dotenv').config()const request = require('request-promise')const btoa = require('btoa')const { ISSUER, TEST_CLIENT_ID, TEST_CLIENT_SECRET, DEFAULT_SCOPE } = process.envconst test = async () => {  const token = btoa(`${TEST_CLIENT_ID}:${TEST_CLIENT_SECRET}`)  try {    const { token_type, access_token } = await request({      uri: `${ISSUER}/v1/token`,      json: true,      method: 'POST',      headers: {        authorization: `Basic ${token}`,      },      form: {        grant_type: 'client_credentials',        scope: DEFAULT_SCOPE,      },    })    const response = await request({      uri: 'http://localhost:3000',      json: true,      headers: {        authorization: [token_type, access_token].join(' '),      },    })    console.log(response)  } catch (error) {    console.log(`Error: ${error.message}`)  }}test()

现在,当您的应用程序仍在端口3000上运行时,运行测试。这将向Okta发送请求以获取令牌,然后将该令牌转发到您的API服务器并打印结果。你应该得到一个很好的“Hello World”问候语!node test.js

即时注册客户

您有一个测试客户端,但在现实世界中,您可能希望让人们注册您的API,而无需登录Okta并为他们手动创建客户端。您也可能不希望每个人共享相同的客户端ID和密码,以便您可以跟踪谁在做什么请求,例如,频率。

Okta提供了一个API,允许您自动执行各种任务。其中之一是创建新的应用程序。Okta还有一个Node库,使其非常简单。要使您的应用程序使用Okta进行身份验证,您需要一个API令牌。登录仪表板,然后从标题中的API下拉列表中选择标记。单击“ 创建令牌”并为其指定有意义的名称。然后它会给你一个令牌 – 如果你输了它,你需要创建另一个。继续,将其添加到您的文件中。.envTOKEN

安装Okta Node SDK 。它需要您的组织URL,因此您也应该将其添加到您的文件中。然后创建一个新路由以注册新客户端。npm install @okta/[email protected]

index.jsconst okta = require('@okta/okta-sdk-nodejs')const oktaClient = new okta.Client({  orgUrl: process.env.ORG_URL,  token: process.env.TOKEN,})app.get('/register/:label', async (req, res) => {  try {    const application = await oktaClient.createApplication({      name: 'oidc_client',      label: req.params.label,      signOnMode: 'OPENID_CONNECT',      credentials: {        oauthClient: {},      },      settings: {        oauthClient: {          grant_types: ['client_credentials'],          application_type: 'service',        },      },    })    const { client_id, client_secret } = application.credentials.oauthClient    res.json({      client_id,      client_secret,      request_token_url: `${process.env.ISSUER}/v1/token`,    })  } catch (error) {    res.json({ error: error.message })  }})

您现在可以(在您的浏览器中很好)创建一个新客户端。第一次去那里,它应该给你一个客户端ID和秘密,并提醒你在哪里请求令牌。您可以使用此新客户端替换之前的客户端ID和密码,然后重新运行以查看此客户端现在也可以使用。http://localhost:3000/register/Awesome+App+Name.envtest.js

如果您重新登录Okta Developer Console,您会看到“Awesome App Name”已添加为应用程序。

请记住,这是一个测试…这只是一个测试。您可能不希望任何人在没有任何验证的情况下公开注册API密钥。这可能是实现某种CAPTCHA或需要用户身份验证来获取API密钥的好地方。一旦他们拥有API密钥,他们就可以在其应用程序中使用它,而无需进一步的用户身份验证。