octokit / octokit.js

The all-batteries-included GitHub SDK for Browsers, Node.js, and Deno.
MIT License
6.86k stars 1k forks source link

[BUG]: Authentication issues when acting as a user via Octokit.js API #2568

Closed danieldelcore closed 6 months ago

danieldelcore commented 8 months ago

What happened?

Unsure how to properly auth in order to request app installations available to the user.

List app installations accessible to the user access token"

    import { App, Octokit } from 'octokit';

    const app = new App({
      appId,
      privateKey,
      oauth: {
        clientId: process.env.GITHUB_ID,
        clientSecret: process.env.GITHUB_SECRET,
      },
      Octokit: Octokit.defaults({ auth: account.access_token }),
      webhooks: { secret }
    });

    const { data: installations } = await app.octokit.request('GET /user/installations');

However when i make the request it returns the following error:

Error: [@octokit/auth-app] installationId option is required for installation authentication.
    at getInstallationAuthentication (webpack-internal:///(rsc)/./node_modules/octokit/node_modules/@octokit/auth-app/dist-node/index.js:153:15)
    at hook (webpack-internal:///(rsc)/./node_modules/octokit/node_modules/@octokit/auth-app/dist-node/index.js:327:40)

InstallationId does not seem to be listed as a parameter for that end point (and it's the information i'm trying to get)

Are there some examples or something that i'm missing 🤔 Any help would be greatly appreciated.

Versions

Octokit ^3.1.1 Node 16

Relevant log output

Error: [@octokit/auth-app] installationId option is required for installation authentication.
    at getInstallationAuthentication (webpack-internal:///(rsc)/./node_modules/octokit/node_modules/@octokit/auth-app/dist-node/index.js:153:15)
    at hook (webpack-internal:///(rsc)/./node_modules/octokit/node_modules/@octokit/auth-app/dist-node/index.js:327:40)

Code of Conduct

github-actions[bot] commented 8 months ago

👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labled with Status: Up for grabs. You & others like you are the reason all of this works! So thank you & happy coding! 🚀

wolfy1339 commented 8 months ago

Can you try to fetch the installation directly using a separate request and not using app.octokit.request()?

Also, we don't support Node 16 anymore since v3. If you cannot upgrade to Node JS >=18, then you will have to stick with v2

danieldelcore commented 8 months ago

Interesting, I managed to get it working now. Apparently when using the rest API typescript will complain about a missing property – username.

const installation = await app.octokit.rest.apps.getUserInstallation({ username: 'danieldelcore' });

However, the request API seems to be completely different and doesn't ask for a username. Would you be able to explain the difference between the two, becauseI thought they were just different APIs to do the same thing?

const { data: installations } = await app.octokit.request('GET /user/installations');
wolfy1339 commented 8 months ago

The first one lists installations for a specific user, the second lists installations for the authenticated user

danieldelcore commented 8 months ago

Ok, the second one seems actually more inline with what i'm trying to achieve actually 🤔 Any ideas what I'm doing wrong?

danieldelcore commented 8 months ago

The docs say this should work.

    const octokit = new Octokit({ auth: account.access_token });
    const installations = await octokit.request('GET /user/installations', {
      headers: {
        'X-GitHub-Api-Version': '2022-11-28'
      }
    });

however, this returns:

      message: 'You must authenticate with an access token authorized to a GitHub App in order to list installations',
gr2m commented 8 months ago

You must authenticate as a user through the app in order to access this endpoint (user-to-server token, see docs). You need to use the OAuth Webflow or Device flow for it.

Try this

  const app = new App({
    appId,
    privateKey,
    oauth: {
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    },
    Octokit: Octokit.defaults({ auth: account.access_token }),
    webhooks: { secret }
  });

const { token } = await app.oauth.createToken({
  async onVerification(verification) {
    console.log("Open %s", verification.verification_uri);
    console.log("Enter code: %s", verification.user_code)
  },
});

const octokit = new Octokit({ auth: token });
const installations = await octokit.paginate('GET /user/installations');
console.log(installations)
danieldelcore commented 8 months ago

I'm using https://next-auth.js.org/providers/github, when you login via the site you're directed to GH via the OAuth Webflow and back to the callback URL, the token will be created as a result and will store the access_token in the DB, which should represent the user-to-server token. Am I understanding correctly? Are we talking about two completely different tokens or am i right in thinking that I'm already doing the app.oauth.createToken step 🤔.

(Sorry, i'm just trying to make sure I fully understand what's happening conceptually here.)

I'm pulling it out of the DB like so:

const { access_token } = await prisma.account.findFirstOrThrow({ where: { id: session.user.id }});

If i'm understanding correctly, we now need to create an App instance including everything necessary for the Server / Server auth, then authenticate an Octokit instance as the user for the Server / User auth.

I feel like I'm not plugging the access_token into the right place somehow, or i'm making some sort of silly mistake.

gr2m commented 8 months ago

Are we talking about two completely different tokens or am i right in thinking that I'm already doing the app.oauth.createToken step

It sounds right. You don't need the App constructor anymore at this point, you have the access token that you use to instantiate Octokit directly. The access token is already bound to the app, if you use the GitHub App's client ID/secret to configure https://next-auth.js.org/providers/github

const octokit = new Octokit({ auth: access_token })

const installations = await octokit.paginate('GET /user/installations');
danieldelcore commented 6 months ago

Closing as I think this is the correct approach:

https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation#using-octokitjs-to-authenticate-with-an-installation-id