#23932·harbor

OIDC: admin group membership never persisted for existing user on re-login (only onboarding)

Author: loomenCreated Sep 15, 2026Updated Sep 15, 2026

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

  1. Configure OIDC auth with oidc_admin_group set (e.g. harbor-admin).
  2. 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.
  3. 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).
  4. Log the user out and back in via OIDC (fresh authorization code flow, new tokens issued).
  5. GET /api/v2.0/users?username=<user> still returns admin_role_in_auth: false, sysadmin_flag: false, and update_time unchanged 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:

go
oidc.InjectGroupsToUser(info, u)

which (in src/pkg/oidc/helper.go) correctly computes the flag in memory:

go
user.AdminRoleInAuth = info.AdminGroupMember

But the callback only persists OIDC token metadata afterwards:

go
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_group table gets the up-to-date group list (including the admin group) synced on every login, so the group claim is reaching Harbor correctly.
  • 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.