2023年11月1日水曜日

Plurk Custom EmotionIcon : Chrome / Firefox web extension 開發筆記 / oAuth流程與參數解說

Plurk的自訂表符,未使用噗幣的帳號,上限是60個,筆者覺得不夠用。當然是建議購買噗幣支持官方營運,就可以擴充上限至600個。
不過管理表符的功能還是需要的。嗯嗯...
原本是打算修改小耀博士開發的噗浪卡卡,之前有改出一版,不過後來因為chrome擋掉一些之前能做的動作(像是動態插入程式碼然後執行),評估後覺得修改困難,決定寫新的。


進入點:manifest.json與javascript相關

第一步:先看入門文件。

https://developer.chrome.com/docs/extensions/mv3/getstarted/

筆者覺得相當合用。下面只列出需要注意的部分。

manifest v3跟v2 有參數的差異。像是最難處理的permissions,v3分成「optional_permissions」與「host_permissions」。

「browser_action」在v3改為「action」。api也從v2的「browser.browserAction」改名為「browser.action」。
要注意的是api文字定義不通用。宣告v2只能用v2的定義「browser.browserAction」,宣告v3只能用v3的定義「browser.action」。

firefox跟chrome也有參數的差異。像是firefox用「background.scripts = [...]」的格式,chrome卻是用「backgound.service_worker = “filename.js”」的格式。
加上雙方都不允許自身未定義的keyword存在,
例如chrome不允許「browser_specific_settings」的參數,
導致manifest不能一份通用兩邊。

這次開發遇到最大的困難是firefox的檔案儲存功能。
firefox的可用程式碼分成下面幾種:

  • content_script:可以注入正在使用的網頁。browser api的限制比較多。
  • background_script:沒有browser api的限制,但是不能對正在使用的網頁進行html tag操作。也不能使用alert / confirm / prompt等等預設對話框。因此基本的設計方式就是在content_script裡面進行ui操作,需要突破browser api限制的動作丟到背景執行。

    背景執行的動作雖然會被瀏覽器暫停,有定義addlistener()等等event driven的設計的話,只要有event送過去,背景動作就會被喚醒。不用擔心會有重啟或是初始化的問題
  • action popup / sidebar:可在瀏覽頁面時同時顯示。可以使用script,但是需遵循content security policy。基本上就是不能使用script tag,所以網頁常用的load script手段都需要把程式碼下載之後跟套件一起打包,記述在manifest的content_scripts.js 參數裡面讓瀏覽器幫你載入。在html記述<script>也會直接無效。
  • options_ui(在擴充套件的設定頁,以html load script),也沒有browser api限制。但是「要使用者點擊打開」之後才會執行。

「content_script」可用的browser api:

https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#webextension_apis

「background_script」「options_ui」可用的browser api:

https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Background_scripts

搜尋了幾十頁,大概是關鍵字不對,得不到正確答案。跑去stack overflow才找到。
其實是列在mozilla的擴充套件介紹的一開始concepts的地方。算筆者沒有從頭讀吧。因為先讀了chrome的介紹,就懶了...

在firefox的擴充套件開發遇到的限制:

  • content_script不可使用downloads api(就算有允許權限也一樣)
  • content_script不可使用permissions api(就算是經由form的submit也一樣)。
  • browser.permissions.request() 雖然在sidebar / popup / option-ui 可以使用,但是「只允許經由使用者操作啟動」。
    而且「必須使用<form>tag的submit功能實作」,不能使用html tag event,例如onclick實作...
    會獲得錯誤訊息「permissions.request may only be called from a user input handler」
    另外,以submit實作的時候,一定要記得在event function的第一行指定「ev.preventDefault();」。不然預設的頁面重刷動作會導致下載失敗。

    例外:可以用在「browser.action.onClicked.addListener」的定義。感覺有條件限制有點複雜。應該要有文件list會比較好。也許還有其他地方可以...
  • background_script可以使用session / local storage。但是跟網頁的資料是各自獨立的。與content_script(網頁的ui注入的script)溝通,需要使用browser.runtime.onMessage()做溝通。也許有其他方式,這邊不研究。

 

chrome的檔案儲存相對簡單。使用以下的一連串動作就解決了:

window.showSaveFilePicker(filePickerOpt)
    .then((newHandle) => newHandle.createWritable())
    .then((writableStream) => writableStream.write(JSON.stringify(data, null, 2)))
    .then((writableStream) => writableStream.close())
    .then((_) => {
        alert('ok');
    })
    .catch((e) => {
        alert('canceled');
    });

 

如何在正在操作的網頁插入擴充套件的icon/image:似乎是沒有直接的方法。
目前是採用把icon轉成base64 data string的方式處理。
參考網址: https://www.atatus.com/tools/image-to-url


關於oAuth

oAuth算是第一個難關。主要問題是簽名的計算。找到一個可用的套件:

https://github.com/bettiolo/oauth-signature-js

不需再包含其他套件是優點。

另外準備Plurk端的測試頁:

https://www.plurk.com/OAuth/test/

