[*Cordova*] Googleアカウント認証のやり方

# はじめに
Googleアカウントでの認証を試した手順メモ。
環境は「Cordova＋AngularJS＋OnsenUI＋jQuery」

# Googleアカウント認証用のクライアントID作成
1. [Google Developers Console](https://console.developers.google.com/project)で新規プロジェクトを作成。
2. 作成したプロジェクトを選択し、**Google APIを利用する**を選択。
3. **Google+ API**を有効にする。
4. 左のメニューの認証情報を押下し、OAuth同意画面でメールアドレスとサービス名を登録。
5. 認証情報の認証情報を追加から**OAuth 2.0 クライアントID**を選択し、アプリケーションの種類はウェブアプリケーションにし、必要情報を入力して作成。

# プロジェクト作成
プロジェクトを作成し、プラットフォームを追加。
アプリ内ブラウザプラグインを追加しておく。

```bash
$ cordova create GoogleOAuth
$ cd GoogleOAuth
$ cordova platform add ios
$ cordova plugin add cordova-plugin-inappbrowser
```

## config.xmlの編集
`config.xml`のアプリ名とかの修正。

```xml:config.xml
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.googleOAuth" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>GoogleOAuth</name>
    <description>
        This is a google oauth sample application.
    </description>
    <author email="sample@example.com" href="http://example.com">
        Sample Team
    </author>
<!-- 以下略 -->
```

## htmlの編集
AngularJS、Onsen UI、jQueryが動くように編集。

```html:index.html
<!DOCTYPE html>
<html ng-app="google_oauth">
<head>
  <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">
  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
  <!-- Onsen UI CSS 読み込み -->
  <link rel="stylesheet" href="bower_components/OnsenUI/css/onsenui.css">
  <link rel="stylesheet" href="bower_components/OnsenUI/css/onsen-css-components.css">
  <link rel="stylesheet" type="text/css" href="css/index.css">
  <meta charset="utf-8" />
  <title>Google OAuth</title>
  <!-- AngularJS, Onsen UI, jQuery 読み込み -->
  <script src="bower_components/angular/angular.min.js"></script>
  <script src="bower_components/OnsenUI/js/onsenui.min.js"></script>
  <script src="bower_components/jquery/dist/jquery.min.js"></script>
  <script type="text/javascript" src="cordova.js"></script>
</head>

<body>
  <ons-navigator var="myNavigator" page="page1.html">
  </ons-navigator>

  <script type="text/javascript" src="js/index.js"></script>
</body>
</html>
```

トップページに表示されるログインボタンと、Google認証後に
ユーザー名を取得して表示するインプットテキストを配置。

```html:page1.html
<ons-page ng-controller="GoogleOAuthController">
    <ons-toolbar>
        <div class="center">Navigator</div>
    </ons-toolbar>

    <div style="text-align: center">
        <br>
        <ons-button id="loginButton" ng-click="oauth()" class="login_button">
            <i class="fa fa-google-plus"></i> ログイン
        </ons-button>

        <div>
          <input type="text" id="userName" value=""/>
        </div>
    </div>
</ons-page>
```


# Googleアカウント認証の実装
[Using OAuth 2.0 for Client-side Applications](https://developers.google.com/identity/protocols/OAuth2UserAgent)に書いてあるのでこれを見つつ、調べつつやる。

## JSの編集

```js:index.js
var app = angular.module("google_oauth", ['onsen']);

var accessToken = "";
var tokenType = "";
var expiresIn = "";
var state = "";

app.controller("GoogleOAuthController", function($scope) {
  $scope.oauth = function() {
    // リダイレクトURLは適切なものに変更
    var redirect_url = "http://example.com/GoogleOAuth/OAuth";

    var authorize_url = "https://accounts.google.com/o/oauth2/auth?";
    authorize_url += "scope=" + "email";
    authorize_url += "&state=" + "google_oauth"; // これも適宜変更
    authorize_url += "&redirect_uri=" + redirect_url;
    authorize_url += "&response_type=token";
    authorize_url += "&client_id=" + "最初に作成したClient IDをここに";
    var win = cordova.InAppBrowser.open(authorize_url, '_blank', 'location=yes');
    win.addEventListener('loadstart', function(event) {
      var url = event.url;
      if (url.indexOf(redirect_url + "#") >= 0) {
        accessToken = getParameterValue(url, 'access_token');
        tokenType = getParameterValue(url, 'token_type');
        expiresIn = getParameterValue(url, 'expires_in');
        state = getParameterValue(url, 'state');
        win.close();

        validateToken(accessToken);
      }
    });
  }
});

function getParameterValue(url, name) {
  var parameter = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");

  var regex_string = "[\\#&]" + parameter + "=([^&#]*)";
  var regex = new RegExp(regex_string);
  var results = regex.exec(url);
  if (results == null) {
    return "";
  } else {
    return results[1];
  }
}

function validateToken(token) {
  $.ajax({
    type: 'GET',
    url: "https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=" + token,
    data: null,
    success: function(responseText) {
      getUserInfo();
    },
    error: function(xhr, status, errThrown) {
      alert(xhr.status + " : " + xhr.statusText);
    },
    dataType: "jsonp"
  });
}

function getUserInfo() {
  $.ajax({
    url: 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + accessToken,
    data: null,
    success: function(response) {
      $('#userName').val(response["name"]);
    },
    error: function(xhr, status, errThrown) {
      alert(xhr.status + " : " + xhr.statusText);
    },
    dataType: "jsonp"
  });
}
```

ただ、このままだとajaxの部分で下記のようなセキュリティポリシー系のエラーで怒られる。

```
Refused to execute inline event handler 
because it violates the following Content Security Policy directive: 
"default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'". 
.......以下略
```

`default-src`の`'self'`の隣に`https://www.googleapis.com/`を追加するとエラーは出なくなる。

```html:index.html
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://www.googleapis.com/ data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
```

## scopeについて
今回は`email`にしてますが、`email`だとこんな値が取れてるみたいです。

|値|説明|
|:--|:--|
|id|ID|
|email|メールアドレス|
|verified_email|メールアドレスの有効性（true/false）|
|name|名前＋苗字|
|given_name|名前|
|family_name|苗字|
|link|Google+へのリンク|
|picture|画像のURL|
|gender|性別（male/female）|

試しに`profile`に編集してみて、取得できたのが以下。

|値|説明|
|:--|:--|
|id|ID|
|name|名前＋苗字|
|given_name|名前|
|family_name|苗字|
|link|Google+へのリンク|
|picture|画像のURL|
|gender|性別（male/female）|
|locale|ロケール（ja）|

指定できるものは[Authorizing API requests](https://developers.google.com/+/web/api/rest/oauth#login-scopes)を参照。

# 動作確認
シミュレータで動作確認。

```bash
$ cordova emulate ios
```

ログインボタンを押すとアプリ内ブラウザが立ち上がり、認証が終わると閉じて、トップページのインプットテキストに取得した名前が表示される。

![スクリーンショット 2015-12-19 12.48.41.png](https://qiita-image-store.s3.amazonaws.com/0/48912/d59237af-af1f-a33a-5285-6005ce6508aa.png)

# 参考
* [Authorizing API requests](https://developers.google.com/+/web/api/rest/oauth#login-scopes)
* [Using OAuth 2.0 for Client-side Applications](https://developers.google.com/identity/protocols/OAuth2UserAgent)
* [Authentication and Authorization for Google APIs in Javascript popup window – Tutorial](http://www.gethugames.in/2012/04/authentication-and-authorization-for-google-apis-in-javascript-popup-window-tutorial.html#comment-33960)