# API Keys

# 1. Summary

API keys allows to define which actions and which indexes are accessible by the holder of an API key. The use of API keys allows to secure the access to the routes in a fine-grained manner of a Meilisearch instance.

# 2. Motivation

To make Meilisearch more reliable for teams and more adapted to production cases, we extend the management and the possibilities of restrictions regarding write and read requests on a Meilisearch instance by introducing a way to manage custom API keys.

# 3. Functional Specification

# 3.1. Glossary

Term Definition
Master Key This is the master key that allows managing API keys. The master key is defined by the user when launching Meilisearch, thus gives access to the /keys API endpoint and requiring requests to be authorized.
API Key API keys are stored and managed from the endpoint /keys by the master key holder.

# 3.2. Explanation

# 3.2.1 Summary Key Points

  • API keys management is restricted to the master key or API keys having keys.get, keys.create, keys.update, keys.delete or * actions.
  • API keys must be provided via the Authorization header using the bearer method to authorize a request.
  • The value of the key field of an API Key is generated from its uid and the master key.
  • When a master key is set at Meilisearch first-launch, it generate two pre-configured default API Key resources. A Default Search API Key authorizing the search action on all indexes and a Default Admin API Key authorizing all actions.
  • If the master-key changes, the key field is re-generated.
  • Default API keys can be modified/deleted from the /keys endpoints but are not re-created if Meilisearch has already created them.
  • API keys can have restrictions on which methods can be accessed via an actions list; they also expiresAt a specific date time and are restricted to a specific set of indexes.
  • name and description fields are the only editable fields of an API key.
  • API key resources are propagated to snapshots and dumps.

# 3.2.2. Master Key

The master key exists to secure a Meilisearch instance. As soon as a master key is set via the MEILI_MASTER_KEY environment variable or the --master-key CLI option, the endpoint /keys is accessible for the master key holder. It can be seen as a super admin key; It must be securely shared only with people who have to manage the security of a Meilisearch instance.

This master key is not an API key, thus is not stored and fetchable from the /keys API endpoint. It must be seen as a runtime lock that activates the security of Meilisearch as soon as an instance is launched with it. The master key should only be used to fetch API Keys the first time. The default Admin API key should be preferred to manage the API keys resources.

At the first launch of Meilisearch with a master key, Meilisearch automatically generates two default API keys to cover the basic needs a user may encounter. It generates a Default Search API Key dedicated to the search that can be used on the client-side and a Default Admin API Key to manipulate a MeiliSearch instance from a backend side.

If the master key is removed at Meilisearch launch, the previously generated API keys no longer secure the Meilisearch instance.

If Meilisearch is launched with the production value for the MEILI_ENV environment variable or the --env CLI option, a master key of at least 16 bytes is mandatory.

If the master key is omitted in that particular case, or is too short, Meilisearch launch is aborted and displays an error.

If Meilisearch is launched with the development value for the MEILI_ENV environment variable or the --env CLI option, Meilisearch displays warning messages given different cases.

See MEILI_MASTER_KEY/--master-key launch option.

The master key must be composed of valid utf-8 characters. It is advisable to enclose it in ' when specified via the --master-key option.

🚨 The master key should never be exposed to the public as it may compromise a Meilisearch instance.

🚨 If the value of the master key changes, all the previously generated API Keys changes, thus allowing to invalidate the set of API keys previously generated by regenerating a different value for their key field. This is particularly useful in the case where the master key might have been leaked and the user needs to re-generate the whole set of keys at once to re-secure the instance.

The master key does not appear on the /keys endpoints and can't be used to authorize requests other than on the /keys endpoint.

The only route not secured in the presence of a master key is the /health route.

# 3.2.3. Default API Keys

The first time a Meilisearch instance is launched with a master key, Meilisearch will generate two API keys described below.

If the user changes the value of the master key later, these two default keys are not created again but the key field is re-generated. However, these two API keys can be updated/deleted using the /keys endpoints.

If these API keys are deleted, the engine should not create them again when Meilisearch is launched again with a master key.

# 3.2.3.1. Default Search API Key

The Default Search API key gives access to the search endpoints on all indexes.

Here is how the Default Search API Key is represented after its generation.

{
    "uid": "01b4bc42-eb33-4041-b481-254d00cce834", //auto-generated value
    "key": "0a6e572506c52ab0bd6195921575d23092b7f0c284ab4ac86d12346c33057f99", //auto-generated value
    "name": "Default Search API Key",
    "description": "Use it to search from the frontend",
    "actions": [
        "search"
    ],
    "indexes": [
        "*"
    ],
    "expiresAt": null,
    "createdAt": "2021-08-11T10:00:00Z",
    "updatedAt": "2021-08-11T10:00:00Z"
}
# 3.2.3.2. Default Admin API Key

The Default Admin API key gives access to all actions by default.

Here is how the Default Admin API Key is represented after its generation.

{
    "uid": "ac06a7e1-6956-4699-bb04-dbeb72a231df", //auto-generated value
    "key": "380689dd379232519a54d15935750cc7625620a2ea2fc06907cb40ba5b421b6f", //auto-generated value
    "name": "Default Admin API Key",
    "description": "Use it for anything that is not a search operation. Caution! Do not expose it on a public frontend",
    "actions": [
        "*"
    ],
    "indexes": [
        "*"
    ],
    "expiresAt": null,
    "createdAt": "2021-08-11T10:00:00Z",
    "updatedAt": "2021-08-11T10:00:00Z"
}

# 3.2.4. API Endpoints Definition

Manipulate API keys of a Meilisearch instance. /keys endpoints are only accessible by the master key holder.

# 3.2.4.1. API Key Resource Representation
field type description
uid string A unique identifier represented by a uuid v4. Can be specified at creation or generated by Meilisearch if ommited.
key string The generated key to use when in the Authorization header when making requests. Generated by MeiliSearch by a combination of uid and the master key.
name string A non unique human readable name to ease identification of the API key. null if empty.
description string A description for the key. null if empty.
actions array A list of actions permitted for the key. ["*"] for all actions. See Actions List Definition part.
indexes array A list of indexes permitted for the key. ["*"] for all indexes.
expiresAt string Represent the expiration date and time as RFC 3339 format. null equals to no expiration time.
createdAt string Represent the date and time as RFC 3339 format when the API key has been created. Generated by MeiliSearch
updatedAt string Represent the date and time as RFC 3339 format when the API key has been updated. Default: Value of createdAt. Generated by MeiliSearch
# 3.2.4.2. GET - /keys

Fetch the API keys of a Meilisearch instance.

# 3.2.4.2.1. Query Parameter Definition
Field Type Required
offset Integer / null false
limit Integer / null false
# offset
  • Type: Integer
  • Required: False
  • Default: 0

Sets the starting point in the results, effectively skipping over a given number of API keys.

  • πŸ”΄ Sending a value with a different type than Integer for offset returns an invalid_api_key_offset error.
# limit
  • Type: Integer
  • Required: False
  • Default: 20

Sets the maximum number of documents to be returned by the current request.

  • πŸ”΄ Sending a value with a different type than Integer for limit returns an invalid_api_key_limit error.
# 3.2.4.2.2. Response Definition

Returns a 200 Success HTTP code when the request is successful.

Field Type Required
results Array of APIKey true
offset Integer true
limit Integer true
total Integer true
# results
  • Type: Array[APIKey]
  • Required: True

An array containing the fetched API keys.

# offset
  • Type: Integer
  • Required: True

Gives the offset parameter used for the query.

# limit
  • Type: Integer
  • Required: True

Gives the limit parameter used for the query.

# total
  • Type: Integer
  • Required: True

Gives the total number of API keys that can be browsed.

API Keys are ordered by createdAt in desc order. (Most recent first)

Expired API keys can be found on the /keys endpoints. An archiving system or a filter could allow to not display them by default. See Future Possibilities part.

# 3.2.4.2.3. Errors
  • πŸ”΄ Accessing this route while a master key is not set for the instance returns a missing_master_key error.
  • πŸ”΄ Accessing this route without the Authorization header returns a missing_authorization_header error.
  • πŸ”΄ Accessing this route without the master key returns an invalid_api_key error.
# 3.2.4.2.4. Example

200 Success

{
    "results": [
        {
            "description": "Manage Products/Reviews Documents API key",
            "uid": "ac06a7e1-6956-4699-bb04-dbeb72a231df",
            "key": "2fcdddd16ab75a4aeea6b74577874bc2888938a69ffafe3d05547560fa72e15b",
            "actions": [
                "documents.add",
                "documents.delete"
            ],
            "indexes": [
                "products",
                "reviews"
            ],
            "expiresAt": "2021-12-31T23:59:59Z",
            "createdAt": "2021-10-12T00:00:00Z",
            "updatedAt": "2021-10-13T15:00:00Z"
        },
        {
            "description": "Default Search API Key (Use it to search from the frontend code)",
            "uid": "87861fb0-e948-41da-ae7f-89617d57d5f5",
            "key": "0fe6fc6d94a21b5ca0b5a714bcb338865108039efc048e99e5ba2e7a976fa330",
            "actions": [
                "search"
            ],
            "indexes": [
                "*"
            ],
            "expiresAt": null,
            "createdAt": "2021-08-11T10:00:00Z",
            "updatedAt": "2021-08-11T10:00:00Z"
        },
        {
            "description": "Default Admin API Key (Use it for all other operations. Caution! Do not share it on the client side)",
            "uid": "ad9af94e-d2db-420f-9ee3-9375f091e565",
            "key": "1846b591d7fd0454bc2b7f1c7ad80c411b1cfe46a51b0d44e6554a30f4bc0a18",
            "actions": [
                "*"
            ],
            "indexes": [
                "*"
            ],
            "expiresAt": null,
            "createdAt": "2021-08-11T10:00:00Z",
            "updatedAt": "2021-08-11T10:00:00Z"
        }
    ],
    "offset": 0,
    "limit": 20,
    "total": 3
}

