Fitbit APIからデータを取得するためには OAuth2 をパスする必要がある。FitbitAPI は以下の2つのflowをサポートしている。
Authenticate grant flow
implicit grant flow
ドキュメントでは、サーバサイドコードからFitbitAPIにアクセスする場合、Authenticate grant flow が推奨されている。
このエントリでは、djchen/oauth2 というoauth2クライアントを利用して、Authenticate grant flow をパスするサンプルを作成してみる。
composerによるoauth2クライアント(djchen/oauth2)の導入手順
dev.fitbit.comへのサンプルアプリ登録
FitbitのOAuth2認証をパスするためのサンプル実装
Fitbitからサンプルデータの取得
composerによるoauth2クライアントの導入手順
phpスクリプトをブラウザから叩ける環境を用意する。VagrantでもVPSでも何でも。
~/wwwが DocumentRoot となるように httpd を構成する。
$ pwd
/home/ikuty/www
次に、composerを使ってOAuth2クライアントをインストールする。
$ composer require league/oauth2-client djchen/oauth2-fitbit
dev.fitbit.comへのサンプルアプリ登録
これから作るサンプルアプリを dev.fitbit.com に登録する必要がある。
例えば以下のような登録を行う。
Application Name: Test Application
Description: This is my first test.
Application Web Site: http://hoge.com:8001
Organization: Personal
Organization Web Site: https://ikuty.com/
OAuth2.0 Application Type: Personal
Callback URL: http://hoge.com:8001/test.php
Default Data Access: readonly
すると、以下を発行してもらえる。
OAUth 2.0 Client ID
Client Secret
OAuth 2.0: Authorization URI
OAUth 2.0: Access/Refresh Token Request URI
FitbitのOAuth2認証をパスするためのサンプル実装
サンプル実装、といっても本家のサンプルをそのまま流用しただけだが…。
以下の Authenticate grant flow の流れの通りとなっている。
ClientID,ClientSecretを使用して認証コードを取得する
認証コード を accessToken に変換する
accessToken の有効期限に達したら refreshToken を使って accessToken を更新する
$ pwd
/home/ikuty/www
$ vi test.php
中身は以下
<?php
require_once \'./vendor/autoload.php\';
use djchenOAuth2ClientProviderFitbit;
use LeagueOAuth2ClientTokenAccessToken;
$provider = new Fitbit([
\'clientId\' => \'{client_id}\', // 登録時に取得したclientId
\'clientSecret\' => \'{clientSecret}\', //登録時に取得したclientSecret
\'redirectUri\' => \'http://hoge.com:8002/test.php\'
]);
session_start();
if(!isset($_GET[\'code\'])){
$authorizationUrl = $provider->getAuthorizationUrl();
$_SESSION[\'oauth2state\'] = $provider->getState();
header(\'Location: \'.$authorizationUrl);
exit;
} elseif ( empty($_GET[\'state\']) ||
($_GET[\'state\'] != $_SESSION[\'oauth2state\'])){
unset($_SESSION[\'oauth2state\']);
exit(\'Invalid state\');
} else {
try
{
$forceToAuth = false;
$needToRewrite = false;
$lines = file(\'token.txt\',FILE_IGNORE_NEW_LINES);
if (($lines == false) || (count($lines) == 0) || $forceToAuth)
{
echo \'authorization_code->\';
//ここで 認証コード と accessToken を交換する
$accessToken = $provider->getAccessToken(\'authorization_code\',[\'code\'=>$_GET[\'code\']]);
$needToRewrite = true;
} else {
echo \'existing AccessToken->\';
$_accessToken = $lines[0];
$_refreshToken = $lines[1];
$_expiredToken = $lines[2];
$accessToken = new AccessToken([\'access_token\'=>$_accessToken,
\'refresh_token\'=>$_refreshToken,
\'expires_in\'=>$_expiredToken]);
// accessToken の有効期限に達したら refreshToken を使って新しい accessTokenを要求する
if ($accessToken->hasExpired())
{
echo \'refresh AccessToken->\';
$refreshToken = $accessToken->getRefreshToken();
$accessToken = $provider->getAccessToken(\'refresh_token\',[\'refresh_token\'=>$refreshToken]);
$needToRewrite = true;
}
}
if ($needToRewrite)
{
$file = fopen(\"token.txt\",\"wb\");
fputs($file, $accessToken->getToken());
fputs($file, \"n\");
fputs($file, $accessToken->getRefreshToken());
fputs($file, \"n\");
fputs($file, $accessToken->getExpires());
fputs($file, \"n\");
fputs($file, $accessToken->getResourceOwnerId());
fputs($file, \"n\");
fclose($file);
}
} catch (Exception $e){
}
}
最初、「$accessToken->getToken()を保存しておいて次回利用時に使いまわす」具体的な方法が分からなかった。oauth2-client の AccessTokenクラスの実装を見ると、コンストラクタにアクセストークン等を渡してあげればインスタンス化できることがわかった。
アクセストークンの有効期限に達すると、hasExpired()メソッドがtrueを返すようになる。その場合、リフレッシュトークンを使って新しいアクセストークンを要求する。DBに保存しておいたアクセストークンを新しい値で上書きする。
Fitbitからサンプルデータの取得
RESTfulなAPIを指定することでデータが得られる。例えばユーザのプロフィールを取得するには以下の通りとする。厳密にAPIにパラメータを全て埋め込むタイプではなくログイン情報等のセッション情報も用いられるタイプ。以下では、user-idとして\"-\"を渡すとセッションにあるユーザIDが使われるようだ。
$request = $provider->getAuthenticatedRequest(
\'GET\',
Fitbit::BASE_FITBIT_API_URL . \'/1/user/-/profile.json\',
$accessToken,
[\'headers\' => [\'Accept-Language\' => \'ja_JP\'],[\'Accept-Locale\' => \'ja_JP\']]
);
$response = $provider->getResponse($request);
var_dump($response);