読者です 読者をやめる 読者になる 読者になる

Google OAuth で認証できるところまで動かしてみた(後編)

はじめに

前編では、Android Studio にプロジェクトを作成して、 backend モジュールを動かすところまでを書きました。
Google OAuth で認証できるところまで動かしてみた(前編) - NOSIX

後編では、Google の OAuth を利用して認証するところまで書きます。

Google Developer Console でプロジェクトを作成

前編では GAE をローカル環境で動かすところまでになっていますので、サーバー上で動かすために Google Developer Console でプロジェクトを作成します。


プロジェクトの認証情報を設定

OAuth を利用するために Client ID を取得する必要があります。
Google Developer Console > API Manager > 認証情報。



OAuth 2.0 Client ID を選択します。

ユーザーに表示される同意画面の設定を行います。サービス名はユーザーに表示されるので、ユーザーにとってわかりやすい名前を。


Client ID を発行

ウェブアプリケーション用の Client ID を発行します。名前の項目は何に使用されるか不明ですが、たぶん Google Developer Console 上でクライアントを区別するために使う程度かと思います。アプリ作成者がわかる名前をつければよいのかと思います。「承認済みの…」項目は、Google Endpoints を使用する今回の手順では不要です。


ソースコードを編集

準備が整ったので、ソースコードを変更します。サーバー側 MyEndpoint.kt の API 定義に scopes, clientIds を追加し、メソッドに未認証時の処理を追加します。

...
import com.google.appengine.api.oauth.OAuthRequestException
import com.google.appengine.api.users.User
...

@Api(name = "myApi", version = "v1",
        scopes = arrayOf("https://www.googleapis.com/auth/userinfo.email"),
        clientIds = arrayOf("発行された Client ID"),
        namespace = ApiNamespace(
                ownerDomain = "backend.sayhello.anyspirit.org",
                ownerName = "backend.sayhello.anyspirit.org",
                packagePath = ""
        )
)
class MyEndpoint {

    /** A simple endpoint method that takes a name and says Hi back  */
    @ApiMethod(name = "sayHi")
    fun sayHi(@Named("name") name: String, auth: User?): MyBean {
        auth ?: throw OAuthRequestException("You need auth.")
        val response = MyBean()
        response.data = "Hi, " + name

        return response
    }

}


未認証の場合には、OAuthRequestException を投げるようにするみたいです。
Using Auth with Endpoints - Java — Google Cloud Platform

クライアント側 index.html を編集します。

...
        <div class="row">
            <a class="btn btn-primary" href="#" onclick="auth();">Sign in!</a>
            <a class="btn btn-primary" href="#" onclick="signout();">Sign out!</a>
        </div>
...
<script type="text/javascript">
    // A function that attaches a "Say Hello" button click handler
    function enableClick() {
      document.getElementById('helloButton').onclick = function() {
        var name = document.getElementById('nameInput').value;
        gapi.client.myApi.sayHi({'name': name}).execute(
          function(response) {
            var outputAlertDiv = document.getElementById('outputAlert');
            outputAlertDiv.style.visibility = 'visible';

            if (!response.error) {
              outputAlertDiv.className = 'alert alert-success';
              outputAlertDiv.innerHTML = '<h2>' + response.result.data + '</h2>';
            }
            else if (response.error) {
              outputAlertDiv.className = 'alert alert-danger';
              outputAlertDiv.innerHTML = '<b>Error Code: </b>' + response.error.code + ' [' + response.error.message + ']';
            }
          }
        );
        return false;
      }
    }

    SCOPES = ['https://www.googleapis.com/auth/userinfo.email'];
    CLIENT_ID = '発行された Client ID';

    function init() {
      var apisToLoad;
      var loadCallback = function() {
        if (--apisToLoad == 0) {
          //signin(true, userAuthed);  // 読み込み完了時に自動的に認証
        }
      };

      apisToLoad = 2; // must match number of calls to gapi.client.load()
      var apiRoot = 'https://' + window.location.host + '/_ah/api';
      if (window.location.hostname == 'localhost'
          || window.location.hostname == '127.0.0.1'
          || ((window.location.port != "") && (window.location.port > 1023))) {
            // We're probably running against the DevAppServer
            apiRoot = 'http://' + window.location.host + '/_ah/api';
      }
      gapi.client.load('myApi', 'v1', loadCallback, apiRoot);
      gapi.client.load('oauth2', 'v2', loadCallback);
    }

    function signin(mode, authorizeCallback) {
      gapi.auth.authorize({
        client_id: CLIENT_ID,
        scope: SCOPES,
        immediate: mode},
        authorizeCallback);
    }

    function userAuthed() {
      var request =
          gapi.client.oauth2.userinfo.get().execute(function(resp) {
        if (!resp.code) {
          enableClick();
        }
      });
    }

    function auth() {
      signin(false, userAuthed);
    }

    function signout() {
      gapi.auth.setToken(null);
    }
</script>


enableClick function は元々のままです。init function は参考サイトの内容を持ってきています。
Using Endpoints in a JavaScript Client - Java — Google Cloud Platform

動作確認

完成したので動かしてみます。未認証状態では、Say Hello ボタンを押しても何もおこりません。userAuthed function にて認証完了時に enableClick function を実行するようになっています。

sign in ボタンを押すとリクエスト許可を求める画面がでるので、許可します。前に設定したサービス名が表示されています。

認証済みの状態で Say Hello ボタンを押してみると結果が返ってきました。

sign out ボタンを押して再び未認証の状態に戻します。この状態で Say Hello を押すとエラーが発生します。

以上で、Google Endpoints + Google OAuth 2.0 での認証の試行は終わりです。