Contents
はじめに
FCMのPHPでの実装方法が公式に書いてないので、苦労されている方も多いのではないでしょうか?
今回は公式のPHP認証用ライブラリでJWT(Json Web Token)作成してFCM HTTPv1を実装したいと思います。
ライブラリを使えばFirebaseコンソールと簡単なコードで実装が可能です。
FCMの認証の方法
認証の方法はいくつかあります。
- OAuth2.0のアクセストークンを使用した認証
googleapiに通信して自身のアカウント認証情報を渡し、アクセストークンを発行してもらう。 FCMプッシュ通知のリクエスト時はそのアクセストークンで認証する。 - JWTを用いた認証
自分のサーバー内で自身のアカウント認証情報を元にJWTを作成する。FCMプッシュ通知のリクエスト時にそのJWTで認証する。
今回はJWTを用いた認証で実装します。
使用するライブラリ
composer require google/auth
アカウント認証情報を取得
Firebaseコンソールから、対象アカウントの秘密鍵と認証情報を含んだjsonをダウンロードします。
Firebaseコンソールに入り、対象のプロジェクトに移動する
プロジェクトの設定画面に移動
サービスアカウントタブへ
秘密鍵を生成してjsonファイルをダウンロードする
このjsonファイルの中身を見ればわかりますが、アカウントやプロジェクトの情報・秘密鍵のデータが入っています。
jsonファイルをサーバの任意の場所に置いてください。
JWT(Json Web Token)とは? ざっくり解説
とりあえず実装見たい方は飛ばしてください。
JWTの構造
早速JWTの例↓
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MDQ3MDQ3Nzd9.bjV32uVK3HzL7dlKnTkungnbaerkBu4_exDaODTf3Cg
構造的には{ヘッダ}.{ペイロード}.{署名}
という形になっていて、ヘッダとペイロードはjsonをbase64エンコードしたものです。
//ヘッダ
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
//base64デコードすると…
{
"alg": "HS256",
"typ": "JWT"
}
//ペイロード(つまりボディ)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MDQ3MDQ3Nzd9
//base64デコードすると…
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1704704777
}
最後の署名部分は、ヘッダとペイロードの部分を、ヘッダの alg で指定したアルゴリズムと秘密鍵で署名します。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MDQ3MDQ3Nzd9
//↓アルゴリズムと秘密鍵で署名
bjV32uVK3HzL7dlKnTkungnbaerkBu4_exDaODTf3Cg
JWT自体には、ペイロードに何の情報を持たせるかの規定はありません。アプリケーション間で決めることになっています。
ただし基本的にはペイロードに有効期限を持たせることになっています。
JWTでトークンベースの認証ができる
Firebaseコンソールでダウンロードしたjsonファイルには、アカウント認証情報(email)と秘密鍵が含まれています。このjsonファイルからJWTを作成・署名することで認証に使用することができます。JWTには以下のようなメリットがあるので、トークンベース、つまりリクエスト毎にURLやヘッダなどにくっつける形の認証によく使われています。
- 比較的短いし、base64エンコードされてるのでURLとかヘッダにくっつけられる
- 秘密鍵で署名されるから作成者の保証ができるし、改ざんされても検知できて安心
- トークンを盗まれても、有効期限が切れたら無効になるので安心
- アプリ側で任意のデータを送ることができる
いざ実装
ライブラリでJWTを作成し、認証ヘッダ(Bearer ~)にくっつければOKです。
ServiceAccountJwtAccessCredentialsの引数にはFirebaseコンソールでダウンロードしたjsonファイルのパスを指定しましょう
<?php
require_once 'vendor/autoload.php';
use Google\Auth\Credentials\ServiceAccountJwtAccessCredentials;
//認証のスコープ FCM送信するために必要
$scope = ["https://www.googleapis.com/auth/firebase.messaging"];
$credentials = new ServiceAccountJwtAccessCredentials({JSONファイルへのパス}, $scope);
$jwt_token = $credentials->fetchAuthToken(); //JWT作成
//一応
if (!$credentials->projectId || !$jwt_token) {
return false;
}
$headers = [
'Authorization: Bearer ' . $jwt_token['access_token'], //JWTをヘッダに付与
'Content-Type: application/json'
];
//プッシュ通知のメッセージオブジェクト
//ペイロード構造も以前のバージョンと変わっているので公式をご覧ください
$message = [
'message' => [
'token' => '{送信先FCMトークン}',
'notification' => [
'title' => $title,
'body' => $body
],
'data' => [
'url' => $url,
]
]
];
$ch = curl_init();
//エンドポイントも変わってます
curl_setopt($ch, CURLOPT_URL, "https://fcm.googleapis.com/v1/projects/{$credentials->projectId}/messages:send");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($message));
$result = curl_exec($ch);
curl_close($ch);
終わりに
OAuth2.0も同じjsonファイルを使用するようなので、多分どちらの方式でも良いかと思います。通信があるので若干パフォーマンスは違うかも…?OAuth2.0の方ははgoogle_clientライブラリを使うようです。