Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth token scopes? #1057

Closed
minrk opened this issue Apr 5, 2017 · 13 comments · Fixed by #3438
Closed

Auth token scopes? #1057

minrk opened this issue Apr 5, 2017 · 13 comments · Fixed by #3438

Comments

@minrk
Copy link
Member

minrk commented Apr 5, 2017

Since we have OAuth set up, we may consider the notion of scopes. However, I realize this does't really have anything to do with OAuth, but rather adding the notion of scopes to our existing API tokens.

This could change how services / servers ask the Hub if a given user should have access. Instead of asking "who is this?" and checking against a local set of permitted users, the service could ask the Hub "should I allow this user?", enabling the Hub to manage the list of users with access to each.

@barrachri
Copy link
Contributor

How do you plan to manage the list of users?

Right now you have a list of users that can login in and the groups for single-server shared among different users.

@minrk minrk added this to the 0.8 milestone Apr 18, 2017
@minrk
Copy link
Member Author

minrk commented Apr 18, 2017

This (or something like it) could limit the potential of a compromised single-user server. Since OAuth right now produces a token that gives access to do anything with the Hub on behalf of the visitor, I'm not okay with that being the case for 0.8.

One simple way to implement this is to separate the OAuth access token into something dedicated for the "Only identify the user" rather than an API token. This can be hardcoded pretty simply by adding support for identification via access token into the /api/user endpoint. I might do this for 0.8, so we don't have to work through the whole idea of scopes before release.

However, if a service really does want to perform actions on behalf of the user, OAuth scopes would be the way to do it.

@minrk minrk modified the milestones: 0.8, 0.9 Oct 3, 2017
@clkao
Copy link
Contributor

clkao commented Mar 25, 2018

With jupyterhub/oauthenticator@a5f05c2 in oauthenticator, we can actually implement some sort of required_scope logic, which verifies auth_state.scope returned from oauth idp.

I currently use a subclass overriding authenticate to only allow users with the gpubasic scope (mapped from a realm role in keycloak)

class OIDCAuthenticator(GenericOAuthenticator):
    ...
    scope = [ 'openid', 'gpubasic']
    @gen.coroutine
    def authenticate(self, handler, data=None):
        user = yield super().authenticate(handler, data)
        if not user:
            return
        if 'gpubasic' not in user['auth_state']['scope']:
            return
        return user

@minrk minrk modified the milestones: 0.9, 0.10 May 4, 2018
@minrk minrk mentioned this issue Oct 17, 2018
8 tasks
@minrk
Copy link
Member Author

minrk commented Oct 17, 2018

Now that we have oauthlib landed, we can start

each scope may have up to three permission specifiers:

  • :r read-only

  • :w write (can modify existing entities, but not add or delete)

  • :a admin (not always relevant, but where relevant can add/remove entities)
    Here are some scopes I have so far:

  • all the highest level. Tokens with this scope can do do everything the owner of the token can do. This is essentially the only scope we have now for API tokens.

    • read: read everything the user can
    • write: modify everything the user can
  • user: everything corresponding to the owner. Equivalent to all for non-admin users, confined to actions on the owner for admin users.

  • identify - the lowest-level, can only be used to identify the user. Can take no action and inspect nothing about the user other than group membership. This is the only scope we have now for OAuth tokens.

  • users: manage users (admin-only)

    • read: read everything about the user
    • write: modify existing users (e.g. group membership, admin)
    • admin: add/remove users
  • servers: manage the owner's servers

    • read: see current server status
    • write: start/stop existing servers
    • admin: create/delete named servers
  • all-servers: (admin only) same as servers, but for all servers

  • tokens manage tokens

    • read:
    • write: administer tokens (no need for separate admin)
  • auth-state

    • read: include auth state in user models. Requires admin.

The main question remaining for me is how to manage access to servers. Right now, we use the identify scope, and then authorization is handled in the singleuser server. But there isn't currently a scope for managing token-based access to running servers. What would that be, and would it be all or nothing?

Additional scopes that make sense, but I might prefer to save for a later iteration:

  • confine access to a specific server or servers (e.g. issue a token for just this server)
  • fine-grained scopes for access to the single-user API (this is probably too hard to modify in a subclass of the notebook server at the moment)

@vilhelmen
Copy link
Contributor

Re: #2436, could you elaborate on assigning scopes to URLs? Are we talking the individual API URLs?

Security-wise, I think my initial goal would be a way to construct an "elevated" account that exists between a weakly-trusted user and an administrator. It grants access to basic user servers (including other elevated users(?)), but no access to admin servers and no ability to modify user data (create/delete/admin).

I don't quite see a way to construct that out of the proposed scopes.

Server access could be hacked together with write access to all-servers granting only non-admin server access.

Admin flag configuration could be moved to admin on user, but then what about groups? (TBH I've never used the groups system, I'm not even sure it does anything yet? I'm struggling to find documentation mentioning it, is it all internal still?).

Under this method, an "elevated" user could look somewhat like RW for servers/all-servers and R for users.

@ellisonbg
Copy link
Contributor

@minrk can you talk a bit more about how you see this interplaying with oauth scopes. I have been thinking that JupyterHub as an oauth provider would declare a standard set of oauth scopes that could be requested in the oauth token flow. These scopes could then be used to control authorization to the different entities running in JupyterHub (admin panel, single user servers (and individual services running within them), other jhub services). This would require making the single user server aware of these scopes/tokens and use them in determining if a given request is authorized. This would allow fine grained access to the different services of the single user servers (for example, rw access to notebooks, but no code running).

@willingc
Copy link
Contributor

willingc commented Mar 7, 2019

@minrk @jupyterhub/binder-team FYI related issue from JupyterLab dashboarding

@minrk
Copy link
Member Author

minrk commented Mar 7, 2019

could you elaborate on assigning scopes to URLs?

I mean go through the endpoints in the rest api and make sure that each API endpoint has an associated scope (this doesn't mean each one needs to be distinct, just that the coverage should be complete). For example identify would cover only /user. GET /api/users would be users:read, while users:write would be required for PATCH /api/users and users:admin for POST /api/users, etc.

Security-wise, I think my initial goal would be a way to construct an "elevated" account that exists between a weakly-trusted user and an administrator. It grants access to basic user servers (including other elevated users(?)), but no access to admin servers and no ability to modify user data (create/delete/admin).

I think this is where users can be granted limited access to scopes. E.g. right now, we only have two classes of user: admin (access to everything, with the possible exception of user servers), and regular (no administrative privileges). Scopes will allow us to define a user's permission level, e.g. granting individuals read access to the user list, for instance. So scopes are applied at both the individual token and the user level (tokens for a given user are limited to a subset of the user's permissions).

can you talk a bit more about how you see this interplaying with oauth scopes

I'm not quite sure I know what this refers to, since the topic here is oauth scopes, so I'm reading that as the interplay of oauth scopes and oauth scopes. But I think your understanding is correct - jupyterhub is an oauth provider and we define scopes for specific actions, such as pieces of the API or integrated services. It would be the responsibility of the single-user server to apply relevant scopes if we have e.g. read+write+execute at the single-user server level (I think it will be very hard to distinguish these in the single-user server, and that will mostly require work in the notebook server that jupyterhub can't add by itself). If we want users to grant access to a server, this would require a scope to be able to take an argument, e.g. server.user-name.server-name:read.

@ellisonbg
Copy link
Contributor

@minrk thanks for the reply. Reading your previous comments, I don't know how I was unclear that you were talking about OAuth scopes. But that is pretty clear now ;-)

I like the idea of r/w scopes being directly related to the HTTP verbs that users is allowed to use.

One of the things that is emerging in conversations with Zach is that the idea of permissions, while related, is possibly a different things than OAuth scopes. Some examples:

  • A user who has r/w OAuth scope, may have a range of permissions on individual files in the Content API. This pattern has come up in Zach's design work on HubShare (which is essentially evolving to being the Content API + permissions)
  • A user who can access some kernels, but not others. Or maybe sees outputs from the kernel, but can't run code.
  • Permissions around which single user server a user can access. I think GitHub is a good example here, and this brings up a question - does GitHub have repo scoped tokens in the manner proposed above (server.username.servername:read)?

@ellisonbg
Copy link
Contributor

Here is the GitHub does on their OAuth scopes:

https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/

From my reading, per repo permissions are a separate question than their scopes.

@minrk
Copy link
Member Author

minrk commented Mar 25, 2019

Ah, I don't believe that JupyterHub should be involved in HubShare per-file permissions. That should be entirely under the control of HubShare. The only scope that's JupyterHub related ought to be whether the given token is allowed to talk to the HubShare service or not.

I believe the same goes for access to kernels. That doesn't seem like it should be in-scope for JupyterHub authentication. That seems to be something that should be wholly under the control of a customized user environment.

I agree that per-server permissions are probably going to need to be handled specially, though scopes seem like they might be a good fit. Otherwise, there will be no way to grant a token access to a server, a token will always and only be able to access all servers a user has permission to access (the current state).

@minrk minrk removed this from the 1.0 milestone Sep 1, 2020
@IvanaH8
Copy link
Contributor

IvanaH8 commented Sep 1, 2020

Hi, @minrk and I have compiled a draft list of JupyterHub oauth scopes:

https://hackmd.io/@4G1An0geTrulPAvy23CzEQ/juptyerhub-scopes

Any comments/suggestions are welcome

@RomainPhil
Copy link

Hi any news with this issue ?
if there is a parameter to restric a user with the defined scope

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants