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

GitHub OAuth app/token pool updates #4169

Open
2 tasks
calebcartwright opened this issue Oct 12, 2019 · 25 comments
Open
2 tasks

GitHub OAuth app/token pool updates #4169

calebcartwright opened this issue Oct 12, 2019 · 25 comments
Labels
blocker PRs and epics which block other work core Server, BaseService, GitHub auth, Shared helpers operations Hosting, monitoring, and reliability for the production badge servers

Comments

@calebcartwright
Copy link
Member

In order for shields Shields to support integrating with GitHub's new(ish) Package Registry offering, we'll need the GitHub tokens in our pool to have the read:packages permission (which they currently do not).

Some action items:

@calebcartwright calebcartwright added core Server, BaseService, GitHub auth, Shared helpers blocker PRs and epics which block other work labels Oct 12, 2019
@paulmelnikow paulmelnikow added the operations Hosting, monitoring, and reliability for the production badge servers label Oct 12, 2019
@chris48s
Copy link
Member

Another relevant question here:

At the moment, we've got a pile of tokens. Is there any mechanism to find out which permissions are granted to a token (other than "try and do a thing that needs permission X and see if it works"). I'm thinking if we ask users to grant a token an extra permission, do we then have any mechanism to know which tokens in our pool now have the extra permission and which ones don't?

@paulmelnikow
Copy link
Member

Hmm, good question. I would think “try it and see if it works”, though maybe there is an endpoint that checks the rights of individual tokens?

@calebcartwright
Copy link
Member Author

It looks like there is some token scope checking endpoints

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

curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com/users/codertocat -I
HTTP/1.1 200 OK
X-OAuth-Scopes: repo, user
X-Accepted-OAuth-Scopes: user

@paulmelnikow
Copy link
Member

I wrote a little CLI utility for working with big piles of GitHub tokens. We could add the scope checking to that: https://github.com/paulmelnikow/github-limited

@chris48s
Copy link
Member

We talked about this a bit earlier today.

Right now, we've got zero tokens that have the necessary permissions to do this and no ability to collect tokens that have it or request it. Regardless of how we're going to manage tokens once we have them, one of the first things we need to do is give ourselves the ability to collect tokens which do have the permissions we need. This is a necessary first step before we can work out how to manage the pool or add badges that need special permissions.

At the moment, tokens are collected via a GitHub OAuth app https://img.shields.io/github-auth which is tied to @espadrine 's account.

