メモ：LINE BusinessConnect における 短期のチャネルアクセストークン の更新処理を実装する

メモ：LINE Messaging API のチャネルトークン、BusinessConnect では、更新処理が必要。


## 短期のチャネルアクセストークンとは
> 短期のチャネルアクセストークンとは、30日間有効な、Messaging API用のアクセストークンです。
>
> 短期のチャネルアクセストークンは/v2/oauth/accessTokenエンドポイントで発行されますが、必ずしも使う必要はありません。その代わりに、コンソールの［Channel設定］ページで発行される、長期のチャネルアクセストークンを使用できます。長期トークンはコンソールでいつでも発行できます。
> 
> 短期トークンは、LINE@プロプランまたは公式アカウントをお使いの場合にのみ必要です。
>
> https://developers.line.me/ja/faq/tags/channel-access-token/

## チャネルアクセストークンを発行する
> 
> 最大で30件のトークンを発行できます。上限を超過した場合は、発行順に既存のチャネルアクセストークンが取り消されます。
> https://developers.line.me/ja/docs/messaging-api/reference/#issue-channel-access-token

上限30件なので、10日毎に新しいアクセストークンを発行して更新していく、とかでいけそう。
トークンのリフレッシュは検討したが、リフレッシュ中に API Access したい場合あるので、常に再発行がいい。

## 設定ファイル
```yml:application.yml
line.bot:
  channel-id: ここに ChannelID
  channel-secret: ここに ChannelSecret
  channel-token-supply-mode: supplier # Supplier モード（プログラム的に AccessToken を与える）
  api-end-point: https://api.line.me/

# この下が重要です。OAuth2 Client を使って accessToken を取得します
security:
  oauth2:
    client:
      client-id: ${line.bot.channel-id}
      client-secret: ${line.bot.channel-secret}
      grant-type: client_credentials
      access-token-uri: ${line.bot.api-end-point}v2/oauth/accessToken
      client-authentication-scheme: form
```

## ビルドファイル
```groovy:build.gradle
dependencies {
    compile project(':line-bot-spring-boot')
    compile 'org.springframework.security.oauth:spring-security-oauth2'
}
```

## ファイル

こんな感じで、`OAuth2ProtectedResourceDetails` (Spring Boot が勝手に作る) を元手に、1日に1回 AccessToken を再作成する

```java:AccessTokenManager.java
package com.example.bot.spring.echo;

import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.stereotype.Component;

import com.linecorp.bot.client.ChannelTokenSupplier;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@EnableOAuth2Client
@EnableScheduling
public class AccessTokenManager {
    private static final int A_DAY_AS_MILLIS = 24 * 60 * 60 * 1000;
    private final OAuth2RestOperations oAuth2RestTemplate;
    private OAuth2AccessToken accessToken;

    public AccessTokenManager(
            final OAuth2ProtectedResourceDetails resourceDetails) {
        oAuth2RestTemplate = new OAuth2RestTemplate(resourceDetails, new DefaultOAuth2ClientContext());
        refresh();
    }

    @Scheduled(initialDelay = A_DAY_AS_MILLIS, fixedRate = A_DAY_AS_MILLIS)
    public void refresh() {
        log.info("AccessToken refresh start");
        accessToken = oAuth2RestTemplate.getAccessToken();
        log.info("AccessToken refresh successfully finished");
    }

    @Bean
    public ChannelTokenSupplier channelTokenSupplier() {
        return accessToken::getValue;
    }
}
```