【问题标题】:How to build request for oAuth2 using nodejs ?"如何使用 nodejs 构建对 oAuth2 的请求?”
【发布时间】:2022-01-11 00:37:15
【问题描述】:

我正在尝试使用 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
                                                }
                    }
}

【问题讨论】:

    标签: javascript node.js express oauth-2.0


    【解决方案1】:

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

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

    RFC6749 定义了两个授权服务器端点用于授权代码授权类型:授权端点和令牌端点。这在 RFC6749 的第 3 节中有描述,但我会总结一下:

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

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

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

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

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

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

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

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

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

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

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

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

    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 中所指定。为了节省您的时间,这些声明应该是:

    • exp
    • client_id
    • 知道
    • jti

    现在您的客户拥有一个令牌,他们应该能够使用它来针对您的 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.
    });
    
    

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

    【讨论】:

      猜你喜欢
      • 2021-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 2019-05-24
      • 2012-09-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多