gr2m / helpdesk

Answering all your GitHub API/automation questions live on Twitch
https://twitch.tv/gregorcodes
Creative Commons Zero v1.0 Universal
22 stars 11 forks source link

Creating a repo on User's account using #64

Closed tahirmahmudzade closed 7 months ago

tahirmahmudzade commented 7 months ago

Please avoid duplicates

Description

Body

I have created a GitHub App and added following scopes:

permissions: {
    checks: 'write',
    actions: 'write',
    contents: 'write',
    metadata: 'read',
    workflows: 'write',
    deployments: 'write',
    administration: 'write'
  },

Then I created a sample user to test out my own GitHub App. I installed my own GitHub App using my newly created User and authorized to act on behalf of this user, however whenever I try to create a repo on this user's account using my GitHub App I get

RequestError [HttpError]: Resource not accessible by integration - https://docs.github.com/rest/repos/repos#create-a-repository-for-the-authenticated-user 

Here is the code I use:

import fs from "node:fs"
import { App } from "@octokit/app"
import { Octokit } from "@octokit/rest"
import devConfig from "~/server/config/devConfig"

const privateKey = fs.readFileSync(devConfig.NUXT_GITHUB_PRIVATE_KEY!, "utf-8")

const app = new App({
  appId: devConfig.NUXT_GITHUB_APP_ID!,
  privateKey,
  oauth: {
    clientId: devConfig.NUXT_GITHUB_APP_CLIENT_ID!,
    clientSecret: devConfig.NUXT_GITHUB_APP_CLIENT_SECRET!,
  },
})

app.eachInstallation(async (installation) => {
  const response = await installation.octokit.request(`POST /app/installations/${installation.installation.id}/access_tokens`)

  const token = response.data.token
  console.log(token)

  const installationOctokit = new Octokit({
    auth: token,
  })

  const repo = await installationOctokit.repos.createForAuthenticatedUser({ name: "test" })
  console.log(repo)
})

P.S: I made a research and found this Github Token Forms. And in here it says we need user-to-server token which is ghu_${token} but the token I get here:

const response = await installation.octokit.request(`POST /app/installations/${installation.installation.id}/access_tokens`)

  const token = response.data.token
  console.log(token)

  const installationOctokit = new Octokit({
    auth: token,
  })

this is ghs: server-to-server tokens . When I make request using this token:

 const installationOctokit = new Octokit({
    auth: token,
  })

  const repo = await installationOctokit.repos.createForAuthenticatedUser({ name: "test" })
  console.log(repo)
})

I get

RequestError [HttpError]: Resource not accessible by integration - https://docs.github.com/rest/repos/repos#create-a-repository-for-the-authenticated-user 

Question

Do I still need to get ghu_${token} user-to-server to create a repo using my GitHub App on behalf of the user ? And if so how can I get it ?

P.S: I need to get it done asap, I would really appreciate if you can help, Thanks in advance!

Would you be interested in joining the show?

gr2m commented 7 months ago

Hmm it's possible that installations cannot create repositories for users.

Do I still need to get ghu_${token} user-to-server to create a repo using my GitHub App on behalf of the user ? And if so how can I get it ?

The way to get a user-to-server token is to redirect the user to an OAuth flow. In your GitHub App's settings, set the callback URL and the " Request user authorization (OAuth) during installation" checkbox. If you use the App constructor from the octokit package, you can set the callback URL to <your houst origin>/api/github/hearts/oauth/callback. You can learn more here:

https://github.com/octokit/octokit.js?tab=readme-ov-file#oauth

The Request user authorization (OAuth) during installation setting will redirect the user to your server with an OAuth code that you can exchange for a user-to-server token.

tahirmahmudzade commented 7 months ago

Hey, thank you for the reply.

So in my project, the whole purpose of creating a GitHub App and installing it in user's account was to create the private template repo on user's account. I have a GitHub oauth flow as well. Basically first, I authenticate user with GitHub oauth and once they are logged in, I provide link which will direct users to install my GitHub App.

export default oauth.githubEventHandler({
  config: {
    scope: ["repo", "user", "workflow"],
  },
  async onSuccess(event, { user, tokens }) {
    console.log(tokens)
    const appLink = `https://github.com/apps/${githubApp}/installations/new/permissions?target_id=${user.id}`

    await setUserSession(event, {
      loggedInAt: new Date().toISOString(),
      user: {
        email: user.email,
        providerId: user.id,
        name: user.name,
        appLink,
      },
    })

    return sendRedirect(event, "/profile")

Please ignore the messy code 😅

I get ghu token with the oauth flow. But the thing is because I only have access to the authenticated user, I can not create the a repo on the newly created user from private template repo that my main account owns, only if it was public, then everything would sort out. So I thought using the GitHub App, I might be able to interact with both: (template owner, new repo owner). What are your thoughts on this? Is there any other way I can archive this ?

gr2m commented 7 months ago

I don't know out of hand, I'd need to investigate this further myself and I don't have the time to do so right now, I'm sorry.

If you find a way, can you share it please? It's a very interesting use case

tahirmahmudzade commented 7 months ago

Well, i just got a response back from Github support and looks like there is no way we can archive that. Thank you for your time ✌🏻

gr2m commented 7 months ago

Sorry to hear that