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 を構成する。
1 2 |
$ pwd /home/ikuty/www |
次に、composerを使ってOAuth2クライアントをインストールする。
1 |
$ 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 を更新する
1 2 3 |
$ pwd /home/ikuty/www $ vi test.php |
中身は以下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
<?php require_once './vendor/autoload.php'; use djchen\OAuth2\Client\Provider\Fitbit; use League\OAuth2\Client\Token\AccessToken; $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が使われるようだ。
1 2 3 4 5 6 7 8 |
$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); |