LoginSignup
5
0

More than 1 year has passed since last update.

LINE WORKSのトークンをPowerAutomateで更新する(RefreshToken期限切れ問題)

Posted at

概要

LINE WORKSのAPIがVer2.0になりましたね。

そろそろ対応しておかないとな、と思って、Postmanで2.0のアクセストークンを取ったりして触っていたのですが、アクセストークンはどうやら24時間で切れる模様。

  • Access Token
    • 有効期限: 24時間
  • Refresh Token
    • 有効期限: 90日

LINE WORKSに限らず、アクセストークンなどはAzure KeyVaultに格納しているので、毎日KeyVaultのシークレットを更新する必要があるということになります。
LINE WORKSのBot運用はPowerAutomateで行っているので、トークンの更新もPowerAutomateのフローで行ってみました。

問題は...

リフレッシュトークンが90日で切れるということ。
リフレッシュトークンが有効期限切れとなった場合は、どうするんだろう...
ググってみると、

Refresh Tokenの有効期限は90日となっておりますので、期限内にTokenを再発行するような運用をご検討ください。

どうやら延長はできなさそう。
やっぱり、取り直さないといけないのか。

手っ取り早く対応するには

JWTの期限を十分長く取っておけば、作成したJWTは何度でも使いまわせるので、ダイスケ的にもオールオッケー(誰だお前は)
ただ、セキュリティ的にはどうなんでしょう?よく分からんとです。

JWTは使いまわさず、その都度作る

リフレッシュトークンが有効期限切れの時は、その都度JWTを作ってトークンを発行する方向で、フローを組んでみたいと思います。
(JWTの有効期限を長くすればよいと思いついたのが、なんやかんやでPowerAutomateでJWT作れた後だったので、今更後にも引けず)

事前準備

KeyVaultに以下のシークレットを作っておきます。

  1. ClientId
  2. ClientSecret
  3. AccessToken(有効期限付き)
  4. RefreshToken(有効期限付き)

それと、JWTの署名に使うプライベートキーファイルをKeyVaultにアップロードします。
2022-08-24_21h56_13.png
ファイルの拡張子は.keyから.pemに変更しておく必要があります。

フロー

全体

1日2回起動するため、「繰り返し」トリガーで起動します。
前半はKeyVaultのアクセストークンの取得です。
その後、LINE WORKSのIDやシークレットをKeyVaultから取得。
条件ブロックでリフレッシュトークンが有効かどうか判別して、リフレッシュトークンによる更新か、JWTを作成して新規に発行するか、の場合分けを行っています。
2022-08-24_21h58_55.png

KeyVaultにアクセスするためのトークンを取得

シークレットの更新はアクションが用意されていないので、直接APIを叩く必要があります。
その際に、アクセストークンが必要になるので、以下のような感じで、KeyVault用にアクセストークンを取得します。
こちらもアクセストークンの有効期限は1時間(3599秒)なので、都度取る必要があります。
2022-08-24_22h11_56.png
詳しくはMSの公式を参照してください。

取得したトークンは変数にぶち込んでおきます。
2022-08-24_22h16_25.png

あと、LINE WORKSのシークレット等をKeyVaultから取得しておきます。
(AccessTokenは使わないので取らなくても構いません)

条件分岐

KeyVaultに設定された、RefreshTokenの有効期限までの残り日数で場合分けします。
2022-08-24_22h18_58.png
残り日数は以下の式で求めています。

div(sub(ticks(outputs('LW2-RefreshToken')?['body/validityEndTime']),ticks(utcNow())),864000000000)

はいの場合(リフレッシュトークンで更新)

有効期限まで3日以上あれば、リフレッシュトークンでアクセストークンを更新します。
2022-08-24_22h23_24.png

リフレッシュトークンによる再発行

はまりポイントは、本文を改行で区切るとエラーではじかれました。
ぜんぶURLエンコードして渡しているのは、そのときの名残で、必要ないものまで全部URLエンコードしてます。(たぶん、必要ない)
2022-08-24_22h24_22.png

