I'm having some issues implementing a secured search with Firebase and Algolia. I've been following the guide at https://firebase.google.com/docs/firestore/solutions/search -> Adding security
My problem is that everything seems to be working fine, however I do not receive any search hits.
When a user searches for an item, it invokes a function which first hits a firebase function to generate a Secured API Key, which also includes the security filter for the queries.
The firebase function:
app.use(getFirebaseUser);
app.get('/', (req, res) => {
// @ts-ignore
const uid = req.user.uid;
// Create the params object as described in the Algolia documentation:
// https://www.algolia.com/doc/guides/security/api-keys/#generating-api-keys
const params = {
// This filter ensures that only documents where author == uid will be readable
filters: `ownerID:${uid}`,
// We also proxy the uid as a unique token for this key.
userToken: uid,
};
// Call the Algolia API to generate a unique key based on our search key
const key = client.generateSecuredApiKey(ALGOLIA_SEARCH_KEY, params);
// Then return this key as {key: '...key'}
res.json({key});
});
This is literally copy/pasted from the firebase example, except I've replaced "author" with "ownerID", which is the term I want to match in my indices.
The getFirebaseUser() is an exact copy/paste from the official firebase example.
Once the client has received the secured API key, it get passed to algolia with the query:
const getSearchResults = async (query: string) => {
const index = client.initIndex('things');
try {
const userToken = await auth.currentUser?.getIdToken();
const response = await fetch(GOOGLE_URL, {
headers: { Authorization: `Bearer ${userToken}` },
});
const data = await response.json();
const client = algoliasearch(ALGOLIA_APP_ID, data.key);
const responses = await index.search(query);
console.log(responses);
} catch (err) {
console.error(err.message);
}
};
This is also the same function as the example, except written with async/await. (I did try an exact copy/paste, and the result is the same). Also, the example shows index.search({query}) where the query string gets passed in as an object. This does not work either, and Typescript states that a string is expected.
If I try to change the initIndex to an index that doesn't exist, I get an error stating that it doesn't exist. This tells me the connection between my app and algolia is working as expected.
The returned object looks like this:
exhaustiveNbHits: true
hits: []
hitsPerPage: 20
nbHits: 0
nbPages: 0
page: 0
params: "query=test&filters=ownerID%3AKK3oXEkrIKb31BECDSJPMdgvTBz2&userToken=KK3oXEkrIKb31BECDSJPMdgvTBz2"
processingTimeMS: 1
query: "test"
If I do a regular search:
const getSearchResults = async (query: string) => {
const client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY);
const index = client.initIndex('things');
const responses = await index.search(query);
console.log(responses);
};
I do get hits:
exhaustiveNbHits: true
hits: Array(1)
0: {productName: "test ja", purchaseDate: "15.03.2021", purchasedFrom: "ok", ownerID: "KK3oXEkrIKb31BECDSJPMdgvTBz2", objectID: "25BadjQxUjRjkjH9ZnwW", …}
length: 1
__proto__: Array(0)
hitsPerPage: 20
nbHits: 1
nbPages: 1
page: 0
params: "query=test"
processingTimeMS: 1
query: "test"
This is literally bypassing the whole cloud function, which means the problem must be with generating the Secured API Key, the parameters or something.
The index data stored in Algolia looks like this:
objectID "25BadjQxUjRjkjH9ZnwW"
productName "test ja"
purchaseDate "15.03.2021"
purchasedFrom "ok"
ownerID "KK3oXEkrIKb31BECDSJPMdgvTBz2"
Which means I should get hits on ownerID, which I have in the cloud function.
EDIT: When setting the filter from the parameter object to an empty string, I receive search hits. There might be something wrong with the way the filter is implemented.
Thanks for looking into this. I would appreciate all the help I can get!
Stephan Valois