Page 1 of 1

Problem with firebase REST API in esp-idf. Cannot Patch and Get in firestore

Posted: Tue Apr 09, 2024 8:23 pm
by Thomaspapad
I'm encountering an issue with retrieving and updating documents in Firestore via REST API using ESP-IDF. While the HTTP requests function correctly in Postman, I'm unable to replicate the same functionality within ESP-IDF.

I've successfully authenticated and obtained the id token, but when attempting to use it to perform a GET or PATCH request on a Firestore document, I receive the following error as an HTTP response:

Code: Select all

{
  "error": {
    "code": 401,
    "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "status": "UNAUTHENTICATED"
  }
}
E (5443) HTTP_CLIENT: This authentication method is not supported: Bearer realm="https://accounts.google.com/"
My GET HTTP Request includes the Authorization header in the correct format of Bearer {idToken} as specified in the documentation.

You can refer to the Firebase documentation for more details:

https://firebase.google.com/docs/reference/rest/auth

https://firebase.google.com/docs/firestore/use-rest-api

https://firebase.google.com/docs/firest ... uments/get

https://firebase.google.com/docs/firest ... ents/patch

These HTTP requests function correctly when tested in Postman.

Code: Select all

AUTH GET ID_TOKEN

Method: POST

URL:https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword
keys: key -> value:{firebase Web API Key}
Body:{"email":"","password":"","returnSecureToken":true}

GET FIRESTORE DOCUMENT IFNO

Method: GET

URL:https://firestore.googleapis.com/v1/projects/esp-idf-test/databases/(default)/documents/test/testDoc (test is the collection testDoc is the Document)
keys: NULL
Body: NULL
Authorization: type->Bearer Token   Token->{The token_id you got from the AUTH GET ID_TOKEN}


PATCH FIRESTORE DOCUMENT IFNO

Method: PATCH

URL:https://firestore.googleapis.com/v1/projects/esp-idf-test/databases/(default)/documents/test/testDoc (test is the collection testDoc is the Document)
keys: updateMask.fieldPaths -> value:testString(the name of the string var inside the document testDoc)
Body: {
    "fields": {
        "testString": {
            "stringValue": "helloWorld"
        }
    }
}
Authorization: type->Bearer Token   Token->{The token_id you got from the AUTH GET ID_TOKEN}
Here's the code including Firebase Auth (successfully obtaining the idToken) and Firestore GET HTTP Requests. Ensure ESP is connected to the internet for these functions to work.

Code: Select all

#include "http_requests.h"

char receivedData[4096];
char idToken[1950];
char client2Header[2000];
bool showReceivedData = false;

// Event handler function for HTTP client events
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR:
        printf("HTTP_EVENT_ERROR\n");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        printf("HTTP_EVENT_ON_CONNECTED\n");
        break;
    case HTTP_EVENT_HEADER_SENT:
        printf("HTTP_EVENT_HEADER_SENT\n");
        break;
    case HTTP_EVENT_ON_HEADER:
        printf("HTTP_EVENT_ON_HEADER\n");
        break;
    case HTTP_EVENT_ON_DATA:

        // Find the first occurrence of '}'
        const char *end_ptr = strchr((char *)evt->data, '}');

        if (end_ptr != NULL)
        {
            // Calculate the length until the '}' character
            size_t length = end_ptr - (char *)evt->data + 1; // Include '}' in the length

            // Concatenate only until the '}' character
            strncat(receivedData, (char *)evt->data, length);
            printf("end_ptr is: %p evt-data is : %p length is :%d \n", end_ptr, (char *)evt->data, length);
        }
        else
        {
            // If '}' is not found, just concatenate the entire data
            strncat(receivedData, (char *)evt->data, evt->data_len - 1);
        }

        if (showReceivedData == true){
            printf("evt-data is %s", (char *)evt->data);
        }
            break;
    case HTTP_EVENT_ON_FINISH:
        printf("HTTP_EVENT_ON_FINISH\n");
        break;
    case HTTP_EVENT_DISCONNECTED:
        printf("HTTP_EVENT_DISCONNECTED\n");
        break;
    case HTTP_EVENT_REDIRECT:
        printf("HTTP_EVENT_REDIRECT\n");
        break;
    }
    return ESP_OK; // Return OK status
}

