iam icon indicating copy to clipboard operation
iam copied to clipboard

Conflict between oidc-agent behavior and IAM parametric scope policy filtering

Open federicaagostini opened this issue 6 months ago • 2 comments

When a deny policy to storage.*:/ scope is defined for all users and an allowed policy for scope with a sub-path is defined, e.g.

{
    "id": 2,
    "description": "Deny access to storage.* scopes",
    "creationTime": "2023-03-01T14:37:13.000Z",
    "lastUpdateTime": "2023-03-01T14:37:13.000Z",
    "rule": "DENY",
    "matchingPolicy": "PATH",
    "account": null,
    "group": null,
    "scopes": [
      "storage.create:/",
      "storage.read:/",
      "storage.modify:/"
    ]
  },
  {
    "id": 18,
    "description": "Test storage restriction",
    "creationTime": "2022-07-21T23:11:45.000+02:00",
    "lastUpdateTime": "2022-07-21T23:11:45.000+02:00",
    "rule": "PERMIT",
    "matchingPolicy": "PATH",
    "account": {
      "uuid": "819bc496-e4cc-4a5d-832b-f349604376bd",
      "username": "vokacpet",
      "location": "https://wlcg.cloud.cnaf.infn.it/scim/Users/819bc496-e4cc-4a5d-832b-f349604376bd"
    },
    "group": null,
    "scopes": [
      "storage.modify:/test",
      "storage.read:/test",
      "storage.create:/test"
    ]
  }

this generates a conflict if a user (including admins) registers a client trough oidc-agent with generic storage.*:/ scope. In case the user registers the oidc-agent client with specific e.g. storage.*:/test scope there is no conflict and the scope is assigned to the client. In fact, the IAM scope policy filtering happens at the consent phase, meaning that the scopes showed during the consent page are already filtered, in case. This causes a refresh token (saved by oidc-agent in the local configuration) bounded to filtered scopes, leading to an access token request with (general or specific) storage scopes, using the refresh token (as oidc-agent does), failing with Error: invalid_scope: Up-scoping is not allowed.

As a workaround, one could:

  • register an oidc-agent client with the specific scope (e.g. storage.read:/test)
  • register a client trough IAM web interface, enable the generic storage.*:/ scopes for a client, perform a device code flow to get an access token requesting the specific (e.g. storage.read:/test) scope. This can be done manually or with python for instance. Maybe this script could be useful.

More details

There is a different between registering a client through oidc-agent and trough IAM dashboard, since the oidc-agent client registration includes all in one:

  • registering a (public) client in IAM
    • this would not create any problem with generic/specific storage scopes
  • trigger an authorization/device code flow
    • even if not selected by the user, oidc-agent includes the offline_access scope to obtain also a refresh token
    • user gives consent of a (sub-)set of scopes, which in IAM are filtered by scope policies, if any
    • the associated refresh token is bounded to the (sub-)set of approved scopes.

From user perspective the client registration trough oidc-agent works perfectly fine (i.e. no errors happen), but he/she request for a token, and what happens behind the scene is

  • user requests for storage.read:/test scope (which is parametric)
  • the refresh token is not bounded to the storage.read:/ scope
  • the refresh token is not even bounded to the storage.read:/test scope as this scope was not specified during oidc-agent client registration
  • oidc-agent asks for a token with storage.read:/test scope using the refresh token not bounded to any storage scope, leading to the Up-scoping is not allowed error.

federicaagostini avatar Jun 12 '25 15:06 federicaagostini

This discussion started from a mail thread (subject: Refresh token & IAM scope policies) and carried on in the yesterday's WLCG Authz call. This issue is to track further discussions.

@giacomini @vokac @maarten-litmaath feel free to comment more if you like.

federicaagostini avatar Jun 13 '25 09:06 federicaagostini

With slightly updated script that gets second access token using refresh token with

    IAM_CLIENT_SCOPES=${IAM_CLIENT_SCOPES:-"offline_access wlcg.groups storage.read:/ storage.modify:/atlasdatadisk/SAM/"}
    ...
    curl -q -L -s \
      -u ${IAM_CLIENT_ID}:${IAM_CLIENT_SECRET} \
      -d grant_type=refresh_token \
      -d refresh_token=${refresh_token} \
      -d scope="storage.read:/atlasdatadisk/SAM/" \
      -d audience="${IAM_CLIENT_AUDIENCE}" \
      ${IAM_TOKEN_ENDPOINT} \
      2>&1 > ${response}

I get following response

{"error":"invalid_scope","error_description":"Up-scoping is not allowed."}

"invalid" scope storage.read:/ gets filtered already from list of approved scopes Image

I'm trying to get refresh token that would be valid for all storage scope paths allowed by scope policies. We have several different areas on one storage used by our data management tools (e.g. /atlasdatadisk/rucio/, /atlasscratchdisk/rucio/, ...) and also our storage namespace is not completely uniform (different prefix, e.g. /t1/datadisk/rucio/, /t2/datadisk/rucion/, ...). It is tricky to add all possible PATHs for storage.{read,create,modify} while using authorization / device code flow with offline_access and this list size could in our case exceeds 2kb.

Right now we would like to give experts (users) access to all storage test areas and again for technical reasons we would like to give access to all possible test subdirectories /atlasdatadisk/SAM/, /atlasscratchdisk/SAM/, /t1/datadisk/SAM/, /t2/datadisk/SAM/, ... because all these storage areas can be provided with different quota (spacetoken). Most simple solution would be refresh token storage PATHs set to / and with token policies users would get access only to "SAM" subdirectiory. With IAM Admin privileges I can temporarily "disable" scope policies to get refresh token associated with just root storage path, but that's not possible by end users.

May be we could significantly reduce number of necessary PATHs by unifying our site storage namespace organization, it might still be tricky for us to fit into 2kb limit. Also it seems to me easier to just rely on scope policies for access token during refresh token flow. Our "dynamic" parametric scopes for storage PATH looks quite unique, so if you could try to investigate if same simple rules (equality) should be applied also for these kind of scopes or that are also other ideas. Are our dynamic parametric scopes really such an unique feature of WLCG JWT profile?

vokac avatar Jun 13 '25 11:06 vokac