Admin Scopes Not Given to Users With Admin Role

I attempt to force scopes with z2jh hub config here

      groups: [JupyterDevs]
      scopes: [admin-ui, admin:users, admin:servers, tokens, admin:groups, list:services, read:services, read:hub, proxy, shutdown, access:services, access:servers, read:roles, read:metrics]

I provide groups from my authenticator, my extra spawner config is below. I also force the auth_model into admin = True here.

      auto_login: true
      username_claim: unique_name
      allow_all: true
        - openid
        - profile
        - email
      authenticator_class: azuread
      mountPath: /usr/local/etc/jupyterhub/jupyterhub_config.d/
class CustomAzureAuth(AzureAdOAuthenticator):
    async def update_auth_model(self, auth_model):
        auth_model['groups'] = []

        #some custom methods that give me clientperms.get() function, don't worry about this
        auth_model['groups'] += clientperms.get(auth_model['auth_state']['user']['oid'])

        if 'JupyterDevs' in auth_model['groups']:
            auth_model['admin'] = True
        return auth_model

c.JupyterHub.authenticator_class = CustomAzureAuth
c.Authenticator.manage_groups = True
c.AzureAdOAuthenticator.client_id = os.getenv('AZURE_CLIENT_ID')
c.AzureAdOAuthenticator.client_secret = os.getenv('AZURE_CLIENT_SECRET')
c.AzureAdOAuthenticator.oauth_callback_url = os.getenv('AZURE_OAUTH_CALLBACK')
c.AzureAdOAuthenticator.tenant_id = os.getenv('AZURE_TENANT_ID')

When I enter the server, I can make a request to get my user details

> curl http://hub:8081/hub/api/user -H "Authorization: Bearer ${JUPYTERHUB_API_TOKEN}"
{"last_activity": "2023-09-08T22:37:31.398506Z", "admin": true, "kind": "user", "name": "dduke", "groups": ["JupyterDevs", ...], "session_id": null, "scopes": ["access:servers!server=dduke/", "read:users:activity!user=dduke", "read:users:groups!user=dduke", "read:users:name!user=dduke", "users:activity!user=dduke"]}

But as you can see, these scopes don’t look correct.
Just to verify:

> curl http://hub:8081/hub/api/services -H "Authorization: Bearer ${JUPYTERHUB_API_TOKEN}"
{"status": 403, "message": "Action is not authorized with current scopes; requires any of [list:services]"}

If I make my way onto the hub, and do some querying on the sqlite, I see below.

> select as username, as group_name, as role_name,
    from users
        join group_role_map on group_role_map.group_id =
        join roles on = group_role_map.role_id
        join user_group_map on user_group_map.group_id =
        join groups on = user_group_map.group_id
        join api_tokens on api_tokens.user_id =
    where username = 'dduke';
dduke|JupyterDevs|admin|["access:servers!server=dduke/", "read:users:groups!user", "read:users:name!user"]
dduke|JupyterDevs|jupyterdevs|["access:servers!server=dduke/", "read:users:groups!user", "read:users:name!user"]
dduke|JupyterDevs|admin|["access:servers!server=dduke/", "users:activity!user"]
dduke|JupyterDevs|jupyterdevs|["access:servers!server=dduke/", "users:activity!user"]
dduke|JupyterDevs|admin|["access:servers!server=dduke/", "users:activity!user"]
dduke|JupyterDevs|jupyterdevs|["access:servers!server=dduke/", "users:activity!user"]

Any thoughts?

JUPYTERHUB_API_TOKEN in the notebook environment only contains the minimal scopes needed for the singleuser-server to interact with JupyterHub- this controlled by the server role:

If you want the additional scopes you can either modify the built-in server role, or create a new API token.

I think they key point here is that the permissions for a server are a subset of the user’s permissions, and the $JUPYTERHUB_API_TOKEN is the server’s token, and the default permissions of a server token are very limited (just access:servers and users:activity).

If you want the server api token to inherit the full permissions of its owner, you can override the server role, granting the ‘inherit’ permissions:

    scopes: [inherit]

Or you can be more precise about what permissions you grant the server token.

Additional API tokens can also be issued from the /hub/token page, which default to this inherit permission, without elevating the permissions of the server’s own API token.

Understood - these differences makes sense, I appreciate the clarity provided @manics / @minrk .

I’ll take the steps to inherit as mentioned, but out of my own curiosity - as is/be default, is there not a way to utilize the users permissions that are set? Or does this require generating a token assigned to the user or inheriting the user roles in the server token as mentioned?