I'm using an ESP32 to upload images to Firebase Storage, but via the underlying Google Cloud Storage JSON API. So, I'm authenticating as a user with the Firebase Rest API and then retrieving the ID token to use for the Storage upload.
But, I'm getting a 401 Unauthorized error when using that ID Token. However, directly replacing the ID token with an OAuth2.0 Token from the Playground, with the scope https://www.googleapis.com/auth/devstorage.read_write
, works fine; I can see the images pop up in the Firebase Console.
I saw this question, where Doug mentions the functionality I'm trying to get but clearly have a bug with.
My code is as follows:
#include "Arduino.h"
#include "globalVars.h"
#include "esp_camera.h"
#include <ArduinoJson.h>
#include <HTTPClient.h>
void checkMotion()
{
delay(10000);
camera_fb_t *fb = esp_camera_fb_get();
uint8_t *fbBuf = fb->buf;
size_t fbLen = fb->len;
uint8_t *jpeg_buf = (uint8_t *)ps_malloc(fbLen);
frame2jpg(fb, 2, &jpeg_buf, &fbLen);
long currTime = millis();
HTTPClient storageClient;
String STORAGE_UPLOAD_PATH = NEW_OBJECT_BASE_PATH + String(currTime) + ".jpeg";
storageClient.begin(STORAGE_UPLOAD_PATH);
Serial.print("uploading to : ");
Serial.println(STORAGE_UPLOAD_PATH);
// Using the ID Token from Auth--doesn't work
Serial.println(idToken); // successfully prints it out
String bearerString = String("Bearer ") + String(idToken);
storageClient.addHeader("Authorization", bearerString);
// Using Playground token instead, and commenting the above--works!
// storageClient.addHeader("Authorization", "Bearer MY_PLAYGROUND_TOKEN_IS_HERE");
// Content type
storageClient.addHeader("Content-Type", "image/jpeg");
storageClient.addHeader("Content-Length", String(fbLen));
int uploadResponseCode = storageClient.POST(jpeg_buf, fbLen);
Serial.print("Response code: ");
Serial.println(String(uploadResponseCode));
String uploadResponseRaw = storageClient.getString();
Serial.println("Received response:" + uploadResponseRaw);
const size_t responseCapacity = JSON_OBJECT_SIZE(100);
StaticJsonDocument<responseCapacity> dUploadResponse;
DeserializationError deserializeResult = deserializeJson(dUploadResponse, uploadResponseRaw);
Serial.println(deserializeResult.c_str());
storageClient.end();
free(jpeg_buf);
}
When using the idToken, this is the response I receive:
{
"error": {
"code": 401,
"message": "Invalid Credentials",
"errors": [
{
"message": "Invalid Credentials",
"domain": "global",
"reason": "authError",
"locationType": "header",
"location": "Authorization"
}
]
}
}
During testing I've also even my Firebase Storage rules to simply allow everything:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if true;
}
}
}
What am I missing? Thanks in advance.