流程:

  • 到Plurk app註冊應用 https://www.plurk.com/PlurkApp/create,取得api key(在此定義為「api_key」)跟api secret (在此定義為「api_secret」),才能使用Plurk的api。線上表符的增減操作必須用到。
  • 以Plurk api「https://www.plurk.com/OAuth/request_token」進行取得暫時token的動作。在Plurk api 測試頁可以操作。注意以下規格:
    • Signature Base String的參數順序必須跟Plurk測試頁上面顯示的「Signature base string」一樣。
      也就是照此順序:
      • oauth_callback:後述。
      • oauth_consumer_key:註冊Plurk api時取得的「api_key
      • oauth_nonce:任意亂數。跟url參數傳入的必須相同
      • oauth_version:固定字串「1.0」
      • oauth_signature_method:固定字串「HMAC-SHA1」
      • oauth_timestamp:現在時間(以秒為單位)

      使用oauth-signature套件的時候,參數物件的參數加入順序要一模一樣。
      看起來是照英文字母順序排。不過第三步的動作又不是,所以只能硬寫。
    • oauth_callback不要進行uri encode,會編碼失敗。
      直接傳入未編碼,帶「https://」的url字串即可。
      oauth_callback會讓Plurk在使用者允許權限之後自動導向,並帶上url參數「oauth_verifier」跟「oauth_token」。可以減少使用者手動輸入參數的不便。
      此動作的兩個參數會在第三步用到。
    • oauth_signature參數使用「oauthSignature.generate('GET', url, parameters, api_secret);」產生。以前面的參數進行上述指令的加密,得到的加密字串,加到要進行https api request的url參數的最後一個。
      api_secret」為註冊Plurk app的時候會取得的api secret。
  • 第一步會以url參數的格式回傳暫用的「oauth_token」跟「oauth_token_secret」。
    建議這兩個參數以localStorage的方式暫存。因為「oauth_token_secret」之後會用到。
     
  • 接著進行第二步:使用url導向至「https://www.plurk.com/OAuth/authorize?oauth_token=<剛回傳的暫用oauth_token>」,讓使用者進行授權操作。
    此步驟可以先用Plurk測試頁進行操作,了解運作流程。不過因為沒有使用oauth_callback,流程會有差異,必須手動記下認證碼(等同oauth_verifier參數)。

    若是要支援多機器,或是讓同一台機器上面的firefox跟chrome同時使用,可以在url參數加上「deviceid」跟「model」兩個參數。規格請參考Plurk api文件。
    「deviceid」需要找一個方法取得對瀏覽器來說幾乎不會變動的id。似乎是可以使用一個亂數產生的字串(32文字以內)儲存在localStorage處理。這部分就不深究。
  • 自動導向回Plurk主頁的動作,會帶上url參數「oauth_verifier」跟「oauth_token」。
    oauth_token」要跟第一步取得的相同。為增加安全性的比對使用。
    記下「oauth_verifier」參數,進行第三步「取得存取token」。
  • 第三步「取得存取token」:以Plurk api「https://www.plurk.com/OAuth/request_token」進行取得「正式存取token」的動作。在Plurk api 測試頁可以操作。注意以下規格:
    • 參數順序:
      • oauth_consumer_key:註冊Plurk api時取得的「api_key
      • oauth_timestamp:現在時間(以秒為單位)
      • oauth_nonce:任意亂數。跟url參數傳入的必須相同
      • oauth_version:固定字串「1.0」
      • oauth_signature_method:固定字串「HMAC-SHA1」
      • oauth_token:第一步回傳的暫用「oauth_token
      • oauth_token_secret:第一步回傳的暫用「oauth_token_secret
      • oauth_verifier:第二步取得的「oauth_verifier」
    • oauth_signature參數使用「oauthSignature.generate('GET', url, parameters, api_secret, oauth_token_secret);」產生。
      注意多了一個參數「oauth_token_secret」。此參數就是第一步取得的「oauth_token_secret」。
      若是要以基本的HMAC-SHA1的加密套件進行加密,密鑰的組合方式為「api_secret&oauth_token_secret」。

      以前面的參數進行上述指令的加密,得到的加密字串,加到要進行https api request的url參數的最後一個。
  • 第三步會以url參數的格式回傳正式的「oauth_token」跟「oauth_token_secret」。
    這次的參數可以使用在Plurk定義的所有api。建議使用localStorage儲存。
    token的失效條件:
    • 使用者再次授權。舊的token就會失效。要同時存在多個token,必須在註冊流程的第二步加上「deviceid」跟「model」兩個參數。
    • 使用Plurk api進行expire動作
  • Plurk api存取:
    • 參數順序的規則:api參數擺最前面,後接基本參數。以取得線上表符的api舉例:
      • custom_only: true,表示只取自訂表符
      • non_custom_only:false
      • oauth_consumer_key:註冊Plurk api時取得的「api_key
      • oauth_timestamp:現在時間(以秒為單位)
      • oauth_nonce:任意亂數。跟url參數傳入的必須相同
      • oauth_version:固定字串「1.0」
      • oauth_signature_method:固定字串「HMAC-SHA1」
      • oauth_token:第三步回傳的正式「oauth_token」
    • oauth_signature參數是使用「oauthSignature.generate('GET', url, parameters, api_secret, oauth_token_secret);」所產生的。重點是以第三步取得的正式「oauth_token_secret」進行加密。
  •  Plurk的api文件頁,並沒有列出全部可用的api。請以Plurk的測試頁的選項為準。


關於蒐集者之眼:

Plurk的自訂表符編輯頁是使用iframe設計,所以取不到...
噗內的表符要打開噗的狀態才取得到。所以設計為固定時間掃整個dom node。












0 件のコメント:

コメントを投稿