繁体   English   中英

如何使用 nodejs 构建对 oAuth2 的请求?”

[英]How to build request for oAuth2 using nodejs ?"

我正在尝试使用nodejs实现oAuth2.0,我想到的方法是调用第一个令牌端点并获取令牌,然后使用令牌调用实际端点来获取数据。 我从后端获取未定义的令牌。

我需要以下场景的帮助,什么是为后端获取 oAuth2 令牌的最佳方法。

main.js

function getAllEODList() {

        const options = {

                    "tokenHost": "https://test/oauth2/token",
                    "addAuthTokenTo": "header",
                    "body": {
                       "scope": "APPPII APPPHI",
                       "grant_type": "client_credentials"
                    },
                    "credentials": {
                       "sdk_voyage": {
                          "clientId": "xyz",
                          "clientSecret": "zbc"
                       }
                    },
                    "authorizationMethod": "header/form"
                 
        }
       const promise =  do_something(options)
              .then(function(my_data){
                       const options2 = {
                             url: 'https://apiwebsite.com',
                             headers: {'authorization' : 'Bearer ' + my_data, 'Content-Type' : 'application/json'},
                             port: '443',
                             path: '/users',
                             method: 'GET'
                             }
                      do_something_else(options2)
                                     .then(function(my_other_data){
                                               //do stuff with my_other_data
                                                }
                    }
}

我对你如何表达你的问题和你的代码没有意义感到有点困惑,但我得到的要点是你想从后端获取一个令牌并且你正在尝试实现 OAuth 2.0,所以我会尽我所能在 Node.js 的背景下回答这个问题。 顺便说一句,当您说:“调用第一个令牌端点并获取令牌,然后使用令牌调用实际端点以获取数据。”,这不符合 OAuth 2.0 指定的推荐协议,并且在事实不安全。 我将首先解释你应该做什么,然后是如何实现它。

我将从实现RFC6749中定义的授权代码授权类型的角度回答这个问题,这是推荐使用的(使用 RFC6736 定义的PKCE ,但我将在此答案中省略 PKCE,因为它'只会让事情复杂化。不过,请随意阅读链接的 RFC。

RFC6749 定义了在授权码授权类型中使用的两个授权服务器端点:授权端点和令牌端点。 这在 RFC6749 的第 3 节中有描述,但我将对其进行总结:

客户端使用 Authorization 端点来获取授权,在本例中为code 这段代码是你需要生成服务器端并以某种方式存储的东西。 代码应该具有高熵,并且应该按照规范的定义在不超过 10 分钟的时间内有效。 您可以通过使用crypto模块生成一些随机字节并以某种方式将其缓存在您的后端 10 分钟来完成此操作。

令牌端点是客户端用来获取令牌的端点,必须提供有效代码才能获取令牌。

现在让我们查看整个流程:

客户端单击客户端前端的登录按钮。 客户端被重定向到授权服务器。 uri 非常重要,应该如下所示:

http://auth.example.com/authorize?grant_type=code&redirect_uri=http://example.com/callback&scope=openid scope1 scope2&client_id=example-client-id&state=some-opaque-value。

这个 url 不是 URL 编码的,但你明白我的意思(空格将替换为正确的 url 安全字符)。 URL 包含五个内容:

  1. 授权类型=代码
  2. redirect_uri=客户端的重定向uri
  3. scope= 由空格分隔的一组范围
  4. client_id=您的客户ID
  5. state= 在发送任何请求之前在客户端生成的具有大量熵的随机字符串。

根据RFC6749 第 4.1.1 节,授权服务器应验证此 URL 并确保其具有必要的查询参数,如果不是,则不应服务任何请求

如果 URL 有效,则 Authz 应显示登录或注册页面,并验证最终用户的凭据。 如果它们不正确,Authz 不应将它们重定向到 redirect_uri,而是显示某种错误 flash 消息。 但是,如果客户端验证成功,Authz 应该生成一个代码,即在您的情况下,您可以使用 Node.js crypto模块生成一个随机字符串。 这应该与客户端先前提供的 state 一起附加到重定向 uri < - 这很重要。 即:您的 Authz 应该将最终用户重定向回如下所示的内容:

http://example.com/callback?code=your-generated-code&state=the-state-that-was-presented

一旦发生这种情况,客户端应验证 url 中的state参数是否与其最初创建的参数相同。 如果它无效,则不应使用该代码 --> 有人在攻击您或您有错误。 如果 state 参数正确,那么您在该重定向 url 中收到的代码现在应该用于在我之前解释的令牌端点获取令牌

您在授权服务器上对令牌端点的请求可能如下所示:

http://oauth.example.com/token?grant_type=token&code=the-code-you-received&redirect_uri=http://example.com/callback&client_id=example-client-id

您的 Authz 现在应该验证

  1. 提供的代码是有效的
  2. redirect_uri 有效
  3. client_id 有效
  4. 代码尚未过期(它们的最大 ttl 应为 10 分钟)

如果一切顺利,Authz 应该创建并生成一个令牌,通常是 JSON Web 令牌 (JWT),其中包含以下声明,如RFC9068中所述。 为了节省您一些时间,这些声明应该是:

  • 国际空间站
  • 经验
  • 奥迪
  • client_id
  • 我在
  • 日铁

现在您的客户拥有一个令牌,他们应该能够使用它来针对您的 api 进行身份验证。

好的,现在我们已经看到了流程,这里有几个伪代码模板,您可以尝试实现它们,但是有很多因素可能会影响到这一点,例如,您的客户是公开的还是机密的(公开客户不' 除非它们被轮换并使用 PKCE 否则不会获得刷新令牌 --> 请参阅Auth0: Authorization Code Flow with PKCE

您的 Authz 可能有一个如下所示的文件:

// forgive me if my node.js isn't perfect, it's been a while since I've 
// written it but you get the gist:

import { Router } from 'express'

const router = Router();

//authorization endpoint
router.route('/authorize').get(async(req,res,next)=> {
    if(!validateRequestQuery(req.query)) return res.status(401).json({valid-oauth-error-msg-here});
    const state = req.query.state;
    const code: string = generateAuthorizationCode();
    const cacheSuccess = await cacheCodeForTenMinutes();
    if(!cacheSuccess) return res.status(500).json('valid-oauth-error-here');
    const callbackUri = `http://example.com/callback?code=${code}&state=${state}`;
    res.redirect(callbackUri);
  }
)
//once the above function is called, the client needs to validate the state //it sends back!

//token endpoint
router.route('/token').get(async(req, res, next){
   if(!validateTokenRequestQuery()) return a valid oauth error
   const code = req.query.code
   const isCodeValid = await checkCache(code);
   if(!isCodeValid) return a valid oauth error
   const accessToken = new Token(); // <--create a valid Access Token here
   const refreshToken = new Token(accessToken); // if your client is confidential or you are using refresh token rotation and PKCE!
   const idToken = new IdToken() // <-- if the openid scope is included
  return res.status(200).json('accesstoken, idtoken'); 
  // NEVER SEND REFRESH TOKEN TO FRONTEND. it is highly insecure. store it 
  // server side instead.
});

如果您有任何问题,请随时回复。 这是一个非常复杂的话题,我所说的某些内容可能过于模糊,所以请让我澄清一下。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM