Securing API Keys with Flutterflow and Firebase
I’ve written this guide on securing API keys with Flutterflow and Firebase Cloud Functions to explore how to handle API requests by encapsulating sensitive data away from client-side exposure. While I will use the Google Maps Distance Matrix API as an example to illustrate the approach, the methods described should be applicable to most API integrations.
This guide shows how to use environment variables and server-side logic, ensuring that key application credentials remain protected. Follow along to learn how to implement these practices in your own projects.
Understanding Built-in Privacy Settings for API Keys in Flutterflow
In the past, there have been concerns and discussions regarding the security of API keys when making API calls directly from client-side applications built with Flutterflow. While Flutterflow provides features to obfuscate headers and hide the endpoint's details, vulnerabilities may still exist if the API key is visible in the payload of the request. Here's a key point from a previous conversation on this topic:
User: "Making the API call private hides the name of where you are making the call to and obfuscates the headers, but you could still see the API key in the payload of the request which any user can steal. This was the case despite also persisting and encrypting the variable on the user's device."
Flutterflow Response: "It doesn’t just obfuscate the headers. It makes sure the 'Authentication' header is no longer set on the frontend and is securely set using a Cloud Function as a middleware."
This exchange highlights the confusion in some places key privacy, and the risk of relying solely on client-side measures for securing API keys. Even with encryption and privacy features, exposing keys directly in the client’s code or through network requests can lead to security breaches.
Alternative Approach Using Firebase Cloud Functions
For developers facing challenges with using Google Cloud Secrets or those who find the setup too cumbersome, a viable alternative is utilizing the built-in features of Cloud Functions within Flutterflow. By leveraging server-side environment variables, developers can ensure that API keys are never exposed in the source code of their application, enhancing overall security. Here’s a brief overview of how this can be achieved:
Firebase Cloud Functions: Use Firebase Cloud Functions to handle API requests. This ensures that all sensitive API calls are made from the server, not the client, effectively reducing the risk of key exposure.
Environment Variables: Store your API keys in environment variables within the Google Cloud Console. This method keeps your keys secure and separate from your application’s repository and client-side environment.
API Gateway: Utilize Cloud Functions as an API gateway where API keys are appended to requests securely on the server side. This hides key management from the client-side and utilizes Firebase's secure backend to handle sensitive data.
Testing and Validation: Regularly test the security and functionality of your API integrations to ensure there are no leaks or vulnerabilities, especially after updates or changes in your application.
Putting The Theory Into Practice
Okay, that is enough about the concepts. Let’s dive right in with a practical example:
Step 1: Set Up Google Maps Distance Matrix API
· Go to the Google Cloud Console at https://console.cloud.google.com/
· Select your project: Choose the project you are working on from the project dropdown.
· Enable APIs and Services: Click on “APIs & Services” and then on “+ ENABLE APIS AND SERVICES”.
· Search for Distance Matrix API: In the search bar, type “Distance Matrix API” and select it.
· Enable the API: Click the “Enable” button to activate the Distance Matrix API for your project.
Step 2: Test the API Using Flutterflow
1. Open Flutterflow: Log into your Flutterflow account.
2. Navigate to API Calls: Select “API Calls” from the left navigation menu.
3. Create a New API Call:
· Click the “+ Add” button and choose “Create API Call”.
· Enter the API Call Name.
· Select the Method Type (GET, POST, etc).
· Enter the API URL of the Google Maps Distance Matrix service.
4. For detailed steps and configurations, refer to Flutterflow’s documentation:
· https://docs.flutterflow.io/data-and-backend/api-calls
· https://docs.flutterflow.io/data-and-backend/api-calls/create-and-test-api-call
Step 3: Enable Google Cloud Functions
1. Access Cloud Functions: In the Google Cloud Console, find “Cloud Functions” under the “Compute” section.
2. Create or Select Function: Either create a new function or select an existing one to modify.
Step 4: Set Up Environment Variables in Google Cloud Console
1. Edit Function: Click on the “Edit” button of your function.
2. Configure Environment Variables:
· Scroll to “Environment variables, networking, timeouts and more”.
· Click to expand and access the “Environment Variables” section.
· Click “Add variable”. Enter GOOGLE_MAPS_API_KEY as the name and paste your API key as the value.
3. Deploy Changes: Click “Save” or “Deploy” to apply changes and redeploy the function.
Step 5: Write your Cloud Function in Flutterflow
Here is an example cloud function, written in the Flutterflow environment which allows you to access the API key securely through the environment variable set up in the previous step.
Note: the error messages are kept to simple numeric return values for the purpose of keeping the code simple to follow.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const https = require('https');
// To avoid deployment errors, do not call admin.initializeApp() in your code
exports.getDistance = functions.region('us-central1').
runWith({
timeoutSeconds: 30,
memory: '128MB'
}).https.onCall(async (data, context) => {
// Write your code below!
if (!context.auth.uid) {
return '101';
}
const origin = data.origin;
const destination = data.destination;
if (!origin || !destination) {
return '102';
}
// Retrieve the API key from environment variables
const apiKey = process.env.GOOGLE_MAPS_API_KEY;
const url = `https://maps.googleapis.com/maps/api/distancematrix/json?origins=${encodeURIComponent(origin)}&destinations=${encodeURIComponent(destination)}&key=${apiKey}`;
return new Promise((resolve) => {
https.get(url, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
const response = JSON.parse(data);
if (response.rows[0] && response.rows[0].elements[0].distance) {
resolve(response.rows[0].elements[0].distance.text);
} else {
console.error('Failed to fetch distance information or no distance available.');
resolve('103'); // Resolve with empty string in case of error or no data
}
} catch (parseError) {
console.error('Error parsing response data:', parseError);
resolve('104'); // Resolve with empty string if there is a parsing error
}
});
}).on('error', (error) => {
console.error('Failed to make API request:', error);
resolve('105'); // Resolve with empty string if there is a request error
});
});
// Write your code above!
}
);
Step 6: Configure package.json in Flutterflow
Ensure that package.json includes all necessary dependencies.
Step 7: Use Cloud Functions in Flutterflow
Navigate to Actions: In Flutterflow, go to any page and select “Actions > Cloud Functions”.
Configure the Function Call: Pass the appropriate parameters (origin and destination) and set up to receive the response as a "Returned Data Type".
Invitation for Community Feedback and Suggestions
I struggled with navigating the setup of Google Cloud Secrets, so opted for this approach. Your comments, feedback, and suggestions are welcome if you’ve got practical ideas on how to set up and use Google Cloud Secrets more practically with Flutterflow. Whether it's sharing your own experiences, challenges, or solutions, your input can greatly benefit the community and help improve the security practices around API key management in Flutterflow projects.