# Add scopes to \`JUPYTERHUB\_API\_TOKEN\` through Helm chart values

**URL:** <https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543>\
**Category:** Zero to JupyterHub on Kubernetes\
**Created:** [March 20, 2023, 4:44pm UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543 "2023-03-20T16:44:22Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![duck\_boy](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/duck_boy/32/9440_2.png) [@duck\_boy](https://discourse.jupyter.org/u/duck_boy)\
**Post date:** [March 20, 2023, 4:44pm UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543/1 "2023-03-20T16:44:23Z")

</div>

Good afternoon,

I’ve recently updated to chart version 2.0.0 of JupyterHub (app version 3.0.0) from the repository at [See installation instructions for: | JupyterHub’s Helm chart repository](https://jupyterhub.github.io/helm-chart/).

Unlike previous versions, the `JUPYTERHUB_API_TOKEN` now has limited scopes due to the use of the service role, so I’m unable to mange groups through the API with that token (missing the `admin:groups` scope).

[This post](https://discourse.jupyter.org/t/understanding-token-scope-difference/10467/2) was very useful and I totally understand why this is happening and I’m on board with the use or RBAC. However, because I use a Helm chart to install JupyterHub, I don’t have any configuration file that I can change to add the required scopes to the `JUPYTERHUB_API_TOKEN`. Is it possible to do this through the use of Helm chart values, or any other way at all?

Thanks,  
David

---

<div class="post-metadata">

**Author:** ![manics](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/manics/32/85_2.png) [@manics](https://discourse.jupyter.org/u/manics)\
**Post date:** [March 20, 2023, 7:05pm UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543/2 "2023-03-20T19:05:39Z")

</div>

You should be able to modify the roles using `hub.loadRoles`:

> **[Configuration Reference](https://z2jh.jupyter.org/en/stable/resources/reference.html#hub-loadroles)**
>
> The JupyterHub Helm chart is configurable by values in your config.yaml. In this way, you can extend user resources, build off of different Docker images, manage security and authentication, and mo...

---

<div class="post-metadata">

**Author:** ![duck\_boy](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/duck_boy/32/9440_2.png) [@duck\_boy](https://discourse.jupyter.org/u/duck_boy)\
**Post date:** [March 21, 2023, 9:51am UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543/3 "2023-03-21T09:51:15Z")

</div>

Thank you for pointing me in the right direction, I was able to do what I needed by adding the following to my values file.

```yaml
hub:
  loadRoles:
    server:
      scopes: [self, admin:groups]

```

Edit: I’ve ended up having to use `inherit`, because Jupyter will try to assign the `admin:groups` scope to all users, so the spawner will fail for non-admin users. This is quite frustrating, because it now means that the server token has all of the scopes of the user, which kind of negates the point of RBAC.

```yaml
hub:
  loadRoles:
    server:
      scopes: [inherit]

```

---

<div class="post-metadata">

**Author:** ![minrk](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/minrk/32/13_2.png) [@minrk](https://discourse.jupyter.org/u/minrk)\
**Post date:** [March 21, 2023, 7:02pm UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543/4 "2023-03-21T19:02:34Z")

</div>

I’m not sure how your use of admin:groups works, but it’s possible that you should use the token for _requests_, which are associated with each user, rather than the server itself. These scopes are more flexible, and will actually resolve to the subset held by the user (I think we can probably do the same for the server token, after some changes in 3.0, but haven’t done this yet). Can you describe more how you use this scope?

To specify the scopes of the _request_ token, set [`c.Spawner.oauth_client_allowed_scopes`](https://jupyterhub.readthedocs.io/en/stable/api/spawner.html#jupyterhub.spawner.Spawner.oauth_client_allowed_scopes), which is what will be _requested_ as you oauth with the single-user server. You can then use this in a request handler ([`token = hub_auth.get_token(handler)`](https://jupyterhub.readthedocs.io/en/stable/api/services.auth.html#jupyterhub.services.auth.HubAuth.get_token)).

I think that the [changes to token scopes in 3.0](https://github.com/jupyterhub/jupyterhub/pull/3833) should probably be extended to the server token, which should enable this same flexibility (config expresses the _limit_ of scopes, but intersect with those of the user before assigning, rather than failing), and that might just do what you were expecting in the first place.

---

<div class="post-metadata">

**Author:** ![duck\_boy](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/duck_boy/32/9440_2.png) [@duck\_boy](https://discourse.jupyter.org/u/duck_boy)\
**Post date:** [March 22, 2023, 9:30am UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543/5 "2023-03-22T09:30:48Z")

</div>

Hi @minrk , thanks for your reply.

To give you a little more context… We have a Notebook that contains several cells to allow Admin users to interact with the Jupyter API, specifically groups. To do this, we have one cell that sets the `Authorization` header and API URL, and then a series of other cells to do things like create groups and list users in, add users to, and remove users from those groups.

So the reason for wanting the `JUPYTERHUB_API_TOKEN` to contain the relevant scopes is so that we don’t have to generate a token and place it in the Notebook each time one of those cells is run. Not only is it easier, it saves the possibility of a token being exposed to a non-admin users.

In an ideal world, we like to say “give the `JUPYTERHUB_API_TOKEN` the `admin:groups` scope if the user has that scope, otherwise don’t”. This may be what you are suggesting with `c.Spawner.oauth_client_allowed_scopes`, but you’ll have to forgive me as I’m pretty new to Jupyter so I’m not sure if that’s the case. But either way, we have the ability to `inherit`, which while not ideal as it gives more scopes than required to `JUPYTERHUB_API_TOKEN`, it does allow us to achieve our goal.

Thanks,  
David

---

<div class="post-metadata">

**Author:** ![minrk](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/minrk/32/13_2.png) [@minrk](https://discourse.jupyter.org/u/minrk)\
**Post date:** [March 22, 2023, 11:41am UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543/6 "2023-03-22T11:41:08Z")

</div>

That use case definitely makes sense, and it is definitely harder to pass the browser token to the kernel! Not impossible, but you’d need something custom to do it, e.g. a JupyterLab extension to send the token via a `getpass` call.

> This may be what you are suggesting

I believe so, and it’s definitely what we should do. In 3.1, we do this we tokens requested elsewhere, and it would be more consistent to do this with `JUPYTERHUB_API_TOKEN`. [This change](https://github.com/jupyterhub/jupyterhub/pull/4400) adds a `Spawner.server_token_scopes` config that is simpler, more flexible, and essentially replaces the `server` role.

---

<div class="post-metadata">

**Author:** ![duck\_boy](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/duck_boy/32/9440_2.png) [@duck\_boy](https://discourse.jupyter.org/u/duck_boy)\
**Post date:** [March 22, 2023, 11:53am UTC](https://discourse.jupyter.org/t/add-scopes-to-jupyterhub-api-token-through-helm-chart-values/18543/7 "2023-03-22T11:53:12Z")

</div>

That change looks to be exactly what we’d need, so if that becomes available it’s defiantly something we’d go for.

Many thanks,  
David
