>We experimented once with trying to put permissions on a JWT (more complex than your popular scopes) but that makes them grow quickly.
We solved it by simply using bitmasks.
Say, you want to encode an access rule "allows reading from Calendar objects". The typical CRUD actions can be encoded with 4 bits. For example, all bits are zero => no access. The first bit is 1 => can create. The second bit is 1 => can read. Etc.
Then, say, if your system has 32 different types of objects, you can say that, "position 13 encodes for calendars". So you get 32*4 = 128 bits, i.e. just 16 bytes to encode information about CRUD rules for 32 different types of objects.
Sure it sounds complicated but if you move it to a library, you stop thinking about it.
I agree it doesn't work for all cases. In our case, some services can have complex, service-specific access control logic that's hard to express declaratively in a token, so we also have to make some checks by consulting the DB. I don't think that's usually a problem (performance-wise) because we already need to contact the DB anyway - to retrieve the entity to work with (and that has, say, an OwnerID property). The access token helps reduce DB load by skipping general checks ("can the user access calendars in principle?"), and for users who can, we then consult the DB additionally, if the service requires it ("is the user actually the owner of this calendar?" or any other additional access logic). The general case "can the user access calendars in principle?" also allows to hide menu items / return 403 in the UI immediately with zero DB or cache cost.
We solved it by simply using bitmasks.
Say, you want to encode an access rule "allows reading from Calendar objects". The typical CRUD actions can be encoded with 4 bits. For example, all bits are zero => no access. The first bit is 1 => can create. The second bit is 1 => can read. Etc.
Then, say, if your system has 32 different types of objects, you can say that, "position 13 encodes for calendars". So you get 32*4 = 128 bits, i.e. just 16 bytes to encode information about CRUD rules for 32 different types of objects.
Sure it sounds complicated but if you move it to a library, you stop thinking about it.