- Title: Tenant Tokens
# Tenant Tokens
# 1. Summary
A Tenant token
is generated by the user code to be used by an end-user when making search queries.
It allows users to have multi-tenant indexes and thus restricts access to documents depending on the end-user making the search request.
A Tenant Token is a JWT containing the information necessary for Meilisearch to verify it and extract permission/rules to apply it to the end user's search.
# 1.1. Key Points
Tenant tokens
are JWTs generated on the user side by using Meilisearch SDKs or their custom code.Tenant tokens
are not stored nor retrievable on the Meilisearch side.Tenant tokens
contain rules that ensure that aTenant token
holder (e.g. an end-user) only has access to documents matching rules chosen at thetenant token
creation.Tenant tokens
are signed from a MeilisearchAPI key
resource on the user's code.Tenant tokens
must not be signed by the master key.Tenant tokens
cannot be more permissive than the signingAPI key
.Tenant tokens
must be signed by anAPI Key
having thesearch
action defined.Tenant tokens
can have different rules for each index accessible by the signing API key. These rules are described in thesearchRules
JSON object.- The only rule available in the
searchRules
object is the search parameterfilter
. Tenant tokens
are sent to Meilisearch via theAuthorization
header like anyAPI Keys
or the master key.- When Meilisearch receives a search query emitted with a
Tenant token
, thetenant token
is decoded, then thesearchRules
are applied for the search request before the search parameters.
# 2. Motivation
Tenant tokens
are introduced to solve multi-tenant indexes use-case.
Multi-Tenant Indexes Definition: It is an index that stores documents that may belong to different tenants. In our case, a tenant within an index can be a user or a company, etc. In general, the data of one tenant should not be accessible by other tenants.
Users today need to set up workarounds to have multi-tenant indexes. They have to use server code to implement the access restriction logic before requesting Meilisearch. It isn't easy to maintain, to implement, and the performance is not optimal because the frontend code does not communicate directly with Meilisearch.
# 3. Functional Specification
# 3.1. Example: Solving Multi-Tenancy with Tenant tokens
Mark
is a developer for a SaaS platform. He would like to ensure that every end-user can only access their documents at search time.
When an end-user registers, Mark's backend code generates a Tenant token
for that end-user so they can only access their documents at search time.
This tenant-token is signed with a Meilisearch API Key so that Meilisearch can ensure that the tenant-token has been generated from a known entity.
Meilisearch checks if the Tenant Token is authorized to make the search request.
Then Meilisearch extracts the Tenant Token's rules to apply for the search request.
# 3.2. Tenant Token
Details
Tenant Tokens are JWTs and must respect several conditions to be understandable by a Meilisearch instance.
# 3.2.1. Header: Algorithm and token type
The Tenant Token must be signed with one of the following algorithms:
HS256
HS384
HS512
e.g. With HS256
{
"alg": "HS256",
"typ": "JWT"
}
The secret key that is used to encrypt the JWT token must be the original API Key value (not the UID).
# 3.2.2. Payload: Data
Meilisearch needs information within the tenant token to check its validity and use it to authorize and perform end-user search requests.
# 3.2.2.1. Validity Information
Fields | Required | Description | Comments |
---|---|---|---|
apiKeyUid (Custom claim) | Required | Must contain the uid field value of the signing Meilisearch API key used to generate the JWT | |
exp (Expiration Time claim) | Optional | A JSON numeric value representing the number of seconds from 1970-01-01T00:00:00Z UTC until the specified UTC date/time. | If the signing API key expires, the Tenant Token also expires. Thus said, the exp can't be greater than the expiration date of the signing API key. |
# 3.2.2.1.1. apiKeyUid
field
apiKeyUid
permits to verify that the signing API key of the Token is known and valid within Meilisearch. It must contain the uid
field value of the Meilisearch API key that generates and signs the Tenant Token.
The apiKeyUid
can't be generated from the master key, and the API Key
must have the search
action defined to generate a usable tenant token.
# 3.2.2.1.2. exp
field
exp
is used to specify the expiration date of the Tenant Token if needed. The format is a JSON numeric value representing the number of seconds from 1970-01-01T00:00:00Z UTC until the specified UTC date/time, ignoring leap seconds.
# 3.2.2.2. Business Logic Information
Fields | Required | Description | Comments |
---|---|---|---|
searchRules | Required | This JSON object contains rules to apply for search queries performed with the JWT depending on the searched index. A Tenant Token cannot access more indexes at search time than those defined as accessible by the signing API key. | Let's say an index uses a field to separate documents belonging to one end-user from another one, but another index needs to separate belonging using a different field in its schema. Defining specific search rules per accessible index avoids generating several tenant tokens for an end-user. |
# 3.2.2.2.1. searchRules
JSON object
searchRules
contains the rules to be enforced at search time for all or specific accessible indexes for the signing API Key.
Here are the accepted formats for the searchRules
property.
In this case, all indexes on which the signing API Key has permissions are searchable by the tenant token without any restrictions.
{
"searchRules": {
"*": {}
}
}
is equivalent to
{
"searchRules": {
"*": null
}
}
is equivalent to
{
"searchRules": ["*"]
}
The search is authorized on all accessible indexes from the signing API Key for the Tenant Token without specific rules.
In this case, all searchable indexes from the signing API Key are searchable by the tenant token, and Meilisearch applies the
filter
search rule before applying the request search parameters.
{
"searchRules": {
"*": {
"filter": "user_id = 1"
}
}
}
In this case, if the
medical_records
index is searchable from the signing API Key, the tenant token is only authorized to search in themedical_records
index.
{
"searchRules": {
"medical_records": {}
}
}
is equivalent to
{
"searchRules": {
"medical_records": null
}
}
is equivalent to
{
"searchRules": ["medical_records"]
}
In this case, if the
medical_records
index is searchable from the signing API Key, the tenant token is only authorized to search in themedical_records
index, and Meilisearch applies thefilter
search rule before applying the request search parameters.
{
"searchRules": {
"medical_records": {
"filter": "user_id = 1"
}
}
}
In this case, if the
medical_records
andmedical_appointments
indexes are searchable from the signing API Key, the tenant token is only authorized to search in those indexes, and Meilisearch applies thefilter
search rule before applying the request search parameters.
{
"searchRules": {
"medical_records": {
"filter": "user_id = 1"
},
"medical_appointments": {
"filter": "user_id = 1 AND accepted = true"
}
}
}
In this case, all searchable indexes from the signing API Key are searchable, and Meilisearch applies the
filter
search rule before applying the request search parameters for all indexes except for themedical_appointments
index. A dedicatedfilter
search rule is applied when making a search query on this index.
{
"searchRules": {
"*": {
"filter": "user_id = 1"
},
"medical_appointments": {
"filter": "user_id = 1 AND accepted = true"
}
}
}
The
filter
field accepts an array, a string, and the mixed syntax as described in the Search Endpoints Specification.
# 3.2.2.3. Payload example
Given a Meilisearch API Key used to sign the JWT from the user code. Here is an example of a valid payload for a tenant token.
e.g. Meilisearch API key: rkDxFUHd02193e120218f72cc51a9db62729fdb4003e271f960d1631b54f3426fa8b2595
{
"apiKeyUid": "f0ec9882-0184-4303-89f0-d4c4d6912bcf", // The uid field value of the signing Meilisearch API Key
"exp": 1641835850, // An expiration date in seconds from 1970-01-01T00:00:00Z UTC
"searchRules": { // The searchRules Json Object definition
"*": {
"filter": "user_id = 1"
}
}
}
In this example,
"*"
allows to specify that no matter which index is searched (among all those accessible by the signing API key that generated the tenant token), thefilter
search rule is applied on all search requests.
# 3.3. Tenant Token Revokation
It is not possible to revoke a specific tenant token.
The only way to do so is to delete the API key that signed it using the DELETE - /keys/:apiKey
endpoints of Meilisearch.
🚨 Doing this revoke all tenant tokens signed by this API Key.
Another much more drastic method is to modify the master key
of the Meilisearch instance.
🚨🚨 Doing this regenerate all the API Keys and thus revoke all the tenant tokens generated regardless of the signing API Key.
# 4. Future Possibilities
- Handle more signing methods for the Tenant Token.
- Handle more search parameters restrictions in
searchRules
. - Add a possibility to revoke a specific Tenant Token.
- Introduce an endpoint to generate tenant tokens on the Meilisearch side.