簡體   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