Behaviour of OIDC group sync #4940

Closed
opened 2026-02-05 09:27:39 +03:00 by OVERLORD · 14 comments
Owner

Originally created by @RZR7332 on GitHub (Sep 6, 2024).

Attempted Debugging

  • I have read the debugging page

Searched GitHub Issues

  • I have searched GitHub for the issue.

Describe the Scenario

Hi Dan,

I think this is more a case of me overthinking, or not quite understanding how the whole setup works properly. I migrated my BookStack setup recently to OIDC fairly smoothly, but there is something odd in the behaviour of the group sync which does not make sense to me (likely a user problem here).

From the documentation:

BookStack has the ability to sync OIDC user groups with BookStack roles. By default this will match OIDC group names with the BookStack role display names with casing ignored. This can be overridden by via the ‘External Authentication IDs’ field which can be seen when editing a role while OIDC authentication is enabled.

This to me implies that the group names will be ignored if the External Authentication ID field is set - which it is. I followed the guide to map my already existing user to the user created on Keycloak by using that field, which works perfectly. I have also configured my .env file as below:

! Enable OIDC group sync.
OIDC_USER_TO_GROUPS=true

! Set the attribute from which BookStack will read groups names from.
OIDC_GROUPS_CLAIM=groups

! Additional scopes to send with the authentication request.
! By default BookStack only sends the 'openid', 'profile' & 'email' scopes.
! Many platforms require specific scopes to be requested for group data.
! Multiple scopes can be added via comma separation.
! Comment out unless this is used, as this will break the redirection/integration
! and throw an unknown error which can be viewed in the URL.
!OIDC_ADDITIONAL_SCOPES=roles
! Remove the user from roles that don't match OIDC groups upon login.
! Note: While this is enabled the "Default Registration Role", editable within the 
! BookStack settings view, will be considered a matched role and assigned to the user.
OIDC_REMOVE_FROM_GROUPS=false

This also seems to work correctly as the below is returned from Keycloak after creating a custom attribute and mapping (tested by dumping user details):

"groups": "Admin",

By my logic, this should match the Admin role within BookStack and assign it to my user, which seems to be fine.

My issue comes in when I enable OIDC_REMOVE_FROM_GROUPS - as soon as that is set to true, when I log in my user has no permissions at all - so I am seemingly hitting the default role/permissions here.

Have I missed or misunderstood something in terms of how the process works?

Exact BookStack Version

v24.05.4

Log Content

No response

Hosting Environment

Installed using official installation script.

Originally created by @RZR7332 on GitHub (Sep 6, 2024). ### Attempted Debugging - [X] I have read the debugging page ### Searched GitHub Issues - [X] I have searched GitHub for the issue. ### Describe the Scenario Hi Dan, I think this is more a case of me overthinking, or not quite understanding how the whole setup works properly. I migrated my BookStack setup recently to OIDC fairly smoothly, but there is something odd in the behaviour of the group sync which does not make sense to me (likely a user problem here). From the documentation: _BookStack has the ability to sync OIDC user groups with BookStack roles. By default this will match OIDC group names with the BookStack role display names with casing ignored. This can be overridden by via the ‘External Authentication IDs’ field which can be seen when editing a role while OIDC authentication is enabled._ This to me implies that the group names will be ignored if the External Authentication ID field is set - which it is. I followed the guide to map my already existing user to the user created on Keycloak by using that field, which works perfectly. I have also configured my .env file as below: ``` ! Enable OIDC group sync. OIDC_USER_TO_GROUPS=true ! Set the attribute from which BookStack will read groups names from. OIDC_GROUPS_CLAIM=groups ! Additional scopes to send with the authentication request. ! By default BookStack only sends the 'openid', 'profile' & 'email' scopes. ! Many platforms require specific scopes to be requested for group data. ! Multiple scopes can be added via comma separation. ! Comment out unless this is used, as this will break the redirection/integration ! and throw an unknown error which can be viewed in the URL. !OIDC_ADDITIONAL_SCOPES=roles ! Remove the user from roles that don't match OIDC groups upon login. ! Note: While this is enabled the "Default Registration Role", editable within the ! BookStack settings view, will be considered a matched role and assigned to the user. OIDC_REMOVE_FROM_GROUPS=false ``` This also seems to work correctly as the below is returned from Keycloak after creating a custom attribute and mapping (tested by dumping user details): ` "groups": "Admin",` By my logic, this should match the Admin role within BookStack and assign it to my user, which seems to be fine. My issue comes in when I enable OIDC_REMOVE_FROM_GROUPS - as soon as that is set to true, when I log in my user has no permissions at all - so I am seemingly hitting the default role/permissions here. Have I missed or misunderstood something in terms of how the process works? ### Exact BookStack Version v24.05.4 ### Log Content _No response_ ### Hosting Environment Installed using official installation script.
OVERLORD added the 🐕 Support label 2026-02-05 09:27:39 +03:00
Author
Owner