πŸ‘‰ Note the two default generated API keys here. When a master key is set at MeiliSearch's launch, it generates two pre-configured API Keys. A Default Search API Key restricted to the search action on all indexes and a Default Admin API Key on all indexes to handle all operations (except managing API Keys).

# 3.2.4.3. GET - /keys/:uid_or_key

Fetch a specific API key of a Meilisearch instance from it's uid or key field.

# 3.2.4.3.1. Query Parameter Definition

n/a

# 3.2.4.3.2. Response Definition

Returns a 200 Success HTTP code when the request is successful.

See API Key Resource Representation section for the response body.

# 3.2.4.3.3. Errors
  • πŸ”΄ Accessing this route while a master key is not set for the instance returns a missing_master_key error.
  • πŸ”΄ Accessing this route without the Authorization header returns a missing_authorization_header error.
  • πŸ”΄ Accessing this route without the master key or an API key missing the keys.get permission returns an invalid_api_key error.
# 3.2.4.4. POST - /keys

Create an API key.

# 3.2.4.4.1. Payload Definition
field type required description
uid string Optional A unique identifier represented by a uuid v4 (opens new window). Specified at creation or generated by Meilisearch if ommited.
name string Optional A non unique human readable name to ease identification of the API key. Default: null
description string Optional A description for the API key. Default: null
actions array Required A list of actions permitted for the API key. ["*"] for all actions. See Actions list definition part. The * character can be used as a wildcard when located at the last position. e.g. documents.* to authorize access on all documents endpoints. Default: No default
indexes array Required [*] for all indexes. The * character can be used as a wildcard when located in the last position. e.g. products_* to allow access to all indexes whose names start with products_. Default: No Default
expiresAt string Required The expiration date and time as RFC 3339 format. null equals to no expiration time. Sending only the date part e.g 2021-12-01 leads to having an expiresAt value set to 2021-12-01T00:00:00. Default: No Default
# 3.2.4.4.2. actions List Definition

:authorizedIndexes can be any value extracted from the indexes field of an API key resource.

