Closed11

LINE Messaging API で Authentication failed

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

このスクラップについて

このスクラップでは LINE Messaging API を利用している時に発生した下記のエラーメッセージの問題解決をしていく。

Authentication failed. Confirm that the access token in the authorization header is valid.

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

対象のソースコード

line-access-token.ts
import { JWS } from 'node-jose';
import fetch from 'node-fetch';

// LINE Messaging API のチャンネルトークン v2.1 を取得します
// https://developers.line.biz/ja/docs/messaging-api/generate-json-web-token/
export async function getLineAccessToken(
  privateKey: string,
  keyId: string,
  channelId: string,
): Promise<string> {
  // チャネルアクセストークンを取得するための JWT を発行します
  const header = {
    alg: 'RS256',
    typ: 'JWT',
    kid: keyId,
  };

  const payload = {
    iss: channelId,
    sub: channelId,
    aud: 'https://api.line.me/',
    exp: Math.floor(new Date().getTime() / 1000) + 60 * 30,
    token_exp: 60 * 60 * 24 * 30,
  };

  const jwt =
    (await JWS.createSign(
      {
        format: 'compact',
        fields: header,
      },
      JSON.parse(privateKey),
    )
      .update(JSON.stringify(payload))
      .final()) + '';

  // エンドポイントからチャネルアクセストークンを取得します
  const accessTokenUrl = 'https://api.line.me/oauth2/v2.1/token';
  const accessTokenResponse = await fetch(accessTokenUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_assertion_type:
        'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
      client_assertion: jwt,
    }).toString(),
  });

  const accessTokenBody: any = await accessTokenResponse.json();
  return accessTokenBody.access_token;
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

テストコード

line-access-token.e2e-spec.ts
import { getLineAccessToken } from '../src/lib/line-access-token';

describe('LINE Webhook', () => {
  it('/ (GET)', async () => {
    const channelAccessToken = await getLineAccessToken(
      process.env.LINE_SECRET_KEY,
      process.env.LINE_KEY_ID,
      process.env.LINE_CHANNEL_ID,
    );

    console.log({ channelAccessToken });
  });
});
コンソール出力
{ channelAccessToken: undefined }

アクセストークンが undefined になっている。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

accessTokenBody

console.log() で出力してみたら下記のように表示された。

コンソール出力
    {
      message: 'The maximum number of access tokens has already been issued'
    }
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

原因

Cloud Run でデプロイしていて accessToken を保持しておくのが面倒だったので API にアクセスする度にアクセストークンを発行するようにしていたのだがどうやらそれがいけなかったようだ。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

長期アクセストークンの発行

長期のチャネルアクセストークンは、LINE Developersコンソールの[チャネル設定]タブでMessaging APIチャネルを選択して、[Messaging API設定]タブで発行できます。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

チャネルアクセストークン v2.1 の運用


LINE 公式ドキュメント より引用

チャネルアクセストークンを正しく管理している場合は、グループごとに一時的に最大2つのチャネルアクセストークンを発行すると、サービスを途切れることなく提供できます。

Cloud Scheduler などを使って定期的にアクセストークンを更新すれば良さそう。

当然だがアクセストークンを安全に保存する場所が必要になる。

やっぱり DB じゃダメかな?

このスクラップは2023/05/05にクローズされました