4

I'm using REST API in a PHP project to send the data to Firestore DB through curl (PHP curl).

I'm struggling in understanding how to setup an authentication rule for a "service" and the suggested approach through Google authorization looks overkilling to me.

My data must be readable by ALL but writable just by my PHP server.

So far I could ensure that a writing operation could happen only if in the dataset sent a specific code is provided (auth=123):

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.resource.data.auth==123
      //request.auth.uid != null;
    }
  }
}

Problem is that, as everybody can read the data, it's very easy to find this field (auth) and to know that 123 is the code.

Would be possible any of the two:

  1. Send headers (or other not recorded into DB data) and perform a rule check against that instead of the data fields?

  2. Otherwise (less preferred) would be possible to hide ONLY the field "auth" to the users?

I know option 2 is not possible in principle but it might be worked around somehow even if I don't know how.

While I understand this is not the best approach, it would be more than enough to secure my application.

EDIT

Probably the simplest cleanest solution would be to create an user in Firestore db with a pair email/password and use that in the PHP curl request

I think I need to issue a curl request following this:

https://firebase.google.com/docs/reference/rest/auth/#section-sign-in-email-password

And then issuing the upload of the data through another PHP curl using as auth the token received in the first request

I feel like it's the right path but.... I get stuck at the first request with

 404. That’s an error.

The requested URL /identitytoolkit/v3/relyingparty/verifyPassword?key=myapiket was not found on this server. That’s all we know.

I feel like I badly shaped my curl request... any suggestion on how to handle properly the payload?

SOLUTION

See my answer below with steps and PHP code

lui
  • 440
  • 3
  • 16

2 Answers2

5

SOLUTION

Ok, I found the solution to achieve a simple but effective authentication when using Firebase Firestore REST API used through curl PHP.

1- Create a Firebase project

2- In my case I need everybody to be able to read data and only one user to write data. So in "Database/Rules" I've put:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth.uid != null;
    }
  }
}

3- In "Authentication/Sign-In Method" enable "Email/Password" as Sign-In Provider

4- In "Authentication/Users", add a user and provide a pair "email/password"

5- Identify your project API key which can be found in "Project Overview/Settings/General/Web API Key"

6- Issue a curl request to obtain a tokenId which is validate through username and password and use this token to validate the Firestore action. Code below:

<?php 

// 1) Retrieve Authorization TOKEN
// https://firebase.google.com/docs/reference/rest/auth/#section-sign-in-email-password
$firestore_key = "MY_KEY";

$url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword";

$postfields  = array(
        "email" => 'email@domain.com',
        "password" => 'password123456',
        "returnSecureToken" => true
    );

$datapost = json_encode($postfields);

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => array('Content-Type: application/json'),
    CURLOPT_URL => $url . '?key='.$firestore_key,
    CURLOPT_USERAGENT => 'cURL',
    CURLOPT_POSTFIELDS => $datapost
));


$response = curl_exec( $curl );

curl_close( $curl );

$responsJson=json_decode($response,true);

// 2) Add data
//https://medium.com/@aliuwahab/firestore-firebase-php-saving-data-to-a-firestore-database-using-php-curl-2921da3b0ed4

$ID=10000001;

$firestore_data  = [
        "status" => ["integerValue" => 1500]
    ];

$data = ["fields" => (object)$firestore_data];

//    Possible *value options are:
//    stringValue
//    doubleValue
//    integerValue
//    booleanValue
//    arrayValue
//    bytesValue
//    geoPointValue
//    mapValue
//    nullValue
//    referenceValue
//    timestampValue

$json = json_encode($data);

#Provide your firestore project ID Here
$project_id = "myspecific_project"; //found in "Database/Data"

#Provide your firestore documents group
$documents_group = "my_group";

// https://stackoverflow.com/questions/50866734/rest-api-firestore-authentication-with-id-token/50866783#50866783
$url = "https://firestore.googleapis.com/v1beta1/projects/$project_id/databases/(default)/documents/$documents_group/$ID/";

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => array('Content-Type: application/json',
        'Content-Length: ' . strlen($json),
        'X-HTTP-Method-Override: PATCH',
        'Authorization: Bearer '.$responsJson['idToken']        
        ),
    CURLOPT_URL => $url . '?key='.$firestore_key ,
    CURLOPT_USERAGENT => 'cURL',
    CURLOPT_POSTFIELDS => $json
));


$response = curl_exec( $curl );

curl_close( $curl );

// Show result
echo $response . "\n";

?>

NOTE: In the code I marked down links to information that helped me coming out with a working solution

lui
  • 440
  • 3
  • 16
-1

It is not possible to send custom headers with your request. Or more accurately: those headers won't be available in your security rules. See In Firebase Firestore, is there any way to pass info to the Security Rules which is not part of the path?, Accessing Cookies in Google Firestore Rule and How to let specific API to run write/read requests on firestore?.

It is also not possible to hide a single field of a document through security rules. Either a document is completely accessible for the user, or it is completely unaccessible. See Firestore: restricting child/field access with security rules and Securing specific Document fields in Firestore.

Your only real option is to authenticate your PHP script and pass the token into the call, then verify that it is your server making changes.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807