@ssddanbrown commented on GitHub (Sep 6, 2024):

👋 Hi @RZR7332,

This also seems to work correctly as the below is returned from Keycloak after creating a custom attribute and mapping (tested by dumping user details):

That output isn't what I'd expect from the OIDC system for groups. BookStack expects to see an array of groups under the given claim, instead of the simple single string value that currently appears to be returned there.

@ssddanbrown commented on GitHub (Sep 6, 2024): :wave: Hi @RZR7332, > This also seems to work correctly as the below is returned from Keycloak after creating a custom attribute and mapping (tested by dumping user details): That output isn't what I'd expect from the OIDC system for groups. BookStack expects to see an array of groups under the given claim, instead of the simple single string value that currently appears to be returned there.
Author
Owner

@RZR7332 commented on GitHub (Sep 6, 2024):

Hi Dan,

I omitted a lot of info, the full token looks as below (values redacted):

{ "exp": REDACTED, "iat": REDACTED, "auth_time": REDACTED, "jti": "REDACTED", "iss": "REDACTED", "aud": "bookstack", "sub": "REDACTED", "typ": "ID", "azp": "bookstack", "sid": "REDACTED", "at_hash": "REDACTED", "acr": "1", "email_verified": true, "name": "REDACTED", "groups": "Admin", "preferred_username": "REDACTED", "given_name": "REDACTED", "family_name": "REDACTED", "email": "REDACTED" }

However, I take your point. Based on this, would you agree that group sync is in fact not working at all in my case and login is merely using the group/role which has been statically assigned to the user?

I must have missed something somewhere, will keep reading and digging.

@RZR7332 commented on GitHub (Sep 6, 2024): Hi Dan, I omitted a lot of info, the full token looks as below (values redacted): `{ "exp": REDACTED, "iat": REDACTED, "auth_time": REDACTED, "jti": "REDACTED", "iss": "REDACTED", "aud": "bookstack", "sub": "REDACTED", "typ": "ID", "azp": "bookstack", "sid": "REDACTED", "at_hash": "REDACTED", "acr": "1", "email_verified": true, "name": "REDACTED", "groups": "Admin", "preferred_username": "REDACTED", "given_name": "REDACTED", "family_name": "REDACTED", "email": "REDACTED" }` However, I take your point. Based on this, would you agree that group sync is in fact not working at all in my case and login is merely using the group/role which has been statically assigned to the user? I must have missed something somewhere, will keep reading and digging.
Author
Owner

@ssddanbrown commented on GitHub (Sep 7, 2024):

However, I take your point. Based on this, would you agree that group sync is in fact not working at all in my case and login is merely using the group/role which has been statically assigned to the user?

Yeah, that's likely.

Looking at the answer here it looks like it should be possible via a "Groups Mapper"?:
https://stackoverflow.com/questions/56362197/keycloak-oidc-retrieve-user-groups-attributes

