PHPでFCMプッシュ通知 HTTP v1 API JWT認証

はじめに

FCMのPHPでの実装方法が公式に書いてないので、苦労されている方も多いのではないでしょうか?
今回は公式のPHP認証用ライブラリでJWT(Json Web Token)作成してFCM HTTPv1を実装したいと思います。

ライブラリを使えばFirebaseコンソールと簡単なコードで実装が可能です。

FCMの認証の方法

認証の方法はいくつかあります。

  • OAuth2.0のアクセストークンを使用した認証
    googleapiに通信して自身のアカウント認証情報を渡し、アクセストークンを発行してもらう。 FCMプッシュ通知のリクエスト時はそのアクセストークンで認証する。
  • JWTを用いた認証
    自分のサーバー内で自身のアカウント認証情報を元にJWTを作成する。FCMプッシュ通知のリクエスト時にそのJWTで認証する。

今回はJWTを用いた認証で実装します。

使用するライブラリ

google-auth-library-php

composer require google/auth

アカウント認証情報を取得

Firebaseコンソールから、対象アカウントの秘密鍵と認証情報を含んだjsonをダウンロードします。

  1. Firebaseコンソールに入り、対象のプロジェクトに移動する


  2. プロジェクトの設定画面に移動


  3. サービスアカウントタブへ


  4. 秘密鍵を生成して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
<?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ライブラリを使うようです。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール