Skip to content

[🐛] Functions: httpsCallable "Unauthenticated" Error Despite Client Auth (v21.14.0, Android, Live & Emulator) #8492

Open
@rupertpople

Description

@rupertpople

🔥 Issue

Describe your issue here

HTTPS Callable Cloud Functions (onCall, v2 syntax) invoked from a React Native app using @react-native-firebase/functions's httpsCallable method consistently fail with an "unauthenticated" error (Cloud Function logs show request.auth is missing/null). This failure occurs even though the client app explicitly confirms that auth.currentUser is valid and authenticated immediately before making the call.

This issue persists across environments:

  1. When targeting local Firebase Emulators: The call reaches the function emulator, but request.auth is null.
  2. When targeting the live deployed function: The exact same "unauthenticated" error is returned to the client.

Further Context & Troubleshooting:

  • Client Auth Confirmed: console.log(auth.currentUser.uid) before the httpsCallable confirms a valid, logged-in user. Forcing token refresh with getIdToken(true) also succeeds.
  • Basic Connectivity Works: A separate non-authenticated onCall function (testNoAuth) invoked via httpsCallable from the same client code succeeds when targeting the emulator. This proves basic function invocation works.
  • onRequest Workaround Failure (Emulator): Attempting an onRequest function with manual token verification (admin.auth().verifyIdToken) using fetch from the client failed in the emulator. The Functions emulator log showed admin.initializeApp() failed to detect the FIREBASE_AUTH_EMULATOR_HOST environment variable, causing verifyIdToken to fail ("Invalid signature"). This secondary issue might hint at broader problems with SDK/emulator environment interaction.
  • Hypothesis: The @react-native-firebase/functions module (v21.14.0) appears to be failing to automatically attach the necessary authentication context/token from the @react-native-firebase/auth module (v21.14.0) when making httpsCallable requests.

Relevant Client Code Snippet (App.js):

// Import statements (simplified)
import { getApp } from "@react-native-firebase/app";
import '@react-native-firebase/functions'; // Ensure module is loaded
import { auth } from "./firebase/firebaseConfig"; // Firebase Web SDK auth instance

// Function making the call
export async function sendDopamineUpdateToServer(timeData) {
    console.log(`[sendDopamineUpdateToServer ENTRY] Auth State:`, auth.currentUser ? `User ${auth.currentUser.uid}` : 'NULL'); // Confirms auth

    const user = auth.currentUser;
    if (!user) {
        console.error("[sendDopamineUpdateToServer] Skipping: User not authenticated.");
        return { success: false, error: 'User not authenticated', skipped: true };
    }

    // ... payload setup ...
    console.log(`[sendDopamineUpdateToServer] User ${user.uid} authenticated. Proceeding with httpsCallable...`);

    try {
        // Optional: await auth.currentUser.getIdToken(true); // Tried this, also succeeds

        const payload = { /* ... */ };
        console.log(`[sendDopamineUpdateToServer] Calling 'updateDopamineState' with payload:`, payload);

        // Using getApp() as per v6+ recommendations
        const updateFunction = getApp().functions('us-central1').httpsCallable('updateDopamineState');
        const functionResult = await updateFunction(payload);
        console.log("[sendDopamineUpdateToServer] Backend 'updateDopamineState' (onCall) result:", functionResult.data);
        // ... success handling ...

    } catch (error) {
        console.error("[sendDopamineUpdateToServer] Error calling 'updateDopamineState' (onCall):", error);
        // Logs show: code: 'unauthenticated', message: 'The function must be called while authenticated.'
        if (error.code) console.error("Function Error Code:", error.code);
        if (error.message) console.error("Function Error Message:", error.message);
        // ... failure handling ...
    }
}

// Emulator setup (currently commented out for live testing, but issue persists both ways)
/*
import rnAuth from '@react-native-firebase/auth'; // Import for emulator setup
useEffect(() => {
  if (__DEV__) {
    console.log("🔧 DEVELOPMENT MODE: Connecting to Firebase Emulators...");
    try {
      const host = Platform.OS === 'android' ? '10.0.2.2' : 'localhost';
      rnAuth().useEmulator(`http://${host}:9099`); // Using @react-native-firebase/auth for emulator setup
      console.log(`🔧 Auth Emulator -> http://${host}:9099`);
      getApp().functions('us-central1').useEmulator(host, 5001);
      console.log(`🔧 Functions Emulator -> ${host}:5001 (region: us-central1)`);
    } catch (error) {
      console.error("❌ Error connecting to Firebase Emulators:", error);
    }
  }
}, []);
*/

Relevant Backend Code Snippet (functions/index.js):

const { onCall, HttpsError } = require("firebase-functions/v2/https"); // Added HttpsError import
const logger = require("firebase-functions/logger");
// const admin = require("firebase-admin"); // Not needed for the basic onCall check

// admin.initializeApp(); // Initializes correctly in live, has issues detecting AUTH emulator env var

exports.updateDopamineState = onCall({ region: 'us-central1' }, (request) => {
  logger.info("updateDopamineState called. Verifications:", request.verifications);

  // This check fails because request.auth is null/undefined
  if (!request.auth) {
    logger.error("[updateDopamineState] Authentication check failed! request.auth is falsy.", { auth: request.auth });
    throw new HttpsError('unauthenticated', 'The function must be called while authenticated.');
  }

  const userId = request.auth.uid;
  logger.info(`Authenticated user: ${userId}`);
  // ... rest of the function logic ...

  // Example response
  return { success: true, message: `State updated for ${userId}` };
});

// Test function without auth check (works via httpsCallable from client to emulator)
exports.testNoAuth = onCall({ region: 'us-central1' }, (request) => {
  logger.info("testNoAuth called. Data:", request.data);
  return { success: true, message: "Hello from testNoAuth!" };
});

Project Files

Javascript

Click To Expand

package.json:

{
    "name": "dopameter",
    "license": "0BSD",
    "version": "1.0.0",
    "main": "index.js",
    "dependencies": {
        "@notifee/react-native": "^9.1.8",
        "@react-native-async-storage/async-storage": "^2.1.2",
        "@react-native-community/cli-plugin-metro": "^12.3.6",
        "@react-native-community/datetimepicker": "^8.3.0",
        "@react-native-community/netinfo": "^11.4.1",
        "@react-native-firebase/app": "^21.14.0",
        "@react-native-firebase/auth": "^21.14.0",
        "@react-native-firebase/firestore": "^21.14.0",
        "@react-native-firebase/functions": "^21.14.0",
        "@react-native-firebase/messaging": "^21.14.0",
        "@react-navigation/native": "^7.0.14",
        "@react-navigation/stack": "^7.1.1",
        "cors": "^2.8.5",
        "firebase": "^11.4.0",
        "hermes-engine": "^0.11.0",
        "metro-react-native-babel-transformer": "^0.77.0",
        "react": "19.0.0",
        "react-dom": "19.0.0",
        "react-native": "0.78.0",
        "react-native-calendars": "^1.1310.0",
        "react-native-chart-kit": "^6.12.0",
        "react-native-foreground-service": "^1.0.0",
        "react-native-gesture-handler": "^2.24.0",
        "react-native-linear-gradient": "^2.8.3",
        "react-native-progress": "^5.0.1",
        "react-native-reanimated": "^3.17.1",
        "react-native-safe-area-context": "^5.3.0",
        "react-native-screens": "^4.9.2",
        "react-native-svg": "^15.11.2",
        "react-native-svg-transformer": "^1.5.0",
        "react-native-tab-view": "^4.0.5",
        "react-native-vector-icons": "^10.2.0"
    },
    "devDependencies": {
        "@babel/core": "^7.20.0",
        "@babel/preset-flow": "^7.25.9",
        "@babel/preset-react": "^7.26.3",
        "@react-native-community/cli": "^15.0.0",
        "@react-native-community/cli-platform-android": "^11.0.0",
        "@react-native/gradle-plugin": "^0.78.0",
        "@react-native/metro-config": "^0.78.0",
        "metro-react-native-babel-preset": "^0.77.0",
        "patch-package": "^8.0.0",
        "react-native-dotenv": "^3.4.11"
    },
    "private": true,
    "overrides": {
        "metro": "0.78.0",
        "metro-config": "0.78.0",
        "metro-core": "0.78.0",
        "metro-runtime": "0.78.0",
        "metro-resolver": "0.78.0",
        "metro-transform-worker": "0.78.0"
    },
    "scripts": {
        "postinstall": "patch-package"
    }
}
// NOTE: Please also add the firebase-admin version from your functions/package.json
// (Assuming functions/package.json has firebase-admin and firebase-functions)

firebase.json for react-native-firebase v6:

{
  "functions": [
    {
      "source": "functions",
      "codebase": "default",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log",
        "*.local"
      ]
    }
  ],
  "emulators": {
    "auth": {
      "port": 9099
    },
    "functions": {
      "port": 5001
    },
    "ui": {
      "enabled": true
    },
    "singleProjectMode": true
  }
}

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:
# --- PASTE YOUR Podfile HERE IF APPLICABLE ---
# N/A (if not targeting iOS or not relevant)

AppDelegate.m:

// --- PASTE YOUR AppDelegate.m HERE IF APPLICABLE ---
// N/A (if not targeting iOS or not relevant)


Android

Click To Expand

Have you converted to AndroidX?

  • my application is an AndroidX application?
  • I am using android/gradle.settings jetifier=true for Android compatibility?
  • I am using the NPM package jetifier for react-native compatibility?

android/build.gradle:

// --- PASTE YOUR android/build.gradle HERE ---
// N/A (Please provide if possible)

android/app/build.gradle:

// --- PASTE YOUR android/app/build.gradle HERE ---
// N/A (Please provide if possible, especially dependencies section)

android/settings.gradle:

// --- PASTE YOUR android/settings.gradle HERE ---
// N/A (Please provide if possible)

MainApplication.java:

// --- PASTE YOUR MainApplication.java HERE ---
// N/A (Please provide if possible)

AndroidManifest.xml:

<!-- --- PASTE YOUR AndroidManifest.xml HERE --- -->
<!-- N/A (Please provide if possible) -->


Environment

Click To Expand

react-native info output:

System:
OS: Windows 11 10.0.26100
CPU: (12) x64 Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
Memory: 776.73 MB / 15.91 GB
Binaries:
Node:
version: 20.18.3
path: C:\Program Files\nodejs\node.EXE
Yarn:
version: 1.22.19
path: ~\AppData\Roaming\npm\yarn.CMD
npm:
version: 11.3.0
path: C:\Program Files\nodejs\npm.CMD
Watchman: Not Found
SDKs:
Android SDK:
API Levels:
"33"
"34"
"35"
Build Tools:
30.0.3
33.0.0
33.0.2
34.0.0
34.0.0
34.0.0
34.0.0
35.0.0
35.0.0
35.0.0
35.0.0
35.0.0
36.0.0
System Images:
android-35 | Google Play Intel x86_64 Atom
Android NDK: Not Found
Windows SDK:
AllowAllTrustedApps: Disabled
IDEs:
Android Studio: AI-242.23726.103.2422.13103373
Visual Studio: Not Found
Languages:
Java: 17.0.12
Ruby: Not Found
npmPackages:
"@react-native-community/cli":
installed: 15.1.3
wanted: ^15.0.0
react:
installed: 19.0.0
wanted: 19.0.0
react-native:
installed: 0.78.0
wanted: 0.78.0
react-native-windows: Not Found
npmGlobalPackages:
"react-native": Not Found
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: Not found
newArchEnabled: Not found

  • Platform that you're experiencing the issue on:
    • iOS
    • Android
    • iOS but have not tested behavior on Android
    • Android but have not tested behavior on iOS
    • Both
  • react-native-firebase version you're using that has this issue:
    • 21.14.0
  • Firebase module(s) you're using that has the issue:
    • Auth, Functions
  • Are you using TypeScript?
    • N


Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions