既にNode.jsのOAuthを使ってGoogleDriveAPIを叩いてみたという記事がありますが、GUIアプリでわざわざ別のブラウザから認証コードをコピペしてくるのも面倒なので内蔵WebViewを使ってよろしくやってみたメモ。
IPCは使っていないですがUIにVue.jsとvue-routerを使っているので若干その知識が必要になります。
ソースはこちら
https://github.com/kuinaein/electron-drive/compare/bc77d0d...88a3c37
大まかな流れは次の通り。
- Google Cloud Console で OAuth クライアント ID を作成
- electron-vue テンプレートを使って新規 Electron プロジェクトを作成
- ユーザー認証のための WebView 画面を作成
- Google API (今回は Drive API) を使う画面を作成
Google Cloud Console で OAuth クライアント ID を作成
このやり方はあちこちで言及されているのでざっと流します。
- Google Cloud Console にログインし、APIとサービス > 認証情報を開く
- 認証情報を作成 > OAuth クライアント ID
- アプリケーションの種類は「その他」を選択
- できたクライアント ID の行の編集ボタン(鉛筆マーク)をクリック
- 「JSONをダウンロード」をクリック
- client_secret.json 等の名前で保存しておく
electron-vue テンプレートを使って新規 Electron プロジェクトを作成
今回は UI ライブラリとして Vue.js 、HTML テンプレートに Pug を利用することにします。
sudo npm install -g vue-cli
# プロジェクト名はお好きに
vue init simulatedgreg/electron-vue electron-drive
cd electron-drive
yarn install
yarn add googleapis pug-html-loader
プロジェクト設定はデフォルトのままでOKです。ただ ESLint を入れるとコーディング規則違反でコンパイルエラーになるので、小物を作るときは外したほうがいいかもしれません。(ちょっと癖がある……)
ユーザー認証のための WebView 画面を作成
最初に client_secret.json を src/ あたりに移しておきます。
まず認証画面を表示するのですが、今回は vue-router を使っているので renderer/router/index.js
にルート定義を書きます。
const REDIRECT_URI = 'http://localhost:11451/'
export default new Router({
routes: [
{
path: '/',
name: 'login',
component: require('@/components/LoginPage').default,
meta: { redirectUri: REDIRECT_URI }
// name: 'landing-page',
// component: require('@/components/LandingPage').default
},
// ...
REDIRECT_URI はダミーなので localhost ならポート番号は何番でも良いです。
renderer/components/LoginPage.vue
は Google のログインページを表示するだけなので、全面WebViewです。
<template lang="pug">
webview(ref="webview" :src="url" @will-navigate="onWillNavigate")
</template>
<style scoped>
webview {
display: inline-flex;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
<script>
const { google } = require('googleapis')
const OAuth2 = google.auth.OAuth2
const clientSecret = require('@/../client_secret.json').installed
export default {
data () {
return { url: 'about:blank' }
},
mounted () {
if (process.env.NODE_ENV === 'development') {
window.webview = this.$refs.webview
}
const auth = new OAuth2(clientSecret.client_id,
clientSecret.client_secret,
// clientSecret.redirect_uris[0])
this.$route.meta.redirectUri)
this.url = auth.generateAuthUrl({scope: 'https://www.googleapis.com/auth/drive'})
},
methods: {
onWillNavigate (ev) {
const url = '' + ev.url
if (url.startsWith(this.$route.meta.redirectUri)) {
// 念の為止める。 stop() は効かない模様
this.$refs.webview.loadURL('about:blank')
const parser = new URL(url)
const code = parser.searchParams.get('code')
this.$router.push({name: 'drive', query: {code}})
}
}
}
}
</script>
認証が通ってリダイレクトされたタイミングで will-navigate
イベントが発火するので、ここで webview.loadURL()
を呼んで WebView を止めます。
そのあと $router.push()
で API を利用する画面に飛ばします。このときに取得した認証コードを渡しています。
Google API (今回は Drive API) を使う画面を作成
こちらもまずルート定義を入れます。
{
path: '/drive',
name: 'drive',
component: require('@/components/DrivePage').default,
meta: { redirectUri: REDIRECT_URI }
},
次いでビューを書きます。今回はマイドライブのファイル一覧を表示するだけのシンプルなものです。
<template lang="pug">
div
div(v-if="null === files") ロード中...
ul(v-else)
li(v-for="f of files" :key="'file-' + f.id") {{f.name}}
</template>
<script>
const { google } = require('googleapis')
const OAuth2 = google.auth.OAuth2
const clientSecret = require('@/../client_secret.json').installed
export default {
data () {
return { files: null }
},
mounted () {
const auth = new OAuth2(clientSecret.client_id,
clientSecret.client_secret,
this.$route.meta.redirectUri)
auth.getToken(this.$route.query.code).then(res => {
auth.credentials = res.tokens
const drive = google.drive({version: 'v3', auth})
return drive.files.list({
q: "'root' in parents and trashed = false"})
}).then(res => {
this.files = res.data.files
}).catch(err => {
console.log(err)
alert('エラー!: ' + err)
})
}
}
</script>
APIを呼ぶためにはまず認証コードを元にトークンを得る必要があるので、最初にそれを行っています。
あとは OAuth オブジェクトにトークンをセットして API を呼ぶだけ。
意外と簡単でした……。あくまで実験用なので auth オブジェクトの持ち方とか雑ですが。