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

[Proposal] Redesign Scoped Personal Access Tokens (PATs) / Fine Grained Personal Access Tokens #24501

Closed
kdumontnu opened this issue May 3, 2023 · 3 comments · Fixed by #24767
Labels
type/feature Completely new functionality. Can only be merged if feature freeze is not active. type/proposal The new feature has not been accepted yet but needs to be discussed first.

Comments

@kdumontnu
Copy link
Contributor

kdumontnu commented May 3, 2023

Feature Description

There are a number of issues with scoped access tokens currently, ranging from UI/UX to implementation.

The gitea implementation of scoped PATs was clearly based off of GitHub's original implementation, which inherits some issues:

  • GH's token scopes never really made sense and have since been depricated
    • You couldn't give separate read vs write permissions to repos
    • They were always ambiguous as to what the scopes gave access to

Additionally, our implementation had to map the odd GH scopes to our API routes, which further complicates the implementation, and leaves reqToken calls sprinkled around the code (which introduces bugs now and will be difficult to maintain going forward).

Consider that we already have a nice hierarchy of scopes introduced by the API - imo, there is no reason to remap these to arbitrary GH scopes. I propose the following high-level scopes:

  • activitypub
  • admin (hidden if user is not a site admin)
  • misc
  • notification
  • organization
  • package
  • issue
  • repository
  • user

The for each of the above we have an option for:

  • read
  • write
  • delete

We also add an option at the top for:

  • only public repositories / orgs

This will be significantly easier for users to understand and for us to document and maintain, and it more closely matches the direction GitHub is moving.

While we're here, some further improvements:

  • By default in the UI, scopes are collapsed, meaning that if a user just creates a PAT without expanding the hidden scopes, they won't have access to anything (or will it? it's unclear what "no scopes" implies).
    • Solution: if no scopes literally gives access to nothing, the "create" button should be disabled if no scopes are selected.
    • if no scopes gives access to something, we should have a "scope" that explains what that access is, and is always checked.
    • edit: apparently default is all, but the shows Scopes: (empty)' and not all`
  • Tooltips & documentation embedded in the scoped access tokens UI
  • In GH, if you selected a higher level scope (eg. "repo") it would automatically check the lower level scopes (eg. repo:status and public_repo), however, it does not for us. So what happens if I select repo ("Full control over all repositories."), but not "repo:status")?
    • Solution: selecting a higher level scope should select all of the lower level scopes
  • User shouldn't be able to select scopes they themselves do not have access to. It's confusing and misleading. We should disable those options.
  • Allow to set a expiry on tokens
  • Allow to scope to repo

Screenshots

eg.
image

@kdumontnu kdumontnu added type/proposal The new feature has not been accepted yet but needs to be discussed first. type/feature Completely new functionality. Can only be merged if feature freeze is not active. labels May 3, 2023
@harryzcy
Copy link
Contributor

harryzcy commented May 3, 2023

In general, I agree that we can have an improved scoping.

I implemented the token scope, so to answer a few questions:

By default in the UI, scopes are collapsed, meaning that if a user just creates a PAT without expanding the hidden scopes, they won't have access to anything (or will it? it's unclear what "no scopes" implies).

no scope means you can access any information that's publicly available, just like you can browse gitea website without login. Anything that requires login are not accessible.
The default for newly created token is empty. Only for tokens already created before v1.19.0, the scope is set to all during migration for backward compatibility.

In GH, if you selected a higher level scope (eg. "repo") it would automatically check the lower level scopes (eg. repo:status and public_repo), however, it does not for us. So what happens if I select repo ("Full control over all repositories."), but not "repo:status")?

It's a UI issue and should be fixed. On the backend, lower level scopes are actually enabled.

I think the routes require some cleanup too and we should order then based on hierarchy. Currently, a few endpoints are scattered in different groups. That makes it harder to maintain.

@kdumontnu
Copy link
Contributor Author

Thanks for the followup @harryzcy! I'm working with @jackHay22 on refactoring some of these items. It would be great to have your help implementing/reviewing.

What do you think about refactoring the scopes to align more with the API routes?
As a followup, we could even introduce "fine-grained" scopes that actually match to specific API routes (maybe using wildcards?), but this is a bigger proposal we could discuss later.

@harryzcy
Copy link
Contributor

harryzcy commented May 3, 2023

@kdumontnu Scopes that align with API routes are much easier to understand. Totally agreed.
I'm not sure what your status is. If you have a PR at some point, feel free to @ me.

If we want to support "fine-grained" scopes, we probably should leave room for it when designing the token pattern. Matching the repo names or even the specific routes could be options. The actually implementation could be for another PR.

techknowlogick pushed a commit that referenced this issue May 5, 2023
This might be a bit contentious, but I think we should try to limit the
impact of deprecating scoped PATs with the rewrite proposed here we're
working on for v1.20: #24501

We should have a PR opened shortly to re-scope the routes.
delvh pushed a commit that referenced this issue Jun 4, 2023
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
    - `activitypub`
    - `admin` (hidden if user is not a site admin)
    - `misc`
    - `notification`
    - `organization`
    - `package`
    - `issue`
    - `repository`
    - `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
  -  `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
  - `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection

### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">

## tokenRequiresScopes  Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error

## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
   -  _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
   - _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
   - _This should be addressed in this PR_
   - For example: 
   ```go
	m.Group("/users/{username}/orgs", func() {
		m.Get("", reqToken(), org.ListUserOrgs)
		m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
   ```

## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default

Closes #24501
Closes #24799

Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
type/feature Completely new functionality. Can only be merged if feature freeze is not active. type/proposal The new feature has not been accepted yet but needs to be discussed first.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants