🐈

【Flutter/Dart】SwitchBot API v1.1 httpsリクエスト時の認証に関するコード

2022/12/19に公開

はじめに

Flutter, Dart から SwitchBot API v1.1 を利用するにあたり、https リクエスト時の認証について具体的なコードを記載します。

注意点

コード

test/get_device_list_data_test.dart
import 'dart:convert';

import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:logger/logger.dart';

void main() {
  test('getDeviceListData', () async {
    // logger
    final logger = Logger();

    // signature生成にあたり必要な情報
    const token = "SwitchBotアプリ - プロフィール - 設定 - 開発者向けオプション - トークンの値を指定";
    const secret = "SwitchBotアプリ - プロフィール - 設定 - 開発者向けオプション - クライアントシークレットの値を指定";
    const nonce = ""; // 指定必須でない
    final dateTimeNowEpoch = DateTime.now().millisecondsSinceEpoch;
    logger.d(
        "token: $token,\n secret: $secret,\n nonce: $nonce,\n dateTimeNowEpoch: $dateTimeNowEpoch");

    String createSignature() {
      // logger
      final logger = Logger();

      // epochからDateTimeへ変換し、日時を確認
      final epochToDateTime =
          DateTime.fromMillisecondsSinceEpoch(dateTimeNowEpoch);

      String message = "epochからDateTimeへ変換した時刻: ";
      message += "$epochToDateTime";
      message += "\n ※時計がズレてると適切なsignatureが生成できず、結果、データ取得失敗する。";
      logger.d(message);

      // secretを文字列からバイト列に変換し、keyとする
      final key = utf8.encode(secret);

      // ハッシュ関数とkeyを引数に渡して、HMAC生成
      // HMAC-SHA256
      final hmacSha256 = Hmac(sha256, key);

      logger.d("key: $key,\n secret: $hmacSha256");

      // signatureの元データ作成
      final signatureSourceData = "$token$dateTimeNowEpoch$nonce";

      // signatureの元データを文字列からバイト列に変換
      final signatureSourceDataByteArray = utf8.encode(signatureSourceData);

      logger.d(
          "signatureSourceData: $signatureSourceData,\n signatureSourceDataByteArray: $signatureSourceDataByteArray");

      // signatureの元データのバイト列をHMACに渡してハッシュ値生成
      final digest = hmacSha256.convert(signatureSourceDataByteArray);

      // ハッシュ値のバイト列をbase64エンコードしてsignature生成
      final digestBytes = digest.bytes;
      final signature = base64.encode(digestBytes);

      logger.d(
          "digest: $digest,\n digestBytes: $digestBytes,\n signature: $signature");

      return signature;
    }

    Future<Response> getDeviceListData() async {
      // logger
      final logger = Logger();

      // request url
      const url = 'https://api.switch-bot.com/v1.1/devices';

      // request header
      final Map<String, String> headers = {
        'Authorization': token,
        'sign': createSignature(),
        'nonce': nonce,
        't': dateTimeNowEpoch.toString(),
        'Content-Type': 'application/json'
      };

      // dioインスタンス化
      final dio = Dio();

      // request, responseのheader情報をコンソール出力する設定
      dio.interceptors.add(LogInterceptor());

      try {
        final response = await dio.get(
          url,
          options: Options(
            headers: headers,
          ),
        );
        return response;
      } catch (e) {
        logger.d("response error: $e");
        rethrow;
      }
    }

    final response = await getDeviceListData();
    logger.d("response: $response");
  });
}

背景

最近、自宅に SiwtchBot を導入しました。
また、同じタイミングで、FlutterKaigi 2022 で、下記の発表を視聴しました。

FlutterKaigi 2022 Day 3
Flutter が拓くハードウェアの世界
https://www.youtube.com/watch?v=CgLQyTUTZPU&t=8730s

SwitchBot API + Raspberry Pi,センサー を組み合わせて、SwitchBot のラインナップを独自に拡張できそうだなと想像しました。
そして、その実装を Flutter,Dart で行えそうな点が、Flutter,Dart 学習の題材としても面白そうに感じ、取っ掛かりとなる SwitchBot API の認証について、記事を書きました。

Discussion