badges / shields

Concise, consistent, and legible badges in SVG and raster format
https://shields.io
Creative Commons Zero v1.0 Universal
23.77k stars 5.5k forks source link

GitHub OAuth app/token pool updates #4169

Open calebcartwright opened 5 years ago

calebcartwright commented 5 years ago

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:

chris48s commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 4 years ago

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:

gr2m commented 3 years ago

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

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

Nice - thanks TIL :mortar_board:

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 :+1:

gr2m commented 3 years ago

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 commented 3 years ago

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

gr2m commented 3 years ago
  • 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 commented 3 years ago

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

:facepalm: Thanks (again).

Right, so another (small) job is to amend our existing endpoint https://github.com/badges/shields/blob/fcf6678a127c9679b0d68284b860181c2580fe26/services/github/auth/acceptor.js#L20-L26 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 commented 3 years ago

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 commented 3 years ago

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 https://github.com/badges/shields/issues/4169#issuecomment-773578522 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 commented 3 years ago

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 commented 3 years ago

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 :pray:". 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 username@users.noreply.github.com we store it then we email everyone from something@shields.io 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 commented 3 years ago
  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 commented 3 years ago

That would be another reason to add to the pile of other reasons to not do that :+1:

paulmelnikow commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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.