# Granular group-based permissions for anonymous and logged in users

**URL:** https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273
**Category:** Announcements
**Tags:** groups
**Created:** [13 במאי,‏ 2026,‏ 1:18am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273 "2026-05-13T01:18:57Z")
**Posts on this page:** 20
**Page:** 2

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [19 ביולי,‏ 2026,‏ 11:32pm UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/23 "2026-07-19T23:32:22Z")

</div>

No problem! Glad the `disallowed_groups` will be helpful. I’ve merged that PR now.

I need to take a pass through all our official themes & components now that we have `disallowed_groups` and `resolve_group_memberships` available, I suggest you and @moin do the same when you can for your own themes and components, as after I’ve made changes to our official repos I would really like to move forward with making the upcoming change from the OP `stable` .

There is a lot of other core work now relying on/using `anonymous_users` and `logged_in_users` and I’d really like to delete the `everyone` group.

---

<div class="post-metadata">

### Author: ![Lilly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/32/575047_2.png) [@Lilly](https://meta.discourse.org/u/Lilly)
#### Post date: [20 ביולי,‏ 2026,‏ 12:30am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/24 "2026-07-20T00:30:29Z")

</div>

hey Martin - question:

i just did a full update of my instance, and i am now adding the `disallowed_groups` object setting to my component for `everyone` and `anonymous_users` based on the auto group ids here:

> <https://github.com/discourse/discourse/blob/fc8df41adbc81a773763480b7efa8c1cf7cee58c/app/models/group.rb#L107-L118>

like this:

```yml
      groups:
        type: groups
        disallowed_groups: "0|4"
        required: true
        resolve_group_membership: true
        validations:
          max: 20

```

