1. OAuth認証についてなぜ調べたか
普段は経理の業務を行っています。
業務ではまだ使ったことがないのですが、前から freee会計 に興味がありました。
今回、ポートフォリオ用に「freee APIを使ったWebアプリ」を作ろうと思い、まずは事前調査として OAuth認証 についてまとめます。
freee APIを利用するためには、最初にOAuth認証でアクセストークンを取得する必要があります。
今回は「PHPでアクセストークンを取得して、companies APIを叩く」まで学習のために行ったので
下記、学習過程で調べたことを備忘録としてまとめます。
2. OAuth認証
freee APIは OAuth 2.0 認証 を使っています。
OAuth 2.0とは、他のサービスと安全に連携し、ユーザーがログインや認可を行うための仕組みです。
(例:GoogleログインなどもOAuthの仕組みを使っています)
OAuth2認証の仕組みで知っておくこと
| 役割 | 説明 |
|---|---|
| リソースオーナー | freee にログインするユーザー(例:経理担当者) |
| クライアント | 自分が作るアプリ(PHP) |
| 認可サーバー | 認可コードやトークンを発行(https://accounts.secure.freee.co.jp/...) |
| リソースサーバー | 実際のデータを返すAPIサーバー(https://api.freee.co.jp/api/1/...) |
freee APIの場合は以下の流れになります:
- ユーザーが freee 認可サーバーの画面にアクセス
- ログインして、アプリにアクセス許可を与える
- freee 認可サーバー → アプリ(リダイレクトURL)に「認可コード」を返す
- アプリ(サーバー側コード)が、その認可コードを freee 認可サーバー に渡してアクセストークンを取得
- アクセストークンを使って freee のリソースサーバー(APIサーバー)を呼び出す
freee APIの流れについて参考にしました
公式ドキュメントに認証フローの図があるので参考に
開発環境
Docker Compose で PHP + MySQL を動かす環境を用意しました。構成は以下の通りです。
-
Docker を利用して構築
-
サービス構成
-
app コンテナ
-
ベースイメージ:
php:8.2-cli -
インストール済みライブラリ:
- PHP拡張:
pdo,pdo_mysql,zip - 開発用:
xdebug - composer(
composer:2イメージからコピー)
- PHP拡張:
-
開発用サーバ:
php -S 0.0.0.0:8000 -t src -
ポート:
8000番をホストに公開 (http://localhost:8000) -
.envを読み込む設定あり (env_file: .env)
-
-
db コンテナ
- イメージ:
mysql:8.0 - ポート:
3306 - 永続化:
db_dataボリューム - 初期設定
- イメージ:
-
-
ボリューム
-
db_data→ MySQL データ永続化用
-
-
その他
- Xdebug 設定済み(リモートデバッグ用)
- composer でパッケージ管理 (
vlucas/phpdotenvなど利用可)
3. 手順メモ(実際にやったこと)
(1) freee 開発者コンソールでアプリを登録
-
client_idとclient_secretを取得 -
redirect_uriを設定(例:http://localhost/callback.php)
(2) 認可コードを取得してみる
開発アプリを作成したらアプリ詳細設定にあるWebアプリ認証用URLにアクセスすると、freeeのログイン画面が表示されます:
認証が成功すると、リダイレクトURLに ?code=xxxxx が付与されます。
(3) 認可コード → アクセストークンに交換(PHPサンプル)
今回の検証では、まずシンプルに index.php → callback.php で認証〜API呼び出しまで試しました。
freee API には PHP 向けの公式リファレンスはないようなので、ChatGPT に相談したところ、PHP では curl などのライブラリを使って HTTP リクエストを送り、JSON を扱う方法があるとわかりました。
具体的な流れとしては、freee API の公式リファレンスに記載されている JSON 構造を、そのまま curl の POST ボディに渡す形になります。PHP では json_encode($data) で同じ形式に変換して送信し、返ってきたレスポンスも JSON なので、json_decode($response, true) を使って連想配列に変換して処理します。
最初は動くものを試して理解したかったのでサンプルコードをChatGPTに作ってもらい、自分の環境に合わせてIDやURLを.envファイルで設定しました。
下記、ChatGPTに作成してもらったコードです。
index.php
<?php
// ----- 設定 -----
$client_id = "ClientID";
$redirect_uri = "callback.php のURL";
$state = bin2hex(random_bytes(16)); // CSRF対策
// ----- 認可URL生成 -----
$url = "https://accounts.secure.freee.co.jp/public_api/authorize?" . http_build_query([
"response_type" => "code",
"client_id" => $client_id,
"redirect_uri" => $redirect_uri,
"state" => $state,
"prompt" => "select_company"
]);
// ----- ユーザーを認可画面にリダイレクト -----
header("Location: $url");
exit;
callback.php
<?php
// ----- 設定 -----
$client_id = "自分のClientID";
$client_secret = "自分のClientSecret";
$redirect_uri = "http://localhost/callback.php";
if (!isset($_GET['code'])) {
die("認可コードがありません");
}
$code = $_GET['code'];
// ----- アクセストークン取得 -----
$token_url = "https://accounts.secure.freee.co.jp/public_api/token";
$data = [
"grant_type" => "authorization_code",
"client_id" => $client_id,
"client_secret" => $client_secret,
"code" => $code,
"redirect_uri" => $redirect_uri,
];
$options = [
"http" => [
"header" => "Content-Type: application/x-www-form-urlencoded\r\n",
"method" => "POST",
"content" => http_build_query($data),
],
];
$context = stream_context_create($options);
$response = file_get_contents($token_url, false, $context);
if ($response === FALSE) {
die("アクセストークン取得失敗");
}
$token_info = json_decode($response, true);
$access_token = $token_info['access_token'];
// ----- 事業所情報を取得 -----
$company_url = "https://api.freee.co.jp/api/1/companies";
$opts = [
"http" => [
"header" => "Authorization: Bearer $access_token",
"method" => "GET"
]
];
$context2 = stream_context_create($opts);
$company_response = file_get_contents($company_url, false, $context2);
if ($company_response === FALSE) {
die("事業所情報取得失敗");
}
$company_data = json_decode($company_response, true);
// ----- 画面表示 -----
echo "<h2>アクセストークン</h2>";
echo "<p>{$access_token}</p>";
echo "<h2>事業所情報</h2>";
echo "<pre>";
print_r($company_data);
echo "</pre>";
実行結果
- ブラウザで
index.phpにアクセスすると freee の認可画面に遷移 - 認可後
callback.phpに戻り、アクセストークンが取得できる - さらに、
companiesAPI を叩いて事業所情報が取得できた
(6) 環境変数で管理する例
.env に以下を記述:
FREEE_CLIENT_ID=自分のClientID
FREEE_CLIENT_SECRET=自分のClientSecret
FREEE_REDIRECT_URI=アクセス先
4. ハマったポイント
- freeeの会計APIリファレンスを読んでも最初は仕組みがよくわからなかった
- 特に「アクセストークンってどう取るの?」が大きな壁でした
参考記事
これらの記事を参考にしつつ、自分の環境で動作するコードをChatGPTに作成してもらい調整しました。
- freee API連携をPHPで試してみた (Qiita)
- PHPでOAuth2認証を実装する方法 (Zenn)
まとめ
-
freee APIを使うには、まず OAuth 2.0 認証フロー を理解することが大事。
特に「認可コード → アクセストークン → API呼び出し」という流れを理解しておくと全体像がつかみやすい。 -
アクセストークンを取得する際に重要なのは、以下の 4つの要素 を正しく設定すること:
- client_id(アプリを識別するためのID)
- client_secret(アプリの秘密鍵)
-
grant_type(利用するフローの種類。今回は
authorization_code) - redirect_uri(コールバックURL)(認可後にコードが返されるURL)
-
仕組みとして覚えておくべきは次の 3ステップ:
- 認可コードを取得する
- 認可コードをアクセストークンに交換する
- アクセストークンを使ってAPIを呼び出す
さいごに
curl などを使うのは初めてだったので良い学びになりましたが、自分で処理を組み立てる必要があるため、実務では使われているのか疑問を持ちました。また、curl以外にもGuzzleというライブラリを使う方法があると後でわかったので、今後試したいと思います。



