I'm trying to access Firebase from a server using PHP, the Google Auth library, and a wrapper for Firebase's REST...This works great to accomplish that:
use Firebase\JWT\JWT;
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use GuzzleHttp\Client;
$email = 'account@email.com';
$key = 'private_key_goes_here';
$scopes = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/firebase.database',
];
$creds = [
'client_email' => $email,
'private_key' => $key,
];
$serviceAccount = new ServiceAccountCredentials($scopes, $creds);
$handler = HttpHandlerFactory::build(new Client());
$token = $serviceAccount->fetchAuthToken($handler);
$firebase = new \Firebase\FirebaseLib($url, $token);
$value = $firebase->get('test/hello');
# $value now stores "world"
However, this requires the security rules in Firebase to be universal read / write, which I do not want. If I update my security rules to this:
{
"rules": {
"test": {
".read": "auth != null"
}
}
}
The result in $value
becomes {"error": "Permission denied"}
. I've searched extensively, and tried numerous permutations and possible solutions, with no conclusive results.
I've used this code to provide JWT tokens to end clients, which can successfully use them and leverage the security rules with no problem. I initially tried that same approach for the server, but was unsuccessful. I opted to try to combine the two methods:
# Snipping code that didn't change...
$serviceAccount = new ServiceAccountCredentials($scopes, $creds);
$handler = HttpHandlerFactory::build(new Client());
$payload = [
'iss' => $email,
'sub' => $email,
'aud' => 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
'iat' => time(),
'exp' => time() + 60 * 60,
'uid' => '123',
'claims' => [
'uid' => '123',
],
];
$payload = $serviceAccount->updateMetadata($payload);
$token = JWT::encode($payload, $key, 'RS256');
$firebase = new \Firebase\FirebaseLib($url, $token);
$value = $firebase->get('test/hello');
This seems to get close, but $value
now contains {"error": "Missing claim 'kid' in auth header."}
. To resolve this, I modified the encode call:
$token = JWT::encode($payload, $key, 'RS256', 'key_id_goes_here');
Which results in a slightly different error: Invalid claim 'kid' in auth header.
, suggesting I'm on the right track...But not quite there. Using the JWT token directly yields the exact same results. Any ideas what I'm doing wrong? The email, private key, and key id all came directly from the json
credential file provided when I created the service account.
I've looked at dozens of pages of documentation and posts, here are the ones that were the most helpful:
- Using JWT for Server Auth (Firebase Docs)
- Using Custom Tokens to make REST requests to FB DB as an admin
- Is it still possible to do server side verification of tokens in Firebase 3?
Cross posted to the Firebase Google Group.