but it is still showing `everyone` in the component group dropdown setting:

 ![Screenshot 2026-07-19 at 5.21.44 PM](https://global.discourse-cdn.com/meta/original/4X/4/6/0/460a463c4a26a3b89888aae97acd2325c33ca05b.png)

what am i doing wrong in the object setting? i notice that even without the `disallowed_groups` in the objects, it still doesn’t show the `anonymous_users` group (so at the moment, there is no difference between the list in my component whether i set `disallowed_groups` or not). i tested with other group ids and i must be doing something wrong with how i am using `disallowed_groups` (or syntax) because it doesn’t seem to have any effect regardless of which ones i use.

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [20 ביולי,‏ 2026,‏ 4:17am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/25 "2026-07-20T04:17:22Z")

</div>

Oh there were some GitHub issues earlier in the day, so only just now has `disallowed_groups` change worked its way into latest [Commits · discourse/discourse · GitHub](https://github.com/discourse/discourse/commits/latest)

Not completely sure that will be the issue, but can you try updating again and see if it persists? If not LMK and point me to your theme component (or is it just your group sidebar one?) so I can debug 🙂

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [20 ביולי,‏ 2026,‏ 6:19am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/26 "2026-07-20T06:19:17Z")

</div>

> [@Lilly](#):
>
> i notice that even without the `disallowed_groups` in the objects, it still doesn’t show the `anonymous_users` group (so at the moment, there is no difference between the list in my component whether i set `disallowed_groups` or not)

I think that was because the upcoming change was disabled on the forum you used for testing. I enabled it and now it’s visible.

 ![Screenshot_20260720_081640_Firefox](https://global.discourse-cdn.com/meta/original/4X/4/c/e/4ce07f8b40985e1be8de0174a439952323c785b9.jpeg)  
I also updated the forum, and `everyone` and `anonymous_users` are hidden as expected.

* * *

To clarify: I’m the one who disabled the change about two weeks ago 😇

---

<div class="post-metadata">

### Author: ![Lilly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/32/575047_2.png) [@Lilly](https://meta.discourse.org/u/Lilly)
#### Post date: [20 ביולי,‏ 2026,‏ 2:10pm UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/27 "2026-07-20T14:10:37Z")

</div>

hah 😃 thanks Moin! i actually forgot that the new granular group setting was in upcoming changes anyways.

Martin, the `disallowed_groups` object setting works perfectly. i really like this change. thanks again Team - great improvement. :discourse: :chefs_kiss:

---

<div class="post-metadata">

### Author: ![satonotdead](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/satonotdead/32/447830_2.png) [@satonotdead](https://meta.discourse.org/u/satonotdead)
#### Post date: [5 באוגוסט,‏ 2026,‏ 3:05pm UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/28 "2026-08-05T15:05:41Z")

</div>

May I know the CSS class for anonymous and registered users? I’m not using their internal IDs because I’m on pure CSS.

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [6 באוגוסט,‏ 2026,‏ 2:21am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/29 "2026-08-06T02:21:21Z")

</div>

We don’t add these CSS classes to the body by default, are you referring to [CSS Classes for Current User's Groups](https://meta.discourse.org/t/css-classes-for-current-users-groups/226068) ?

That component needs to be updated to add `group-anonymous` or `group-logged-in-users` depending on whether there is a current user or not.

---

<div class="post-metadata">

### Author: ![Lilly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/32/575047_2.png) [@Lilly](https://meta.discourse.org/u/Lilly)
#### Post date: [6 באוגוסט,‏ 2026,‏ 2:52am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/30 "2026-08-06T02:52:25Z")

</div>

Heya Martin 👋

i opened a quick PR to add those two classes (`anonymous_users` and `logged_in_users`) to the theme component, but i don’t think it is really necessary (see my note below).

> <https://github.com/discourse/discourse-groups-css-classes-in-body/pull/37>
>
> There are new granular groups for \`logged\_in\_users\` and \`anonymous\_users\` in cor…e now as seen here:
> 
> https://github.com/discourse/discourse/blob/2f70686a3fe4d183d6c643489ec2140a75103cbe/app/models/group.rb#L107-L119
> 
> This PR adds a check to see if the user exists (is logged in) or not (anonymous) and adds classes to the body tag for either. 
> 
> Files touched:
> 
> \`javascripts/discourse/api-initializers/init-theme.js\`
> 
> \*\*\* 
> 
> worth noting that this component isn't necessary to achieve group class css code changes for anonymous and logged in users:
> 
> https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/30

i haven’t really tested it (lol) but i think it’s pretty straight-forward. the code just checks if the current user exists, and if they do, then they are a member of `logged_in_users`, and if not, then they are `anonymous_users`. 😁

**note:** you can do this sort of anon/logged in css without that component. i’m pretty sure Discourse automatically adds `.anon` anyways, so the css for anonymous vs logged in can be achieved without installing the component, but the PR simply adds code to use the new group conventions.

for example, something like this:

```css
// for anonymous users
@if $hide_this_from_anon {
  html.anon {
    .something {
      display: none;
    }
  }
}

// for logged-in users
@if $hide_this_from_logged_in_users {
  html:not(.anon) {
   .something {
      display: none;
    }
  }
}

```

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [6 באוגוסט,‏ 2026,‏ 6:31am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/31 "2026-08-06T06:31:45Z")

</div>

> [@Lilly](#):
>
> **note:** you can do this sort of anon/logged in css without that component. i’m pretty sure Discourse automatically adds `.anon` anyways, so the css for anonymous vs logged in can be achieved without installing the component, but the PR simply adds code to use the new group conventions.

Oh yeah you’re right, I didn’t notice that before, I was only looking at `<body>` :

> <https://github.com/discourse/discourse/blob/f5af6f99db10bead1e13cba3b18ab3d3aeb87f65/app/helpers/application_helper.rb#L213>

I approved your PR for the component anyway, I think that’s fine 🙂

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [6 באוגוסט,‏ 2026,‏ 6:33am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/33 "2026-08-06T06:33:41Z")

</div>

Also FYI all, I hadn’t posted here yet, but I’ve merged these PRs for official components to use `resolve_group_membership` and `disallowed_groups`:

- [DEV: Add group-based setting metadata - Pull Request #23 - discourse/discourse-require-edit-reason - GitHub](https://github.com/discourse/discourse-require-edit-reason/pull/23)
- [DEV: Add group-based setting metadata - Pull Request #60 - discourse/discourse-unanswered-filter - GitHub](https://github.com/discourse/discourse-unanswered-filter/pull/60)
- [DEV: Add group-based setting metadata - Pull Request #76 - discourse/discourse-gated-topics-in-category - GitHub](https://github.com/discourse/discourse-gated-topics-in-category/pull/76)
- [DEV: Add group-based setting metadata - Pull Request #48 - discourse/discourse-whisper-warning - GitHub](https://github.com/discourse/discourse-whisper-warning/pull/48)

I’m working on a plan for next steps of this upcoming change now, I think there are still some places in the core/plugin codebases looking at `everyone` directly or not using `user.in_any_groups?` on the server side.

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [12 באוגוסט,‏ 2026,‏ 10:41pm UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/34 "2026-08-12T22:41:05Z")

</div>

> [@martin](#):
>
> I approved your PR for the component anyway, I think that’s fine 🙂

Is there a reason why this hasn’t been merged yet?

* * *

The reason I opened this topic is that I finally updated my component

> [@martin](#):
>
> > [@Moin](#):
> >
> > The default setting for the default filters is “everyone,” because I find it helpful for even unregistered users to be able to see and use the default filters. The problem is that `everyone` changes to ‘logged\_in\_users’ even though I specifically selected it. Do I need to create a custom migration for this so that admins currently using `everyone` will continue to have filters for unregistered users in the future? When does this migration need to take place? Or does every admin need to change this individually after you have run the migration?
> 
> I would change the default of `default_favorite_filters_groups` to `4|5` in your theme component, which is logged in & anonymous users, rather than `0 (everyone)` which is going away soon.

I did that. But I still have the impression that it helps only for forums adding the component after I changed this. On those already using it, the new default isn’t applied (which is usually good!). So I still see the problem that there is an unexpected change in behavior for those already using the component.

Also do you know what happens if an admin configured a setting with a group which I add as a disallowed group in my update?

Since I don’t think the component is used on many forums I wasn’t too worried and merged anyways, but both the migration and setting disallowed groups might also be relevant to other theme developers

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [13 באוגוסט,‏ 2026,‏ 4:28am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/35 "2026-08-13T04:28:16Z")

</div>

> [@Moin](#):
>
> > [@martin](#):
> >
> > I approved your PR for the component anyway, I think that’s fine 🙂
> 
> Is there a reason why this hasn’t been merged yet?

Nope, for some reason I guess my brain thought this was not a PR on the discourse org 🤦‍♂️ Will merge shortly after the CI checks run.

> [@Moin](#):
>
> I did that. But I still have the impression that it helps only for forums adding the component after I changed this. On those already using it, the new default isn’t applied (which is usually good!). So I still see the problem that there is an unexpected change in behavior for those already using the component.
> 
> Also do you know what happens if an admin configured a setting with a group which I add as a disallowed group in my update?

For cases like these and others in future, I think the best way is probably writing a migration on a per theme/component basis [Migrate Discourse theme settings](https://meta.discourse.org/t/migrate-discourse-theme-settings/287783)

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [13 באוגוסט,‏ 2026,‏ 6:54am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/36 "2026-08-13T06:54:17Z")

</div>

I asked when this would have to happen in my post above. Migrating without being sure the new groups work on all forums could break stuff too, and admins can toggle the change on and off. So it feels impossible to migrate at the right time.

---

<div class="post-metadata">

### Author: ![gormus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/gormus/32/428592_2.png) [@gormus](https://meta.discourse.org/u/gormus)
#### Post date: [13 באוגוסט,‏ 2026,‏ 8:41am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/37 "2026-08-13T08:41:27Z")

</div>

Apologies if this was already mentioned, and I missed it…

I realized, if `resolve_group_membership` is included in the settings, I can only access the boolean value via the `user_in_` prefix, but no longer can access the raw value of the setting field.

```yml
aabbccdd_allowed_groups:
  refresh: true
  default: "1|2"
  type: list
  list_type: group
  resolve_group_membership: true

```

```js
console.log(settings.aabbccdd_allowed_groups); // undefined

console.log(settings.user_in_aabbccdd_allowed_groups); // true or false

```

Is this intentional?

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [13 באוגוסט,‏ 2026,‏ 9:07am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/38 "2026-08-13T09:07:27Z")

</div>

I think so. Otherwise, the solution to this bug could have been different.

> [@Fallback for \`user\_in\_X\` theme setting support fails when resolved value is false](https://meta.discourse.org/t/fallback-for-user-in-x-theme-setting-support-fails-when-resolved-value-is-false/408524/3):
>
> The fallback should only happen when `Object.hasOwn(settings, "user_in_copy_button_allowed_groups")` is false

It also makes sense to me. `user_in_x` also checks groups the frontend doesn’t know about, because the group is only visible to admins or its visibility is limited by default, like `everyone`. So you get different results depending on what you use, so combining the two could have unexpected consequences.

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [16 באוגוסט,‏ 2026,‏ 11:32pm UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/39 "2026-08-16T23:32:05Z")

</div>

Thanks Moin, this is exactly right. @gormus the only place that the actual group IDs come through still is in the admin UI for the theme settings.

> [@Moin](#):
>
> Migrating without being sure the new groups work on all forums could break stuff too, and admins can toggle the change on and off.

Not sure if this is clear based on what I’ve posted before, but the `anonymous_users` and `logged_in_users` are usable at any time without this upcoming change being enabled, I added them independently months ago. This is the main thing the upcoming change does:

> [@martin](#):
>
> When the upcoming change is enabled, any setting with `everyone` as a selected group will have it automatically translated to the `logged_in_users` ID, so no data in the site settings table will be changed when toggling the upcoming change. When the upcoming change becomes Permanent, we will do a data migration for all group settings to make this change.

So either way, you are safe if you get rid of `everyone` everywhere it’s used and only use `anonymous_users` and `logged_in_users` from now on.

Today I plan to figure out a plan of the remaining work I need to do to truly get rid of `everyone` for **site settings** (leaving category settings alone at the moment), and I will post it here, so hopefully that will help stay on the same page going forward, and I can keep updating this plan as I go.

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [17 באוגוסט,‏ 2026,‏ 12:10am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/40 "2026-08-17T00:10:26Z")

</div>

> [@martin](#):
>
> the `anonymous_users` and `logged_in_users` are usable at any time without this upcoming change being enabled, I added them independently months ago.

But the groups aren’t visible in the interface if the change is disabled. Admins cannot change a setting to those groups. So when I add “everyone” as a disallowed\_group, they no longer see any groups which allow them to set up a component to be visible for visitors. For me, “usable” implies not only working but also being visible. Since it’s still possible to disable the change, I wouldn’t call relying only on the new groups “safe”.

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [17 באוגוסט,‏ 2026,‏ 4:04am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/41 "2026-08-17T04:04:08Z")

</div>

Hmm you’re right. There are a couple more things I need to fix when the upcoming change is disabled for this `disallowed_group` problem to go away:

1. `anonymous_users` and `logged_in_users` aren’t in the group picker at all. I think it’s safe to allow these here now, then it won’t matter if you’ve added `everyone` to `disallowed_groups` if the upcoming change is turned off.
2. Fix `Guardian::AnonymousUser#in_any_groups?` to respect `anonymous_users` with the upcoming change turned off.
3. Add the same read time aliasing of `0 (everyone)` → `5 (logged_in_users)` for theme settings, which we do for site settings.

I think I might also mark `everyone` with `(legacy)` in the group selector(s) while the upcoming change is still optional, and disabled.

Will prioritise these ones, and add them to my overall plan I’m working on.

---

<div class="post-metadata">

### Author: ![martin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/martin/32/491371_2.png) [@martin](https://meta.discourse.org/u/martin)
#### Post date: [17 באוגוסט,‏ 2026,‏ 5:44am UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/42 "2026-08-17T05:44:35Z")

</div>

I’ve made a dedicated topic here @moin [The road to stable, then permanent, for granular\_anonymous\_and\_logged\_in\_groups\_permissions](https://meta.discourse.org/t/the-road-to-stable-then-permanent-for-granular-anonymous-and-logged-in-groups-permissions/410178) . The OP is incomplete, I’m still going through all the cases locally, I will keep updating it. I don’t mind if you keep posting in this topic, but I would prefer if we can do further discussions in the new one, so I can quote parts of the OP or add to it as appropriate.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [7 בספטמבר,‏ 2026,‏ 8:58pm UTC](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/43 "2026-09-07T20:58:45Z")

</div>

wait just seen this.

so some people were confused by everyone meaning just logged in users?

who?!

everyone is everyone surely - very clear.

everyone is everyone who is hitting the site, logged in or no, surely that’s simple?

so now a Category that is fully public has to have a minimum of two groups now instead of one - logged in and anon? that’s silly and not an upgrade?

and if not, and you only have to put “anon” because that’s a synonym for “everyone” - that’s no longer correct as logged in users are _not_ anon.

* * *

where there was some “learning” around discourse was TL0 in some cases meaning “all those who have an account and logged in” but could also mean “those who have not yet reached TL1 but have an account and are logged in”

**the key here is that it didn’t represent a single group of people, it represented a _threshold_ and that was key.**

by changing just TL0 to “logged in users” you now break the consistency of each security level being a threshold. TL1 is _also_ a threshold and not really a “single group”. so does that mean we need a group called “logged in users who are at least trust level 1”?!

I’m not at all convinced any of this needed changing - change for change sake?

Very confusing and access control is a _very_ sensitive part of the app!

This is messing with some really long term staple Discourse norms which have stood the test of time because they were correct in the first place (imho)

[Previous page](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273.md?page=1)

[Next page](https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273.md?page=3)