esp_err_t firestore_get_document(const char *id_token)
{
    esp_http_client_config_t config = {
        .url = "https://firestore.googleapis.com/v1/projects/esp-idf-test/databases/(default)/documents/test/testDoc",
        .method = HTTP_METHOD_GET,
        .event_handler = _http_event_handler,
        .transport_type = HTTP_TRANSPORT_OVER_SSL,
        .crt_bundle_attach = esp_crt_bundle_attach,
        .buffer_size = 2048,
        .buffer_size_tx = 4096, // Adjust buffer size as needed
    };

    esp_http_client_handle_t client2 = esp_http_client_init(&config);

    showReceivedData = true;

    // Authentication: Bearer
    strlcpy(client2Header, "Bearer ", sizeof(client2Header));
    strlcat(client2Header, id_token, sizeof(client2Header));

    printf("Header data: %s\n", client2Header);

    // Set headers
    esp_http_client_set_header(client2, "Authorization", client2Header);

    if (!client2)
    {
        return ESP_FAIL;
    }
    memset(receivedData, '\0', sizeof(receivedData));

    // Perform HTTP request
    esp_err_t err2 = esp_http_client_perform(client2);
    if (err2 != ESP_OK)
    {
        printf("problem \n");
    }

    // Clean up
    esp_http_client_cleanup(client2);

    return err2;
}

// Function to authenticate with Firebase using email and password
void firebase_authenticate(const char *email, const char *password, const char *api_key)
{

    // Configure HTTP client
    esp_http_client_config_t config = {
        .url = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword",
        .method = HTTP_METHOD_POST,
        .event_handler = _http_event_handler,
        .user_data = NULL,
        .auth_type = HTTP_AUTH_TYPE_BASIC,
        .transport_type = HTTP_TRANSPORT_OVER_SSL,
        .crt_bundle_attach = esp_crt_bundle_attach,
    };

    // Construct JSON payload for authentication
    char post_data[150];
    snprintf(post_data, sizeof(post_data), "{\"email\":\"%s\",\"password\":\"%s\",\"returnSecureToken\":true}", email, password);

    printf("%s \n", post_data);

    // Initialize HTTP client
    esp_http_client_handle_t client = esp_http_client_init(&config);

    // Set content type header
    esp_http_client_set_header(client, "Content-Type", "application/json");

    char url_with_query[256];
    snprintf(url_with_query, sizeof(url_with_query), "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=%s", api_key);
    // Set the URL for the HTTP request
    esp_http_client_set_url(client, url_with_query);

    // Set post data
    esp_http_client_set_post_field(client, post_data, strlen(post_data));

    printf("\nThe final url with query in order to sign in with email and pass is: \n %s \n", url_with_query);

    char *clientHeader;

    esp_http_client_get_header(client, "Content-Type", &clientHeader);

    printf("The header in order to sign in with email and pass is: \n %s \n", clientHeader);

    char *clientPostData;

    esp_http_client_get_post_field(client, &clientPostData);

    printf("The body in order to sign in with email and pass is: \n %s \n\n", clientPostData);

    // Perform HTTP POST request
    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK)
    {
        printf("we are here\n");

        // Check if response code is 200 OK
        if (esp_http_client_get_status_code(client) == 200)
        {
            printf("Response code of firebase sign in is OK \n");
            cJSON *json = cJSON_Parse(receivedData);
            if (json != NULL)
            {
                cJSON *id_token = cJSON_GetObjectItem(json, "idToken");
                if (id_token != NULL)
                {
                    strncpy(idToken, id_token->valuestring, sizeof(idToken));
                    size_t idToken_len = strlen(idToken);
                    idToken[idToken_len] = '\0'; // Null-terminate the string again
                    printf("ID Token: %s\n", idToken);
                }
                cJSON_Delete(json);
            }
        }
    }
    else
    {
        printf("Something went wrong with client perform");
    }

    // Clean up HTTP client
    esp_http_client_cleanup(client);

    printf("calling the HTTP Request \n");

    firestore_get_document(idToken);
}
I'm encountering an issue with authenticating using the Bearer token. Any insights or suggestions would be greatly appreciated. Thank you.