Help us understand the problem. What is going on with this article?

Electron で Github OAuth トークンを取得する

More than 1 year has passed since last update.

wamw Friday I/O 6回目
株式会社ワムウでは、毎週金曜日は 興味がある事柄に取り組み、その成果を何らかの形でアウトプットする日としています。

今日は時間がなかったので、メモです。

はじめに

下記コードは既存コードからメモ用に抜き出し、書き換えたものです。
これがそのまま完動するわけではないので、あくまで処理の流れをメモったものとなります。

前提

レンダラプロセスから何らかのアクションにてメインプロセスへ connect-to-github を送るとする。
その際メインプロセス側で下記のような形でアクセストークンを取得したい。

// in main process

ipcMain.on('connect-to-github', async (event: Electron.Event) => {
  const credentials: GithubOAuthCredentials = {
    scopes: ['user', 'public_repo', 'repo'],
    client_id: process.env.GITHUB_CLIENT_ID || '',
    client_secret: process.env.GITHUB_CLIENT_SECRET || ''
  }

  const token: string = await getToken(credentials)
  event.sender.send('github-connected', token)
})

GithubOAuthCredentials はどこかで適当に。

interface GithubOAuthCredentials {
  scopes: string[]
  client_id: string
  client_secret: string
}

アクセストークン取得方法

アクセストークンを取得するためには code を取得する必要がある。
通常のWebアプリケーションの場合、アプリケーションの画面上でOAuth認証画面へ遷移し、認証後にリダイレクトURIへ遷移し返ってくる、という挙動から問題はないのだが、Electronの場合リダイレクトURIなどというものが存在しないため困ることになる。

これを解決するために「リダイレクトした、もしくはどこかの画面に遷移した際URIに code が存在したらその値を取得しウィンドウを閉じる」などの動きを作る必要がある。

上記の getToken は下記のような形だとする。

export async function getToken(credentials: GithubOAuthCredentials): Promise<string> {
  const code = await getCode(credentials)
  const uri = `${accessTokenUri}?code=${code}&client_id=${credentials.client_id}&client_secret=${credentials.client_secret}`
  const headers = { 'Accept': 'application/json' }
  const response = await fetch(uri, { headers })
  const json = await response.json()
  const accessToken: string = json.access_token

  return accessToken
}

この場合の getCode は下記のような動きとなる。
createOAuthWindow は適当なウィンドウを作成する関数。
getCode が要求された場合、新規ウィンドウを立ち上げてそのウィンドウ上でGithubの認証を通す。
認証が通った後、リダイレクトがなされた場合、もしくは認証済みの場合単に遷移するのでその code を取得し、開いたウィンドウを閉じる。

const oauthWindow = Electron.BrowserWindow | null

function getCode(credentials: GithubOAuthCredentials): Promise<string> {
  return new Promise((resolve, reject) => {
    if (!oauthWindow || oauthWindow.isDestroyed) {
      oauthWindow = createOAuthWindow()
      oauthWindow.on('close', () => {
        oauthWindow = null
      })
      oauthWindow.webContents.on('did-get-redirect-request', (event: Electron.Event, oldUrl: string, newUrl: string): void => {
        const code = extractCode(newUrl)
        if (code) {
            oauthWindow.destroy()
        }
        resolve(code)
      })
      oauthWindow.webContents.on('will-navigate', (event: Electron.Event, url: string): void => {
        const code = extractCode(url)
        if (code) {
            oauthWindow.destroy()
        }
        resolve(code)
      })
    }
    const uri = `${authorizeUri}?client_id=${credentials.client_id}&scope=${credentials.scopes}`
    oauthWindow.loadURL(uri)
    oauthWindow.show()
  })
}

extractCode はこんな感じ。

function extractCode(url: string): string {
  const rawCode: RegExpExecArray | null = /code=([^&]*)/.exec(url) || null
  const code: string = (rawCode && rawCode.length > 1) ? rawCode[1] : ''
  const error: RegExpExecArray | null = /\?error=(.+)$/.exec(url) || null

  if (error) {
    throw new Error(error[1])
  }

  return code
}

参考

http://manos.im/blog/electron-oauth-with-github/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした