OIDC: admin group membership never persisted for existing user on re-login (only onboarding)
Summary
When a Harbor user was onboarded via OIDC before being added to the configured OIDC Admin Group, and is later added to that group in the IdP, logging back in does not grant sysadmin. The admin-group membership is correctly recomputed on every login, but the result is never persisted back to the existing user's record — only the OIDC token metadata is updated.
Steps to reproduce
- Configure OIDC auth with
oidc_admin_groupset (e.g.harbor-admin). - Onboard a user via OIDC while they are not yet a member of the admin group. They onboard normally with
sysadmin_flag=false,admin_role_in_auth=false. - Add the user to the admin group in the IdP (verified: the IdP's token/userinfo response for this user now includes the admin group name in the configured group claim).
- Log the user out and back in via OIDC (fresh authorization code flow, new tokens issued).
GET /api/v2.0/users?username=<user>still returnsadmin_role_in_auth: false,sysadmin_flag: false, andupdate_timeunchanged from onboarding.
Expected behavior
Per the original design (#13113): "the change in membership or 'admin group' setting will only be reflected if his ID token is refreshed in Harbor such as re-login" — i.e. re-login should be sufficient to pick up new admin-group membership for an already-onboarded user.
Actual behavior
Re-login never updates the stored admin status for an existing user. Confirmed by reading main at the time of filing:
In src/core/controllers/oidc.go, Callback(), the branch for an existing user (the else after ctluser.Ctl.GetBySubIss succeeds) calls:
oidc.InjectGroupsToUser(info, u)which (in src/pkg/oidc/helper.go) correctly computes the flag in memory:
user.AdminRoleInAuth = info.AdminGroupMemberBut the callback only persists OIDC token metadata afterwards:
if err := ctluser.Ctl.UpdateOIDCMeta(ctx, oidcUser); err != nil {There is no corresponding call that writes AdminRoleInAuth / SysAdminFlag back to the user's row for this (existing-user) branch. So the value computed by InjectGroupsToUser is discarded once the request completes, and the stored user record — which is what GET /users and the actual authorization checks read — never reflects new admin-group membership after the initial onboarding.
This looks distinct from #13977 (fixed in 2.2.0 via #14151), which was about the admin badge not being displayed in the UI — this is about the underlying flag never being persisted for pre-existing users at all.
Environment
- Harbor version: v2.15.2
- OIDC IdP: Authentik (self-hosted), confirmed via direct DB inspection that:
- The provider's "groups" scope mapping is attached and returns
{"groups": [group.name for group in request.user.ak_groups.all()]}. - The user is genuinely a member of the configured admin group in the IdP.
- Harbor's own
user_grouptable gets the up-to-date group list (including the admin group) synced on every login, so the group claim is reaching Harbor correctly.
- The provider's "groups" scope mapping is attached and returns
- External PostgreSQL 18 backend (not the bundled
harbor-db), if relevant.
Workaround
Manually re-issue PUT /users/{id}/sysadmin {"sysadmin_flag": true} after adding a user to the admin group. This has to be repeated any time the flag needs to change, since re-login never fixes it.
Source: goharbor/harbor