Unencrypted: maybe a user preference, or first name, or something that adds value but does not overlap with auth. Like if a frontend could serve the same functionality across three departments but the styling is different, the token's unencrypted claims could determine which style set to use.
Encrypted: user uuid, roles, maybe other known settings, things the backend can handle. Someone stealing a token will just try it anyway whether they can see claim or not, but because it's encrypted it will get decrypted so it's also a different logic flow than unencrypted and could trigger a process i.e. did it come from an accepted ip address or range? Does the ip match the previous? Does the ip match the other known ip for a websocket? So also in this sense we wouldn't want anyone to know what else we might check.
How would you do that? At the end of the day the front-end needs to know what you can access somehow.
I guess you could add a compile step that rewrites all your permission checks into validating opaque uuid’s.
The thing is that you cannot change the payload of the token without invalidating it, so you couldn’t set the role to admin and then still have the server accept it.
To answer the question in your other comment, the back end can, in principle, provide a boolean yes/no value for every authorization-related decision the front end needs to make: can_change_order_status, can_view_supervisor_dashboard, can_view_all_conversations, etc. That way you never leak the model (such as roles) underlying those decisions. In practice, I've never seen anybody bother. Instead they have the back end send the front end values that map 1:1 to the back end authorization model: is_supervisor, is_account_admin, etc. That's why I think for most applications it doesn't matter if users roles are encrypted in the JWT.