#community-help

Trouble with Scoped Search API Keys in Flutter App

TLDR Shane struggled with scoped search API keys in Typesense library for a Flutter app, which returned a 401 error. Jason identified that the error may be a result of an invalid filter within the key, and instructed to create separate keys for different permissions. On implementation, the error was resolved.

Powered by Struct AI

1

19
3mo
Solved
Join the chat
Jul 05, 2023 (3 months ago)
Shane
Photo of md5-0b25f6aaa8aaae5ededba95a4d4d2538
Shane
02:31 PM
I have a Flutter based app where I"m trying to make scope search API keys work, but i don't think that the official Typesense lib supports it. Do i need to call HTTP direct instead of using this lib if i want this security feature? https://pub.dev/packages/typesense
02:38
Shane
02:38 PM
I add the scoped search key where the normal apiKey goes, but get a 401

401: {"message": "Forbidden - a valid `x-typesense-api-key` header must be sent."}

Code block

Future<Configuration> getTypeSenseConfig(Environment environment) async {
    final typeSenseConfig = TypeSenseConfig(environment);

    var generatedScopeKey =
        await callGenerateScopedSearchKey('userId', 'accountId');

    // Create and return the Configuration instance
    final config = Configuration(
      generatedScopeKey,
      nodes: {
        Node.withUri(
          Uri(
            scheme: 'https',
            host: typeSenseConfig.typeSenseHost,
            port: 443,
          ),
        ),
      },
      numRetries: 3, // A total of 4 tries (1 original try + 3 retries)
      connectionTimeout: const Duration(seconds: 2),
    );

    return config;
  }
Jason
Photo of md5-8813087cccc512313602b6d9f9ece19f
Jason
05:01 PM
The client library is agnostic to scoped api keys - it’s just another api key from the client’s perspective
05:02
Jason
05:02 PM
It’s likely that there’s some filter embedded inside the scoped api key that’s invalid
05:02
Jason
05:02 PM
Could you share the code snippet you’re using to generate the scoped api key?
Shane
Photo of md5-0b25f6aaa8aaae5ededba95a4d4d2538
Shane
05:12 PM
this code is in a Cloud Function and is called by the client to get the search key. this will successfully return a key.

// @ts-check
const Typesense = require("typesense");

// Initialize Typesense client
const typesenseClient = new Typesense.Client({
  // Set the appropriate endpoint and API key for your Typesense instance
  nodes: [
    {
      host: "",
      port: 443,
      protocol: "https", // Use 'https' for secure connection
    },
  ],
  apiKey: "redacted",
});

// Function to generate a scoped search key
async function generateScopedSearchKeyForTypesense(userId, accountId) {
  try {
    // Make sure that the parent search key you use to generate a scoped search key
    //  has no other permissions besides `documents:search`

    // Generate a scoped search API key with embedded filter
    const searchKey = "redacted"; // Replace with your main search API key
    const parameters = {
      filter_by: `accountId:=${accountId}`,
    };
    const scopedSearchKey = await typesenseClient.keys().generateScopedSearchKey(searchKey, parameters);

    // Make sure that the parent search key you use to generate a scoped search key
    //  has no other permissions besides `documents:search`

    // Return the scoped search key
    return scopedSearchKey;
  } catch (error) {
    console.error("Error generating scoped search key:", error);
    throw error;
  }
}

module.exports = generateScopedSearchKeyForTypesense;
Jason
Photo of md5-8813087cccc512313602b6d9f9ece19f
Jason
05:17 PM
This comment is the solution:

// Make sure that the parent search key you use to generate a scoped search key
//  has no other permissions besides `documents:search`
05:17
Jason
05:17 PM
It looks like all your API keys have this as the permission:

[ "documents:search", "documents:get" ]
05:18
Jason
05:18 PM
You want to create separate API keys to use with a scoped search api key and the search endpoint (with just the documents:search permission), vs the documents:get endpoint
Shane
Photo of md5-0b25f6aaa8aaae5ededba95a4d4d2538
Shane
05:29 PM
so generate a new api search key that ONLY has documents:search. Okay, i'll try that. thanks

1

06:27
Shane
06:27 PM
you are a wise man, yes that worked! I'm getting this error now, however i'm wondering the best way to generate this:

400: {"message": "No search fields specified for the query."}

I'm using that cloud function in order to generate the scope searched key with the only required parameter (for security purposes) that is required and that is accountId.

I'm taking that scoped security key and using it in my Configuration object from Flutter. Then I'm querying with what the user would be searching. Maybe i move this all to the server side cloud function?

final searchParameters = {
      'q': searchModel
          .searchKey, // Access searchKey property using the null-aware operator
      'per_page': pageSize.toString(),
      'page': (pageKey + 1).toString(),
      'preset': 'inventory_search',
      'filter_by': filterBy,
      "sort_by": "modifiedOn:desc",
      'facet_by': 'category,status',
    };
Jason
Photo of md5-8813087cccc512313602b6d9f9ece19f
Jason
07:18 PM
You also want to specify the query_by field in the search parameters
Shane
Photo of md5-0b25f6aaa8aaae5ededba95a4d4d2538
Shane
09:39 PM
This message contains interactive elements.
Image 1 for This message contains interactive elements.
Jason
Photo of md5-8813087cccc512313602b6d9f9ece19f
Jason
09:39 PM
Oh you’re right - I missed the preset param in your code snippet above. My bad.
09:40
Jason
09:40 PM
Could you give me a curl command that replicates the HTTP 400, with all the search params?
Shane
Photo of md5-0b25f6aaa8aaae5ededba95a4d4d2538
Shane
09:40 PM
yes, give me a few
Jul 06, 2023 (3 months ago)
Shane
Photo of md5-0b25f6aaa8aaae5ededba95a4d4d2538
Shane
12:25 AM
so it looks like it wants to q parameter, but the purpose of this function is get my search scope key, and then call the actual search with that key and a query. is that how it's supposed to work?
Jason
Photo of md5-8813087cccc512313602b6d9f9ece19f
Jason
03:20 AM
&gt; the purpose of this function is get my search scope key
You would use the code in this function to get your search scoped key
03:21
Jason
03:21 AM
And then use that scoped API key to make this search API call