User Permissions

Last Edit: May 05 2020

Stream Chat ships with a configurable permission system that allows high resolution control over what users, guests, admins, and anonymous users are permitted to do.

Permission Checking

Before we dive into more detail on how permission policies are structured, let's explain how permission checking works in general. Every time a user performs an API request the following steps happen:

  1. The policies for the channel type are loaded

  2. The list of policies is iterated based on priority (highest first)

    1. If the policy matches the current request, then the request is either accepted or discarded

    2. If there is not match, continue iterating until one policy matches

Here's what a list of permissions look like:

  1. Admin users can perform any action

  2. Anonymous users are not allowed

  3. Users can modify their own messages

  4. Users can create channels

  5. Members of a channel can read and send messages

  6. Anything not matching the previous list should not be allowed

Let's now see how this would play out and introduce a couple of users: Tommaso and Thierry. Tommaso is an admin and Thierry a user. Thierry is also a member of the "Sailing" channel.

  1. Thierry posts a message on channel "soccer"

  2. Tommaso edits a message from Thierry

  3. Thierry creates a channel called "founders"

  4. An anonymous user opens the "Sailing" channel

Here's what the outcome of these requests would be:

  1. 🙅‍♂️ Only the last policy matches and it denies this action

  2. 👌 Tommaso is an admin, 1st policy is a match

  3. 👌 Thierry is a user, 4th policy is a match

  4. 🙅‍♂️ Anonymous users are discarded

Permission Policy Structure

Now that we have a better idea about how the permission system works, let's define more precisely the structure of a permission policy.

Name API Name Description Example
Name name The name of the policy, this serves as a human readable placeholder to remember the policy is all about "Channel Member Permissions"
Resources resources The list of resources this policy applies. This field cannot be empty but you can set it to <code>[&quot;*&quot;]</code> if you want to match any resource. ["ReadChannel", "SendMessage"]
Roles roles The list of roles this policy applies. Same as the resources field, this must not be empty and can be set to <code>[&quot;*&quot;]</code> to match any user role. ["channel_member"]
Owner owner This is an optional field that you can use to match actions performed on objects that the user owns. Ownership only applies to some resources like messages and channels. When set to true the policy will only match if the user is owner of the resource part for the request (when applicable). true
Action action The action to perform if the policy matches on resource, role and ownership. This field can either be set to 0 or to 1 where 0 stands to Deny and 1 for Allow. 0
Priority priority The priority field defined the position of the policy in the list. Permission policies are evaluated in consistent order from the highest priority to the lowest. 99

Example list

Let's now write the example from before into a list of real policies.

Name Resources Roles Owner action priority
Admin users can perform any action ["*"] ["admin"] - 1 (Allow) 600
Anonymous users are not allowed ["*"] ["anonymous"] - 0 (Deny) 500
Users can modify their own messages ["UpdateMessage"] ["user"] true 1 (Allow) 400
Users can create channels ["CreateChannel"] ["user"] - 1 (Allow) 300
Members of a channel can read and send messages ["ReadChannel", "CreateMessage"] ["channel_member"] - 1 (Allow) 200
Anything not matching the previous list should not be allowed ["*"] ["*"] - 0 (Deny) 100

import { AnyResource, AnyRole, Allow, Deny} from 'stream-chat';

const permissions = [
  new Permission("Admin users can perform any action", 600, AnyResource, ["admin"], false, Allow),
  new Permission("Anonymous users are not allowed", 500, AnyResource, ["anonymous"], false, Deny),
  new Permission("Users can modify their own messages", 400, AnyResource, ["user"], true, Allow),
  new Permission("Users can create channels", 300, AnyResource, ["user"], false, Allow),
  new Permission("Channel Members", 200, ["ReadChannel", "CreateMessage"], ["channel_member"], false, Allow),
  new Permission("Discard all", 100, AnyResource, AnyRole, false, Deny),
];

await client.updateChannelType("messaging", {permissions});
                    

[
  {
    "action":"Allow",
    "name":"Admin users can perform any action",
    "resources":[
      "*"
    ],
    "roles":[
      "*"
    ],
    "owner":false,
    "priority":600
  },
  {
    "action":"Deny",
    "name":"Anonymous users are not allowed",
    "resources":[
      "*"
    ],
    "roles":[
      "anonymous"
    ],
    "owner":false,
    "priority":500
  },
  {
    "action":"Allow",
    "name":"Users can modify their own messages",
    "resources":[
      "*"
    ],
    "roles":[
      "user"
    ],
    "owner":true,
    "priority":400
  },
  {
    "action":"Allow",
    "name":"Users can create channels",
    "resources":[
      "*"
    ],
    "roles":[
      "user"
    ],
    "owner":false,
    "priority":300
  },
  {
    "action":"Allow",
    "name":"Channel Members",
    "resources":[
      "ReadChannel",
      "CreateMessage"
    ],
    "roles":[
      "channel_member"
    ],
    "owner":false,
    "priority":200
  },
  {
    "action":"Deny",
    "name":"Discard all",
    "resources":[
      "*"
    ],
    "roles":[
      "*"
    ],
    "owner":false,
    "priority":100
  }
]