保护服务器客户凭据可能很棘手,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密钥,他们就可以在其应用程序中使用它,而无需进一步的用户身份验证。