name description
search Provides access to GET and POST methods on /indexes/:authorizedIndexes/search routes.
documents.add Provides access to POST and PUT on /indexes/:authorizedIndexes/documents routes.
documents.get Provides access to GET methods on /indexes/:authorizedIndexes/documents, /indexes/:authorizedIndexes/documents/:documentId and POST methods on /indexes/:authorizedIndexes/documents/fetch routes.
documents.delete Provides access to DELETE method on /indexes/:authorizedIndexes/documents/:documentId, indexes/:authorizedIndexes/documents/:documentId and POST method on /indexes/:authorizedIndexes/documents/delete-batch and /indexes/:authorizedIndexes/documents/delete routes.
indexes.create Provides access to POST /indexes. ⚠️ indexes field should indicate the newly created index or having [*] to permits access on it..
indexes.get Provides access to GET /indexes and /indexes/:authorizedIndexes. ⚠️Non-authorized indexes are omitted from the response on /indexes.
indexes.update Provides access to PUT /indexes/:authorizedIndexes.
indexes.delete Provides access to DELETE /indexes/:authorizedIndexes.
indexes.swap Provides access to POST /swap-indexes. See Swap Indexes API specification.
tasks.get Provides access to GET /tasks. ⚠️Non-authorized indexes are omitted from the response on /tasks. Also add access to GET /indexes/:authorizedIndexes/tasks routes.
tasks.cancel Provides access to POST /tasks/cancel. route.
tasks.delete Provides access to DELETE /tasks route.
settings.get Provides access to GET /indexes/:authorizedIndexes/settings and /indexes/:authorizedIndexes/settings/* routes.
settings.update Provides access to POST / DELETE /indexes/:authorizedIndexes/settings and /indexes/:authorizedIndexes/settings/* routes.
stats.get Provides access to GET /stats. ⚠️Non-authorized indexes are omitted from the response on /stats. Also add access to GET /indexes/:authorizedIndexes/stats.
metrics.get Provides access to GET /metrics route. A restriction on indexes stops you from calling the route.
dumps.create Provides access to POST /dumps route. As dumps are not scoped by indexes, a restriction on indexes does not affect this action.
snapshots.create Provides access to POST /snapshots route. As snapshots are not scoped by indexes, a restriction on indexes does not affect this action.
version Provides access to GET /version route.
keys.get Provides access to GET /keys route.
keys.create Provides access to POST /keys route.
keys.update Provides access to PATCH /keys routes.
keys.delete Provides access to DELETE /keys routes.
experimental.get Provides access to GET /experimental-features routes.
experimental.update Provides access to PATCH /experimental-features routes.
# 3.2.4.4.3. Response Definition

Returns a 201 Created HTTP code when the request is successful.

See API Key Resource Representation section for the response body.

# 3.2.4.4.3. Errors
# 3.2.4.5. PATCH - /keys/:uid_or_key

Update an API key found by it's uid or key field. Only the name and description fields of an API key can be modified.

# 3.2.4.5.1. Payload Definition
field type required description
name string Optional A name for the API Key. Default: null
description string Optional A description for the API key. Default: null
# 3.2.4.5.2. Response Definition

Returns a 200 Success HTTP code when the request is successful.

See API Key Resource Representation section for the response body.

# 3.2.4.5.3. Errors
# 3.2.4.6. DELETE - /keys/:uid_or_key

Delete an API key found by it's uid or key field.

# 3.2.4.6.1. Payload Definition

n/a

# 3.2.4.6.2. Response Definition

Returns a 204 No-Content HTTP code when the request is successful.

# 3.2.4.6.3. Errors
  • πŸ”΄ Accessing this route while a master key is not set for the instance returns a missing_master_key error.
  • πŸ”΄ Accessing this route without the Authorization header returns a missing_authorization_header error.
  • πŸ”΄ Accessing this route without the master key or an API key missing the keys.delete permission returns an invalid_api_key error.
  • πŸ”΄ Attempting to access an API key that does not exist returns a api_key_not_found error.
# 3.2.4.7. Using an API key on client-code
# 3.2.4.7.1 Authorization Bearer Header

When the Meilisearch API is secured by the presence of a master key, the Authorization header must be used with a bearer to authorize requests. The specified value must be the value of the key field of an API key.

    "Authorization: Bearer `:key`"
    "Content-Type: application/json"
  • πŸ”΄ Accessing a route with an API Key that has expired, been deleted or don't have sufficient permissions returns an invalid_api_key error.

# 4. Technical Aspects

# 4.1. API Key generation

An uid representing by a uuid v4 is generated if not specified at creation by the user.

The final key is then an HMAC with the master key, as the secret, and the uid, a hyphenated Uuidv4, as the data. HMAC uses an SHA-256 algorithm internally.

The final key could be generated with openssl as below:

echo -n $HYPHENATED_UUID | openssl dgst -sha256 -hmac $MASTER_KEY

# 4.2. Synchronous write of API Key resources

Writing to /keys endpoints are synchronous in order to return errors directly to the user when he performs an operation on them. This means that API key management operations do not appear as a task on /tasks.

# 4.3. Propagating API Key to dumps.

The generated API keys must also transit within a dump to facilitate the upgrade of a MeiliSearch instance.

🚨 As a reminder, dumps must be stored in secure areas not accessible to the public or unaccredited persons. In general, you should avoid moving them off the host machine or do so via a secure channel as a security measure.

If the dumps ever leak, the api keys cannot be spoofed from the dump inspection because it needs the master key to have the full value of a valid API key. Only the uid value is propagated in the dumps.

# 4.4. Propagating API Key to snapshots.

The generated API keys must also transit within a snapshot to facilitate the recovery of a MeiliSearch instance.

🚨 As a reminder, snapshots must be stored in secure areas not accessible to the public or unaccredited persons. In general, you should avoid moving them off the host machine or do so via a secure channel as a security measure.

If the snapshot ever leak, the API keys cannot be spoofed from the snapshot inspection because it needs the master key to have the full value of a valid API key. Only the uid value is propagated in the snapshots.

# 4.5. API Keys storage size limit

The maximum size of the API key storage layer is 100GB.

# 5. Future Possibilities

  • Regenerate a specific API Key.
  • Have an "archive" state where manually deleted API Keys can be restored for a certain amount of time.
  • Add rate-limiting per API Key.
  • A restriction on the maximum offset/limit.
  • Add search parameters restrictions for an API Key.
  • Add rfc2822 format expression for expiredAt field. e.g. Wed, 18 Feb 2022 23:16:09 GMT
  • Add an alias that can only be associated to one API Key to retrieve it easily on client side. e.g. GET /keys/:uid_or_alias
  • Supports wildcard expressions at the start/middle of a string.