Google Sign-In をReactなSPAで使った時の日記です。
サーバーとの連携やセッションの管理について少し悩んだのでメモっておきます。ちょっと長くなりそうなので何回かに分けます。
Google Sign-In
Google Sign-Inと言っているのはこれのこと。 developers.google.com
こちらにしたがって、Google Cloud Platformの認証情報を作成し、クライアントIDを取得する。「承認済みの JavaScript 生成元」として localhost:3000
を指定しておく。
取得できたら早速使ってみる。まずはReactなしで試す。必要なのは以下の4つ。
- metaタグに取得したクライアントIDを指定する
https://apis.google.com/js/platform.js
を読み込むscriptタグを用意するg-signin2
というclass属性のついたdivタグを用意する- 上のdivタグに
data-onsuccess
属性でログイン成功時のコールバックを指定する
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>sample</title> <meta name="google-signin-client_id" content="<CLIENT_ID>"> <script src="https://apis.google.com/js/platform.js" async defer></script> </head> <body> ようこそ! <div class="g-signin2" data-onsuccess="onSignIn"></div> <script> function onSignIn(googleUser) { var profile = googleUser.getBasicProfile(); console.log('ID: ' + profile.getId()); console.log('Name: ' + profile.getName()); console.log('Image URL: ' + profile.getImageUrl()); console.log('Email: ' + profile.getEmail()); } </script> </body> </html>
<CLIENT_ID>
はGoogle Cloud Platformのコンソールで取得した値にすること。適当なWebサーバーを3000ポートで起動しブラウザで localhost:3000
にアクセスする。
ログインボタンをクリックしてGoogleアカウントでログインする。ブラウザのコンソール出力を見ると以下のような出力が。
ID: 111 Name: hoge Image URL: https://lh4.googleusercontent.com/fuga/photo.jpg Email: foo.bar@gmail.com
おぉー、簡単!
Reactと一緒に使う
ReactアプリケーションでGoogle Sign-Inを使ってみる。
create-react-appでアプリケーションの雛形を作成する。使った create-react-app
のバージョンは
$ create-react-app --version 1.5.2
package.jsonのdependencies。
"dependencies": { "react": "^16.3.0", "react-dom": "^16.3.0", "react-scripts": "1.1.1" },
雑にさっきのコードをsrc/App.js
に移植する。jsxでは class
属性は className
と書くのでそこは直しておく。
class App extends Component { onSignIn = (googleUser) => { var profile = googleUser.getBasicProfile(); console.log('ID: ' + profile.getId()); console.log('Name: ' + profile.getName()); console.log('Image URL: ' + profile.getImageUrl()); console.log('Email: ' + profile.getEmail()); } render() { return ( <div> ようこそ! <div className="g-signin2" data-onsuccess="onSignIn"></div> </div> ) } }
$ npm start
して、 localhost:3000
にアクセス。
む、ログインボタンが表示されない。metaタグとscriptタグを追加していなかった。public/index.html
にmetaタグとscriptタグを追加する。
--- a/public/index.html +++ b/public/index.html @@ -19,6 +19,8 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> + <meta name="google-signin-client_id" content="<CLIENT_ID>"> + <script src="https://apis.google.com/js/platform.js" async defer></script> <title>React App</title> </head> <body>
表示された。ログインしてブラウザのコンソール出力を見ると...
何も表示されない。 data-onsuccess="onSignIn"
で指定したコールバックが呼ばれないですね。まぁそりゃそうだなぁ。
react-google-login
どうしよっかなぁと思ってGitHubを眺めてるとreact-google-loginというのを発見。
試してみたけど、ログインボタンをrenderするときにクライアントIDを指定する方式になっていて、ログイン済みの状態でアクセスした時にログアウトボタンだけ欲しい場合に都合が悪かったので使わないことにする。
JavaScriptコードからログインボタンを作成する
Googleのドキュメントを読んでみると、自分のJavaScriptコードでログインボタンを作成できることが分かる。この時にコールバックを指定できる。この方法を使えば以下のような感じでログインボタンを描けそう。
gapi.signin2.render('google-signin-button', { 'onsuccess': this.onSignIn, 'onfailure': (err) => console.error(err) });
さらにリファレンスを見ると、metaタグでclient_idを指定してたけど、これもJavaScriptで指定できるようだ。
gapi.load('auth2', () => { gapi.auth2.init({client_id: <CLIENT_ID>}) .then( (result) => console.log(result), (err) => console.error(err) ); })
これらを使って、 initSignInButton
メソッドを追加する。
--- a/src/App.js +++ b/src/App.js @@ -1,6 +1,20 @@ import React, { Component } from 'react'; class App extends Component { + initSignInButton = (gapi) => { + gapi.load('auth2', () => { + gapi.auth2.init({client_id: <CLIENT_ID>}) + .then( + (result) => { + gapi.signin2.render('google-signin-button', { + 'onsuccess': this.onSignIn, + 'onfailure': (err) => console.error(err) + }); + }, + (err) => console.error(err) + ); + }) + } onSignIn = (googleUser) => { var profile = googleUser.getBasicProfile(); console.log('ID: ' + profile.getId()); @@ -26,7 +42,7 @@ class App extends Component { return ( <div> ようこそ! - <div className="g-signin2" data-onsuccess="onSignIn"></div> + <div id='google-signin-button'></div> </div> ) }
あとは gapi
オブジェクトをどうやって取得するか。Googleのドキュメントのサンプルにある <script src="https://apis.google.com/js/platform.js?onload=renderButton" async defer></script>
というところ。scriptタグでGoogleのjsファイルを読み込んでるところをどう書き直そう(npmになさそう・・・)。先ほどの react-google-login
のコードを調べてみると、DOMをごにょごにょしてscriptタグを動的に書き込んでた。それを真似する。
--- a/src/App.js +++ b/src/App.js @@ -1,6 +1,19 @@ import React, { Component } from 'react'; class App extends Component { + componentDidMount() { + this.downloadGoogleScript(this.initSignInButton) + } + downloadGoogleScript = (callback) => { + const element = document.getElementsByTagName('script')[0]; + const js = document.createElement('script'); + js.id = 'google-platform'; + js.src = '//apis.google.com/js/platform.js'; + js.async = true; + js.defer = true; + element.parentNode.insertBefore(js, element); + js.onload = () => callback(window.gapi); + } initSignInButton = (gapi) => { gapi.load('auth2', () => { gapi.auth2.init({client_id: <CLIENT_ID>})
public/index.html
に追記したタグは不要になったので消す。
--- a/public/index.html +++ b/public/index.html @@ -19,8 +19,6 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - <meta name="google-signin-client_id" content="<CLIENT_ID>" - <script src="https://apis.google.com/js/platform.js" async defer></script> <title>React App</title> </head> <body>
localhost:3000
にアクセスして、ログインしてみると...うまくいった!
おしまい
次回は、react-routerでログイン前と後で画面分けたりする、予定。
最終的に create-react-app
した後に編集したのは src/App.js
だけ。