シークレットの更新

KeyVaultのAPIを叩くときは、日時はUNIX時間を指定する必要があります。
UNIX時間への変換は以下のページを参考にさせてもらいました。ありがとうございます。

2022-08-24_22h35_30.png
updatedとexpに、更新日時と有効期限を渡してやります。

こちらは、ここで終了。

いいえの場合(JWTを作成して再発行)

JWTを作成して、アクセストークンを発行し、KeyVaultのシークレットを更新する流れになります。
2022-08-24_22h38_44.png
JWTに必要なHeader、Payload、Signatureをそれぞれこさえていきます。

JWTのHeader部(①)

LINE WORKSのAPIリファレンス通りにJSONを書いて、Base64エンコードするだけ。
簡単ですね。
2022-08-24_22h45_53.png

JWTのPayload部(②)

これもリファレンス通り。
生成日時をutcNow()、期限日時をutcNow()の1時間後としています。
2022-08-24_22h47_38.png

JWTのSignature部(③)

最大の難関だったところです。
そもそも、署名のアルゴリズムとか全然わかっていないので、なにをどうしたらいいやら状態でした。
2022-08-24_22h54_20.png
カスタムコネクタの前に、KeyVaultに格納されたキーファイルで署名を作成するHTTPアクションですが、これもKeyVaultのコネクタには署名アクションが用意されていないため、直接APIを叩く必要があります。

algでアルゴリズムを指定して、valueで値を渡すだけです。
結論としては、concat(①,'.',②)をSHA256でハッシュ化した値を渡せばよいということになります。
PowerAutomateではハッシュ化する関数は用意されていないので、以下のページを参考にさせていただきました。

上記ページは、MD5でのハッシュかですが、コードの中のMD5をSHA256に変えるだけで、ほぼそのまま流用させていただきました。感謝!
「SHA256」アクションに、以下の式を渡して、その結果をKeyVaultに渡すと、署名が出来上がります。

concat(outputs('作成-Header(encoded)'),'.',outputs('作成-ClaimSet(encoded)'))

JWT完成!

できあがった①②③を、'.'で連結するとJWTが完成します。
2022-08-24_23h09_00.png

concat(outputs('作成-Header(encoded)'),'.',outputs('作成-ClaimSet(encoded)'),'.',body('JSON-PrivateKeyを使用して署名')?['value'])

(署名はBase64エンコード不要です)

アクセストークンの発行

作成したJWTをassertionに渡して、アクセストークンを発行します。
2022-08-24_23h11_50.png

アクセストークン・リフレッシュトークンをKeyVaultに格納!

リフレッシュトークンで更新した場合と同様、KeyVaultに格納していきます。
アクセストークンだけでなく、リフレッシュトークンも更新する必要があります。
リフレッシュトークンの有効期限を90日後として、expに渡すことで、条件ブロックが正しく判別できるようになります。
2022-08-24_23h13_57.png

最後にお知らせ

更新したアクセストークンで、LINE WORKSに通知してやります。
この辺はお好みで。
2022-08-24_23h16_26.png

あと、エラーとなった場合、LINE WORKSのアクセストークンが正しくないと思われるので、LINE WORKSではなく、Teamsに通知するようにしました。
2022-08-24_23h18_37.png

まとめ

だいぶ苦労しましたが、署名の方法やカスタムコードなど、初めて知ったことが多いフローとなりました。
Azure Functionsでやるしかないか、と思っていたのが、Power Platform内で完結できるのは非常に便利だなと思います。
ただ、C#が...(VBぐらいしか使えない人間なので)

PrivateKeyを再発行した場合でも、KeyVaultのキーを更新すれば手動でJWTを作り直して格納しておく必要もないので、運用はかなり楽になるのではないかと思います。(そんなに頻繁に再発行するものではないと思いますが)

最後に、長くなりすぎてごめんなさい。(スクリーンショットが...)

5
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0