Multi-Tenant & Teams
Let us know how we can improve our documentation:
Many apps that add chat have customers of their own. If you're building something like Slack, or a SaaS application like Invision you want to make sure that one customer can't read the messages of another customer. Stream Chat can be configured in multi-tenant mode so that users are organized in separated teams that cannot interact with each other.
Teams
Stream Chat has the concept of teams for users and channels. The purpose of teams is to provide a simple way to separate different groups of users and channels within a single application.
If a user belongs to a team, the API will ensure that such user will only be able to connect to channels from the same team. Features such as user search can be configured so that a user can only search for users from the same team.
When enabling multi-tenant mode all user requests will always ensure that the request applies to a team the user belongs to. For instance, if a user from team "blue" tries to delete a message that was created on a channel from team "red" the API will return an error.
Enable Teams for your application
In order to use Teams, your application must have multi-tenant mode enabled. You can ensure your app is in multi-tenant mode by calling the Application Settings endpoint.
const client = new StreamChat('YOUR_API_KEY', 'YOUR_API_SECRET');
// switch your application to v2 permissions
await client.updateAppSettings({
multi_tenant_enabled: true,
});
User teams
When using teams, users must be created from your back-end and specify which teams they are a member of. (Note this is only allowed server side)
// creates or updates a user from backend to be part of the "red" and "blue" teams
client.updateUser({id, teams: ["red", "blue"]});
Channel team
Channels can be associated with a team. Users can create channels client-side but if their user is part of a team, they will have to specify a team or the request will be rejected with an error.
// watches the channel red-general from team red
client.channel("messaging", "red-general", {team: "red"}).create()
// watches the channel red-general from team red
Map<String, Object> extraData = new HashMap<>();
extraData.put("team", "red");
client.createChannel("messaging", "red-general", extraData).enqueue(result -> {
if (result.isSuccess()) {
Channel channel = result.data();
} else {
ChatError error = result.error();
}
return Unit.INSTANCE;
});
// watches the channel red-general from team red
val extraData = mutableMapOf<String, Any>()
extraData["team"] = "red"
client.createChannel("messaging", "red-general", extraData)
.enqueue { result ->
if (result.isSuccess) {
val channel = result.data()
} else {
val error = result.error()
}
}
let channel = Client.shared.channel(type: .messaging, id: "red-general", team: "red")
channel.create { (result) in
// handle result
}
User Search
By default the user search endpoint allows users to search for any other user, applications in multi-tenant mode are required to specify a team filter when performing user searches client-side.
Because users can belong to multiple teams, you need to use the $contains
operator for this match.
// search for users with name Jordan that are part of the red team
client.queryUsers({
$and: [
{ name: { $eq: "Jordan" } },
{ teams: { $contains: "red" } }
],
});
Client.shared.queryUsers(filter: .contains("teams", "blue")) { (result) in
// handle result
}
// search for users with name Jordan that are part of the red team
FilterObject filter = and(
eq("name", "Jordan"),
eq("teams", Filters.contains("red"))
);
int offset = 0;
int limit = 1;
QuerySort sort = null;
boolean presence = false;
client.queryUsers(new QueryUsersRequest(filter, offset, limit, sort, presence)).enqueue(result -> {
if (result.isSuccess()) {
List<User> users = result.data();
} else {
ChatError error = result.error();
}
return Unit.INSTANCE;
});
// search for users with name Jordan that are part of the red team
val filter = and(
eq("name", "Jordan"),
eq("teams", contains("red"))
)
val offset = 0
val limit = 1
client.queryUsers(QueryUsersRequest(filter, offset, limit))
.enqueue { result ->
if (result.isSuccess) {
val users = result.data()
} else {
val error = result.error()
}
}
Query Channels
When using multi-tenant, the query channels endpoint will only return channels that match the query and are on the same team as the user. This happens automatically on Stream Chat side and cannot be circumvented by users.
Server-side you can use query channels to get channels from any team and you can filter them using the team field as well.
// server-side you can query channels from any team
const otherFilters = {};
client.queryChannels({team: "red-team", ...otherFilters}, sort, options);