2
votes

Trying to do a simple read via PHP cURL. I can read my data successfully if my security rules let everyone in e.g.

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

However if I restrict read/write to a specific username e.g.

{
  "rules": {
    ".read": "auth.username == 'admin'",
    ".write": "auth.username == 'admin'"
  }
}

I get permission denied.

The code is as follows...

require('JWT.php');
$secret = 'MY_FIREBASE_SECRET';
$data = array('username' => 'admin');
$token = JWT::encode($data, $secret);

$url = "https://MY_FIREBASE.firebaseio.com/messages.json?auth=$token";
$curl = curl_init();
curl_setopt_array($curl, array(
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => $url
));
$response = curl_exec($curl);

Its worth noting, if I just use my FB secret instead of a token in the URL I am able to successfully read the data (auth=$secret). I have also successfully tested reading the data in the Forge simulator using "custom auth" e.g. {'username': 'admin'}

I'm using the PHP JWT library: https://github.com/luciferous/jwt/blob/master/JWT.php

Not sure if I'm getting permission denied because my cURL call is not correct or I'm not constructing the token properly. I have tried using POST and GET via cURL but I'm getting the same result.

Any suggestions would be much appreciated...


Thanks for the super quick response Andrew. I tried your suggestion. Unfortunately, I'm still getting 'permission denied'. Here is my updated code...

require('JWT.php');
$secret = 'my-secret';
$user = array( 'v' => 0, 'iat' => time(), 'd' => array('username' => 'admin', 'type' => 'admin', 'fullname' => 'Administrator'));
$token = JWT::encode($user, $secret);
$curl = curl_init();
$url = "https://myfirebase.firebaseio.com/messages.json?auth=$token";
curl_setopt_array($curl, array(
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => $url
));
$response = curl_exec($curl);
curl_close($curl);
  • I did get this working by changing the .read rule for our data to "auth != null" - but that doesn't seem to quite as secure...

For reference our data structure is simply

+ myfirebase
            + messages
                       - 000001 = "this is the 1st test message"
                       - 000002 = "this is the 2nd test message"

BTW: Our application will only have 1 user reading/writing data. If I can not get the token to work... Is there a better way to authenticate calls via the REST API without resorting to passing our secret key in the URL? e.g. &auth='my-secret'

1

1 Answers

2
votes

The Firebase JWT has some structure to it that is missing here. There's a detailed explanation of what should be in these auth tokens here: https://www.firebase.com/docs/security/jwt-auth-token-format.html

Here is a snippet with the appropriate structure.

require_once('JWT.php');

$fbSecret = 'your-secret';
$user = array( 'v' => 0, 'iat' => <timestamp>, 
  'd' => array('username' => 'jimbob', 'type' => 'admin',\
    'fullname' => 'Jim Bob')
  );
$token = JWT::encode($user, $fbSecret);

Note that the "d" field contains the actual payload. "v", and "iat" are also required. "iat" should be the number of seconds since the epoch (it's the number that (new Date()).getTime() returns in Javascript).