@ssddanbrown commented on GitHub (Sep 7, 2024): > However, I take your point. Based on this, would you agree that group sync is in fact not working at all in my case and login is merely using the group/role which has been statically assigned to the user? Yeah, that's likely. Looking at the answer here it looks like it should be possible via a "Groups Mapper"?: https://stackoverflow.com/questions/56362197/keycloak-oidc-retrieve-user-groups-attributes
Author
Owner

@RZR7332 commented on GitHub (Sep 9, 2024):

Thanks Dan, had a very quick look and it seems similar to what I have done (I added a custom attribute and mapped it to the application). Will set aside some time this week to work through it and see where the mismatch is.

Much appreciated!

@RZR7332 commented on GitHub (Sep 9, 2024): Thanks Dan, had a very quick look and it seems similar to what I have done (I added a custom attribute and mapped it to the application). Will set aside some time this week to work through it and see where the mismatch is. Much appreciated!
Author
Owner

@RZR7332 commented on GitHub (Sep 27, 2024):

As suspected, I was the problem: I was operating under the understanding that as long as the correct string/text was present in the token, it would be mapped correctly - this was a mistake. My initial test for group sync was done with user attributes in Keycloak, which clearly did not work.

Thanks to inspiration from another issue (https://github.com/BookStackApp/BookStack/issues/3004#issuecomment-1197974958), the correct sequence of events should be similar to the below:

  1. Create client-specific role, e.g. Admin.
  2. Map your user to that role.
  3. Under the [client]-dedicated scope, configure a new mapper and choose User Client Role.

This should return a Token Claim Name of resource_access.${client_id}.roles which is the correct and expected claim.

Thanks for the patience and help, hope this helps someone in the future.

@RZR7332 commented on GitHub (Sep 27, 2024): As suspected, I was the problem: I was operating under the understanding that as long as the correct string/text was present in the token, it would be mapped correctly - this was a mistake. My initial test for group sync was done with user attributes in Keycloak, which clearly did not work. Thanks to inspiration from another issue (https://github.com/BookStackApp/BookStack/issues/3004#issuecomment-1197974958), the correct sequence of events should be similar to the below: 1. Create client-specific role, e.g. Admin. 2. Map your user to that role. 3. Under the [client]-dedicated scope, configure a new mapper and choose User Client Role. This should return a Token Claim Name of resource_access.${client_id}.roles which is the correct and expected claim. Thanks for the patience and help, hope this helps someone in the future.
Author
Owner

@ssddanbrown commented on GitHub (Sep 27, 2024):

@RZR7332 Good to hear you found a solution and got things working!

@ssddanbrown commented on GitHub (Sep 27, 2024): @RZR7332 Good to hear you found a solution and got things working!
Author
Owner

@den5o commented on GitHub (Dec 19, 2024):

@RZR7332

Thanks to inspiration from another issue (#3004 (comment)), the correct sequence of events should be similar to the below:

1. Create client-specific role, e.g. Admin.

2. Map your user to that role.

3. Under the [client]-dedicated scope, configure a new mapper and choose User Client Role.

This should return a Token Claim Name of resource_access.${client_id}.roles which is the correct and expected claim.

I tried your instructions and have been unsuccessful in logging in as administrator.

Step 1
image
Step 2
image
Step 3
image
image

Am I missing something?

@den5o commented on GitHub (Dec 19, 2024): @RZR7332 > Thanks to inspiration from another issue ([#3004 (comment)](https://github.com/BookStackApp/BookStack/issues/3004#issuecomment-1197974958)), the correct sequence of events should be similar to the below: > > 1. Create client-specific role, e.g. Admin. > > 2. Map your user to that role. > > 3. Under the [client]-dedicated scope, configure a new mapper and choose User Client Role. > > > This should return a Token Claim Name of resource_access.${client_id}.roles which is the correct and expected claim. I tried your instructions and have been unsuccessful in logging in as administrator. Step 1 ![image](https://github.com/user-attachments/assets/69441c7e-84f3-4e2e-8c0f-ad56a72a9277) Step 2 ![image](https://github.com/user-attachments/assets/ca3388b4-8bc9-4105-a449-f53c4aa9e251) Step 3 ![image](https://github.com/user-attachments/assets/b1f31204-2b85-4793-a9c7-8c43cdcc18e7) ![image](https://github.com/user-attachments/assets/c85defe0-9b99-4666-bb4c-4897393472e6) Am I missing something?
Author
Owner

@RZR7332 commented on GitHub (Dec 20, 2024):

@den5o everything looks okay, apart from the empty Token Claim Name field - not sure why it is blank. Try pasting resource_access.${client_id}.roles into the field and save, see what happens. You may need to enable the debug on BookStack to see what is being returned.

@RZR7332 commented on GitHub (Dec 20, 2024): @den5o everything looks okay, apart from the empty Token Claim Name field - not sure why it is blank. Try pasting resource_access.${client_id}.roles into the field and save, see what happens. You may need to enable the debug on BookStack to see what is being returned.
Author
Owner

@den5o commented on GitHub (Dec 20, 2024):

@RZR7332 I tried resource_access.${client_id}.roles in the Token Claim Name field but results are the same.

It turns out the issue is greater than me not having admin access. All my users including myself, are logged in with the least amount of privilege possible. We can't even create shelves/books, It's view only.

I used APP_DEBUG=true in my compose file but the logs are the same as without it:

[20/Dec/2024:10:48:20 +0000] "GET /login?prevent_auto_init=true HTTP/1.1" 200 3791 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"

[20/Dec/2024:10:48:23 +0000] "POST /oidc/login HTTP/1.1" 302 3644 "https://bookstack.mydomain.tld/login?prevent_auto_init=true" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"

[20/Dec/2024:10:48:36 +0000] "GET /oidc/callback?state=a66372e545017b3d98c6e7ff2b6f32e2&session_state=6b077d5d-103f-418f-8f80-31c38f9ba940&iss=https%3A%2F%2Fauth.mydomain.tld%2Frealms%2Fgfc&code=85229dea-45e6-4dd2-b1fd-d70f72acf588.6b077d5d-103f-418f-8f80-31c38f9ba940.16963e45-cad9-4e36-a71e-bef38dbbc000 HTTP/1.1" 302 1902 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"

[20/Dec/2024:10:48:37 +0000] "GET / HTTP/1.1" 200 5547 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"

[20/Dec/2024:10:48:37 +0000] "GET /uploads/images/user/2024-12/thumbs-30-30/xzc5qsdxjo-avatar.png HTTP/1.1" 200 2398 "https://bookstack.mydomain.tld/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"

Do I need to exec into the container to get the debug logs? If so, how?

@den5o commented on GitHub (Dec 20, 2024): @RZR7332 I tried `resource_access.${client_id}.roles` in the Token Claim Name field but results are the same. It turns out the issue is greater than me not having admin access. All my users including myself, are logged in with the least amount of privilege possible. We can't even create shelves/books, It's view only. I used `APP_DEBUG=true` in my compose file but the logs are the same as without it: ``` [20/Dec/2024:10:48:20 +0000] "GET /login?prevent_auto_init=true HTTP/1.1" 200 3791 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" [20/Dec/2024:10:48:23 +0000] "POST /oidc/login HTTP/1.1" 302 3644 "https://bookstack.mydomain.tld/login?prevent_auto_init=true" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" [20/Dec/2024:10:48:36 +0000] "GET /oidc/callback?state=a66372e545017b3d98c6e7ff2b6f32e2&session_state=6b077d5d-103f-418f-8f80-31c38f9ba940&iss=https%3A%2F%2Fauth.mydomain.tld%2Frealms%2Fgfc&code=85229dea-45e6-4dd2-b1fd-d70f72acf588.6b077d5d-103f-418f-8f80-31c38f9ba940.16963e45-cad9-4e36-a71e-bef38dbbc000 HTTP/1.1" 302 1902 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" [20/Dec/2024:10:48:37 +0000] "GET / HTTP/1.1" 200 5547 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" [20/Dec/2024:10:48:37 +0000] "GET /uploads/images/user/2024-12/thumbs-30-30/xzc5qsdxjo-avatar.png HTTP/1.1" 200 2398 "https://bookstack.mydomain.tld/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" ``` Do I need to exec into the container to get the debug logs? If so, how?
Author
Owner

@den5o commented on GitHub (Dec 20, 2024):

I used OIDC_DUMP_USER_DETAILS=true and got the following response:

...
    "resource_access": {
        "bookstack-client": {
            "roles": [
                "Admin"
            ]
        }
    },
...
@den5o commented on GitHub (Dec 20, 2024): I used `OIDC_DUMP_USER_DETAILS=true` and got the following response: ``` ... "resource_access": { "bookstack-client": { "roles": [ "Admin" ] } }, ... ```
Author
Owner

@den5o commented on GitHub (Dec 20, 2024):

@RZR7332 your original post is based around OIDC user groups and Bookstack roles. Do I need to also configure groups in my keycloak instance for this to work?

@den5o commented on GitHub (Dec 20, 2024): @RZR7332 your original post is based around OIDC user groups and Bookstack roles. Do I need to also configure groups in my keycloak instance for this to work?
Author
Owner

@RZR7332 commented on GitHub (Dec 20, 2024):

That looks correct as well, same as mine. Maybe something missing on the BookStack side? Have you set OIDC_USER_TO_GROUPS and OIDC_GROUPS_CLAIM correctly?

I was on the wrong path with my original post - it seems the Groups thing is just a string in the token rather than actual permissions.

@RZR7332 commented on GitHub (Dec 20, 2024): That looks correct as well, same as mine. Maybe something missing on the BookStack side? Have you set OIDC_USER_TO_GROUPS and OIDC_GROUPS_CLAIM correctly? I was on the wrong path with my original post - it seems the Groups thing is just a string in the token rather than actual permissions.
Author
Owner

@den5o commented on GitHub (Dec 20, 2024):

I've set those to:

OIDC_USER_TO_GROUPS=true
OIDC_GROUPS_CLAIM=groups

but I'm not entirely sure that's the correct config.

OK this was my mistake. I set OIDC_GROUPS_CLAIM=resource_access.${OIDC_CLIENT_ID}.roles and I'm now logged in as admin.

@RZR7332 Thank you so much for helping out.

@den5o commented on GitHub (Dec 20, 2024): ~~I've set those to:~~ ``` OIDC_USER_TO_GROUPS=true OIDC_GROUPS_CLAIM=groups ``` ~~but I'm not entirely sure that's the correct config.~~ OK this was my mistake. I set `OIDC_GROUPS_CLAIM=resource_access.${OIDC_CLIENT_ID}.roles` and I'm now logged in as admin. @RZR7332 Thank you so much for helping out.
Author
Owner

@RZR7332 commented on GitHub (Dec 20, 2024):

I think you fell into the same trap I did, that is what lead me down the groups road in the first place. Basically, that string (OIDC_GROUPS_CLAIM) tells BookStack where to pull the roles from in the token - since there was no groups claim in your token, your user had no roles and thus could do nothing when logged in.

Glad its sorted!

@RZR7332 commented on GitHub (Dec 20, 2024): I think you fell into the same trap I did, that is what lead me down the groups road in the first place. Basically, that string (OIDC_GROUPS_CLAIM) tells BookStack where to pull the roles from in the token - since there was no groups claim in your token, your user had no roles and thus could do nothing when logged in. Glad its sorted!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/BookStack#4940