One possible approach we could take would be:

  • Start from scratch and write a new Github app under the @badges org (either an OAuth app or a marketplace app - TBC) which does roughly the same thing, but asks for more permissions when you authorize it.
  • Give it some tokens (even if step one is the maintainers give the new app a token, that's some seed data)
  • Modify the code that imports tokens into the app when we bootstrap to import tokens collected by both apps (instead of just one) into the same token pool
  • Then we can work out whether we want to try and:
    • maintain two parallel pools
    • store the permissions each token has
    • drop all the old tokens and try to collect enough via the new app to cover our usage
    • something else?
      as another separate issue
  • Once we're happy with that, deprecate/stop linking to the old app and completely replace it with the new one

@gr2m
Copy link

gr2m commented Feb 4, 2021

Hi there, I'd be happy to help out with any questions you have. I do maintain the @octokit libraries and know the quirks of OAuth Apps vs GitHub Apps pretty well.

The terms "permissions" and "oauth" and "scopes" are all rather confusing. In General

  • A GitHub App can be installed on repositories. It has only access to these repositories. An OAuth app has no concept of installations
  • A GitHub App uses permissions for access control. An OAuth App uses scopes (the ones you see at https://github.com/settings/tokens/new)
  • A GitHub App can also create OAuth tokens. These tokens are also limited by the installations of the app. And these tokens do not support scopes, they inherit the permissions of the app.

I think a GitHub App is not a good fit here, because the OAuth tokens it creates are limited by the repositories the app is installed on. I think what you want to do is to update the app at https://img.shields.io/github-auth to also request the read:packages scope.

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

curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com/users/codertocat -I
HTTP/1.1 200 OK
X-OAuth-Scopes: repo, user
X-Accepted-OAuth-Scopes: user

Any API endpoint will work, the information is in the response header. I recommend taking advantage that GET /rate_limit does not count against the token's rate limit:

curl --head -H"Authorization: token ca056f86d1a1d68462c2282d7ada42f97ff3a088" https://api.github.com/rate_limit
# ...
x-oauth-scopes: read:packages
# ...

Let me know if you have any further questions.

@chris48s
Copy link
Member

chris48s commented Feb 4, 2021

This is useful info. Thanks.
Just to clarify, when you say

I think what you want to do is to update the app at https://img.shields.io/github-auth to also request the read:packages scope

Would that request the read:packages only from new users who sign up, or would that also allow us to retrospectively request the read:packages scope from users who have already signed up via that app?

@gr2m
Copy link

gr2m commented Feb 4, 2021

I think what you want to do is to update the app at https://img.shields.io/github-auth to also request the read:packages scope

Would that request the read:packages only from new users who sign up, or would that also allow us to retrospectively request the read:packages scope from users who have already signed up via that app?

Both. If a user already granted access for the app, but the app is requesting a new scope, the user will be asked to grant permission again.

@chris48s
Copy link
Member

chris48s commented Feb 4, 2021

Nice - thanks TIL 🎓

I guess even if we request additional scopes, some users may not grant them (or the accounts may be dormant and nobody will ever see the notification) so I guess we still need to deal with the tokens having a mixed bag of permissions but that sounds like the way forward 👍

@gr2m
Copy link

gr2m commented Feb 5, 2021

yes. However you store the current tokens, you'll have to store whether it has the read:packages scope. For the existing tokens you'll have to set the flag once, scopes don't change for tokens, you don't need to check every time

@chris48s
Copy link
Member

We talked about this in the ops meeting today. Conclusion is there are basically 2 things we need to do to move on this:

  • Currently the auth/token pooling code assumes all tokens have the same permissions. For each scope, we are going to need to deal with the fact that we'll have some tokens that have got scope X and some that haven't so we're going to need to query/store that with each token when we select it to swap it into the pool, or when we init the tokens or something. Then badges that need an additional scope need to be able to specify that so the request can be made with a suitably scoped token. Still a bit of thinking do do about the exact mechanics of this, but it is achievable.
  • The existing OAuth app has now been transferred to the shields org 🎉 . I don't have access to it, but @paulmelnikow does. We couldn't figure out how to request more scopes from users on the call, but it looks like these are the relevant docs: https://docs.github.com/en/developers/apps/editing-a-github-apps-permissions @paulmelnikow - I think we probably don't actually want to request new permissions right this second, but could you have a really quick look over those docs and double-check we at least have the necessary buttons.

@gr2m
Copy link

gr2m commented Feb 23, 2021

  • We couldn't figure out how to request more scopes from users

You have to send the user to the OAuth Web flow with the scope query parameter set to the scopes you need

The link you shared (https://docs.github.com/en/developers/apps/editing-a-github-apps-permissions) is for GitHub Apps, not OAuth Apps. It's unrelated to what you need. You have an OAuth app and OAuth apps don't have permissions, and they don't have a global set of scopes that would need to be updated for the OAuth app registration on GitHub

@chris48s
Copy link
Member

The link you shared (https://docs.github.com/en/developers/apps/editing-a-github-apps-permissions) is for GitHub Apps, not OAuth Apps.

🤦 Thanks (again).

Right, so another (small) job is to amend our existing endpoint

client_id: authHelper._user,
redirect_uri: `${baseUrl}/github-auth/done`,
})
ask.res.setHeader(
'Location',
`https://github.com/login/oauth/authorize?${query}`
)

to request the additional scopes we want. That will start requesting the additional scopes from new users who authorize, but doing that doesn't change anything for users who have already authorized.

For the users who have already authorized, we need to actively prompt them to go back though that process. Am I right that we'd then follow this process https://docs.github.com/en/developers/apps/authorizing-oauth-apps#directing-users-to-review-their-access to request existing users go back though the auth flow?

@gr2m
Copy link

gr2m commented Feb 23, 2021

Am I right that we'd then follow this process https://docs.github.com/en/developers/apps/authorizing-oauth-apps#directing-users-to-review-their-access to request existing users go back though the auth flow?

You need to send existing users to the same URL as new users. GitHub will detect that you are requesting a scope that the user did not authorize before and prompt the user to grant the additional access

@chris48s
Copy link
Member

You need to send existing users to the same URL as new users

OK, so this process of "send existing users to the [same] URL".. Is that something where there is a mechanism in github to request a user does that?

i.e: I've seen apps send me a notification like

additional-scopes

in the past. I've digested the comments in #4169 (comment) and I've taken on board "permissions" (which are being requested in that screenshot) are different from "scopes" (which is what we need) so I accept this isn't a direct example of what we're trying to achieve.

Is there a way for us to do something similar to request an additional scope, or are we on our own to try and reach out to users in the community who have authorized the app and try and ask them to go back through the auth process?

@gr2m
Copy link

gr2m commented Feb 23, 2021

are we on our own to try and reach out to users in the community who have authorized the app and try and ask them to go back through the auth process?

Yes, when it comes to OAuth user authorization tokens, you are on your own in terms of reaching out, sorry.

You might be able to use the existing tokens and see if you can get the user's email address, but that requires the user:email scope (included in user). Telling from your existing code, you don't have that permission.

What you can do is to use the existing token for a GET /user request in order to get the user login, then create a public repository in which you can create one issue per user, asking them to go through the login again. But that could get you trigger abuse limit pretty quickly. I'd check in with https://support.github.com/contact, they might have a better idea, or at least let you know what you can do to avoid being blocked.

You definitely should use the throttle plugin: https://github.com/octokit/plugin-throttling.js/. It will limit requests to create issues to one per 3s, which should be fine, but cannot make promises what will happen if you create thousands of issues that all mention a different user

@chris48s
Copy link
Member

Got it. We've been working with slightly different understandings on what it means to "request new scopes from an existing user", but we're now on the same page. This is one of the reasons why we initially thought it might make more sense to move away from an OAuth app. My assumption is this is a problem we'll need to deal with again at some point in future: GH will launch another new thing that requires a new scope and we're back here again. Anyway, a github app is unsuitable for other reasons, so..meh

In terms of how we would orchestrate the process of trying to get users who have already donated a token to re-authorize, I can think of 3 things we can do:

  1. Lightest touch option - we use what comms channels we have (twitter, discord, pinned issue, etc) to say something to the effect of "we need new OAuth scopes so we can do $NEW_EXITING_THINGS if you've previously authorised our app, please consider re-authorizing us at https://img.shields.io/github-auth to give us more permissions 🙏". I suspect that will be highly ineffective but it is also unlikely to upset anyone. Also drive for new signups at the same time. If we can deal with tokens having a mix of scopes it doesn't really matter if we get new users authorizing or existing users re-authorizing - as long as we get those sweet sweet rate limit points to feed the beast!
  2. Slightly more proactive option: Something like @gr2m suggests above. We use the tokens we have to grab usernames attached to those tokens and @mention everyone on github somehow asking for MOAR scopes. Probably more effective in reaching people, but I think it is more likely to be seen as overreach and upset some users too. I don't think I've ever seen a project do something like this before. I guess we could do a small scale test (e.g: we sample 1% of tokens we hold, try this and see how badly it upsets people). I think I'm still against it though.
  3. Super-creepy option: Same as above, but we write a script to find a commit for each user and slurp the email address from the commit. If we get something other than [email protected] we store it then we email everyone from [email protected] asking for the extra permissions. We totally could do this, and we totally shouldn't IMO.

I think the only thing I'd be comfortable with us doing from that list is option 1 tbh, but interested to hear from other core team on this. Any opinions? Any other ideas?

@gr2m
Copy link

gr2m commented Feb 24, 2021

  1. Super-creepy option: Same as above, but we write a script to find a commit for each user and slurp the email address from the commit

don't do this, I'm pretty sure that is against GitHub's Terms of Service.

Make sure to talk to support, they are great folks, they might have more ideas

@chris48s
Copy link
Member

That would be another reason to add to the pile of other reasons to not do that 👍

@paulmelnikow
Copy link
Member

Option 1 sounds good to me. I might be more optimistic than you are that some people will do this. Or, maybe more content with having a small fraction of tokens with the needed permission.

At first, we could even reserve the tokens with the added scope for the query that needs them. We don't have too many things using the GraphQL quotas to begin with, and overall we have a lot more quota than we need.

And @gr2m… thank you so much for helping us with this!

@calebcartwright
Copy link
Member Author

Started digging back into this and trying to refresh my memory. One thing that's not immediately clear to me is whether the "access to public information (including user profile info, repository info, and gists)" part of the default (no_scope) option is always inherent, or if that gets wiped out when explicit scopes are requested.

For example, we'll obviously want the read:packages, almost certainly read:user (i could see cases for/against read:org based on some past requests we've had for org-level Project badges). However, there's no read-only type scopes I've ever found for repositories, so if we start requesting ?scope=read:packages%20read:user is there the potential for those subsequent tokens to be unusable for certain repository info?

@gr2m
Copy link

gr2m commented Sep 15, 2021

The read: scopes are always about getting more information that is already available publicly. You don't need any extra scopes to access public repositories. A token without any scopes can be used to access information from any public repositories, but your rate limits is bumped to 5000 requests / hour because you are authenticated.

@uncenter
Copy link
Contributor

This is a little unrelated but - are we looking for more people to contribute tokens? I didn't even know this was a thing I could do until reading this thread. If we do want a larger pool that link (https://img.shields.io/github-auth) should totally be in the README or somewhere prominent on the website!

@calebcartwright
Copy link
Member Author

This is a little unrelated but - are we looking for more people to contribute tokens?

No, not right now. Obviously folks may donate one if they really want to, but we've got plenty of overhead right now with our existing queue.

We really don't want to actively solicit new tokens until we've solved the design and implementation challenges discussed above, as if someone is going to donate a new token then we'd rather have the new scope/permissions established so that the token can be used for the newer badges that require those scopes/permissions

@chris48s
Copy link
Member

Yeah right now we have more than enough tokens to cover our current level of GitHub API usage, which is why it is not a very visible call to action.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocker PRs and epics which block other work core Server, BaseService, GitHub auth, Shared helpers operations Hosting, monitoring, and reliability for the production badge servers
Projects
None yet
Development

No branches or pull requests

5 participants