【GAS × LINE WORKS】JWT認証

LINE WORKS APIの認証方法のひとつとしてJWT認証があります。
[Service Account認証 (JWT)](https://developers.worksmobile.com/jp/reference/authorization-sa?lang=ja)

LINE WORKS APIを使用するにあたりJWT認証が必要になるのですが、この処理が非常に面倒だったのでライブラリを作りました。良ければこちらを登録して使ってください。
自分でコード書きたい！って人は後ほど解説しますのでそちらを参照してください。

1. 登録したいGoogleAppScriptを開く
1. 「ライブラリ」の＋ボタンをクリック
1. 「ライブラリを追加」の欄に 1aLcCr3CWqfenPMyM0_FWIDUgRcTxsit9bO6BTx61NCXrCtkY2zbHBlod を入力して検索ボタンをクリック
1. 「LINEWORKS」ライブラリが表示されるので最新バージョンを選んで追加ボタンをクリック

## LINEWORKSライブラリ解説

解説ってほど中身用意してないんですが(^_-)-☆
取り合えず、今回の認証の部分だけ。

スクリプト内に`LINEWORKS.getAccessToken(ENV, scopes)`と記載することでTokenを取得できます。指定するパラメータは以下の通りです。
- ENV: LINE WORKSの認証情報（下記example.gs参照）
- scopes: 使用するAPIを指定します。Botを使いたいなら`bot`（詳細は[公式Document](https://developers.worksmobile.com/jp/reference/scopes?lang=ja)参照）

```js: example.gs
function example() {
    const token = LINEWORKS.getAccessToken(getEnv_(), "bot")
    Logger.log(token)
}
function getEnv_() {
  return {
    CLIENT_ID: "xxxxxxxxxxxxxxxx",
    CLIENT_SECRET: "xxxxxxxxxxxxxxxx",
    SERVICE_ACCOUNT: "xxxxxx.serviceaccount@xxxxxxxxxx",
    PRIVATE_KEY: "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0xxxxxxxxxxxxxxxxxxxxxxxxxxxxx==\n-----END PRIVATE KEY-----"
  }
}
```

## Reterun Value

```: 実行ログ
{
    refresh_token=jp1AAAAhPnqSpMJrEymayxxxxxxxxxxxxxxxxxxxxxxxxDfA==,
    token_type=Bearer,
    access_token=jp1AAABFJyracgGzaC4OcpgxxxxxxxxxxxxxxxxxxxxxTMmxQ==,
    expires_in=86400,
    scope=bot
}
```
JSON.parseしてあるのでスクリプト上では`token.access_token`と指定すればtokenを利用できます。

## Code

GoogleAppsScriptでのJWT認証はこんな風に実装しています。

```js: serviseAccount.gs
// 1. JWTを生成
function getJwt(ENV){
  const header = Utilities.base64EncodeWebSafe(JSON.stringify({"alg":"RS256","typ":"JWT"}), Utilities.Charset.UTF_8)
  const claimSet = JSON.stringify({
    "iss": ENV.CLIENT_ID,
    "sub": ENV.SERVICE_ACCOUNT,
    "iat": Math.floor(Date.now() / 1000),
    "exp": Math.floor(Date.now() / 1000 + 2000)
  })
  const encodeText = header + "." + Utilities.base64EncodeWebSafe(claimSet, Utilities.Charset.UTF_8)
  const signature = Utilities.computeRsaSha256Signature(encodeText, ENV.PRIVATE_KEY)
  return encodeText + "." + Utilities.base64EncodeWebSafe(signature)
}

// 2.JWTを使ってAccess Tokenを取得
function getAccessToken(ENV, scopes) {
  const uri = "https://auth.worksmobile.com/oauth2/v2.0/token"
  const payload = {
    "assertion" : getJwt(ENV),
    "grant_type" : encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
    "client_id": ENV.CLIENT_ID,
    "client_secret": ENV.CLIENT_SECRET,
    "scope": scopes
    
  }
  const options = {
    "method": "post",
    "headers": {"Content-Type" : "application/x-www-form-urlencoded"},
    "payload": payload
  }
  return JSON.parse(UrlFetchApp.fetch(uri, options))
}
```

#### API 1.0との違い

getJwt()
1. "iss": SERVER ID -> CLIENT IDに変更
1. "sub": SERVICE_ACCOUNTを新しく指定するようになった
1. Utilities.base64Encodeしたあと末尾を削らなくて良くなった。これは元々何で削る必要があったのか、ほんとよくわからなかったから、削らなくて良くなってせいせいした( ﾟДﾟ)

getAccessToken()
1. "payload": "client_id"と"client_secret"を新たなに指定するようになった

# おわりに

ここまでお付き合いいただきありがとうございました。
改めて思いましたけど、LINE WORKSの認証関係はずいぶんわかりやすくなりましたね。
認証クリアしてTokenはゲットできるようになったので、次はAPI Requestですね～。
まずはメッセージ送信からかな？Bot関係に沿って作って行こうと考えています。

ではまた！(^^)/

# 参考にさせていただきましたm(_ _)m

[GoogleAppsScript 公式サイト](https://developers.google.com/apps-script/reference/)
[LINE WORKS Developers](https://developers.worksmobile.com/jp/document?lang=ja)