GDK Glassware の認証

GDK Glassware がウェブサービスに対してユーザーを認証する必要がある場合、GDK には、ユーザーが Glassware をインストールするときに認証情報を入力できる API が用意されています。

この API を使用すると、Glass ユーザーに一貫したユーザー エクスペリエンスを提供し、独自のカスタム認証スキームを実装するオーバーヘッドを回避できます。

Google API サービス アカウントの作成

認証が正しく設定されると、ウェブアプリのバックエンドは Mirror API を使用して、サービスに対する認証後にユーザーのアカウント情報を Glass に push します。

この API にアクセスするには、Google API プロジェクトを作成し、「サービス アカウント」のクライアント ID を作成します(「ウェブ アプリケーション」ではありません)。サービス アカウントを使用すると、ユーザーは自分の認証情報を Glass にプッシュする権限をアプリケーションに付与する必要がなく、OAuth 権限ページと独自の認証ページの両方を再び表示する必要がなくなります。

このアカウントを作成するには:

  1. Google Developers Console にアクセスします。
  2. [プロジェクトを作成] ボタンをクリックし、必要な情報を入力します。
  3. プロジェクトを作成したら、プロジェクト番号をメモしておきます。これは後で必要になります。
  4. [API と認証] で [API] をクリックし、新しいプロジェクトに対して Google Mirror API を有効にします。
  5. [API と認証] で [認証情報]、[新しいクライアント ID を作成] の順にクリックします。[サービス アカウント] というラベルの付いたチェックボックスをオンにして、プロジェクトの新しい OAuth 2.0 クライアント ID を作成します。
  6. 秘密鍵がパソコンにダウンロードされていることを示すポップアップ ウィンドウに、その秘密鍵のパスワードが表示されます。このウィンドウを閉じると、この秘密鍵をダウンロードしたり、パスワードを表示したりできなくなります。紛失した場合は、新規に作成する必要があります。
  7. 後で API 呼び出しを行うときに必要になる、サービス アカウントのメールアドレスをメモしておきます。

Glassware に関するメタデータの指定

Glassware を送信する準備ができたら、次の情報を提供する必要があります。これにより、Glassware を実装する際に正しく認証されるように設定できるようになります。

  • 認証 URL。ユーザーが MyGlass で Glassware を有効にしたときにリダイレクトされます。
  • アカウント タイプ(Glass デバイスで Android AccountManager API を呼び出すときに使用する文字列)
  • AndroidManifest.xml でのアプリのパッケージ名
  • 上記で作成したプロジェクトの数値の Google API プロジェクト ID
  • MyGlass にアップロードする APK。テストにおいて、MyGlass から Glassware を有効にした場合に、この APK を一度だけダウンロードすれば済みます。その後は、デバイス上で APK を上書きすることにより、ローカルで反復処理とデバッグを行うことができます。なお、この APK は次の条件を満たす必要があります。
    • zip で揃えてください。
    • これ以降は、パッケージ名や秘密署名鍵を変更しないでください(Android パッケージ マネージャーは、これらの変更のいずれかを行ってもアップグレードを許可しません)。
    • 50 MB 未満にしてください。
    • GDK の最新バージョンを使用してコンパイルする必要があります。

認証フローの実装

次の図は、GDK Glassware の基本認証フローを示しています。

認証フローを実装するには:

  1. ユーザーが MyGlass で Glassware を有効にすると、認証 URL にリダイレクトされます。これらのリクエストには、後で使用する必要がある userToken という名前のクエリ パラメータが含まれています。

  2. ユーザーが認証ページで認証情報を入力します。

  3. サーバーがユーザーの認証情報を検証します。認証情報が有効な場合は、mirror.accounts.insert メソッドに Mirror API の呼び出しを行います。このメソッドでは、Mirror サービス オブジェクトをビルドするときに https://www.googleapis.com/auth/glass.thirdpartyauth スコープを指定する必要があります。未加工の HTTP または Java を使用してこの API 呼び出しを行う例については、アカウントの作成例をご覧ください。

    下記のパラメータとリクエスト本文は、デバイスでアカウントを直接作成した場合に Android の AccountManager に提供したものと同じ情報を表します。

    プロパティ名 説明
    features[] 文字列のリスト 機能のリスト(AccountManager.hasFeatures を参照)。
    password 文字列 アカウントのパスワード(AccountManager.getPassword を参照)。ユーザーの実際のパスワードをこのフィールドに保存するのではなく、有効期間が長い更新トークンなどのプライベート データを保存するために使用することをおすすめします。
    userData[] オブジェクトのリスト アカウントに関連付けられている 1 組のユーザーデータ(AccountManager.getUserData を参照)。
    userData[].key 文字列 特定のユーザーデータの Key-Value ペアに関連付けられたキー。
    userData[].value 文字列 特定のユーザー Key-Value ペアに関連付けられた値。
    authTokens[] オブジェクトのリスト アカウントに関連付けられている 1 つ以上の認証トークン(AccountManager.getAuthToken を参照)。
    authTokens[].type 文字列 認証トークンのタイプ。
    authTokens[].authToken 文字列 認証トークン
  4. mirror.account.insert リクエストを受信すると、Mirror API はアカウントをユーザーの Glass デバイスに push します。これにより、AccountManager クラスを使用してそのアカウントにアクセスできます。

ユーザー フレンドリーな認証フローを実装する手順は次のとおりです。

  • モバイル デバイス向けにフローを最適化します。
  • フローにスコープがあり、ユーザーがキャンセルした場合は、適切に設計されたエラー メッセージを表示します。
  • リクエストしたスコープが Glassware で実際に使用されていることを確認します。
  • ユーザー アカウントが接続可能な場合は、必ず接続してください。
  • 可能であれば、ユーザーデータはクラウドにバックアップしてください。

Glassware の認証で一貫性を維持するには、次のいずれかの認証フローを使用します。

アカウントなしでミラーまたはハイブリッド

  1. MyGlass で切り替えると、認証 URL がポップアップで開きます。
  2. これにより、ユーザーは承認するスコープに直接送信されます。
  3. ユーザーがスコープを承認またはキャンセルしたら、ポップアップを閉じます。

アカウントでミラーリングする

  1. MyGlass に切り替えると、認証 URL がポップアップで開きます。
    • ユーザーがすでにサービスにログインしている場合は、ユーザーをスコープに直接送信します。
    • ユーザーがログインしていない場合は、ログイン フィールドを表示し、ユーザーがサービスにログインできるようにしてから、スコープに送信します。
    • ユーザーがアカウントを持っていない場合は、アカウントを作成するためのリンクを提供します。ユーザーは、インストール フローのプロセスでアカウントを作成する方法を用意する必要があります。
  2. ユーザーがスコープを受け取ります。
    • Glassware で構成可能な設定が行われている場合は、妥当なデフォルトが選択された状態で、設定設定ページにユーザーを誘導します。
    • Glassware に設定可能な設定がない場合は、確認ページにユーザーを誘導します。追加の構成が不要な場合は、ポップアップを閉じます。

アカウントによるハイブリッド

  1. MyGlass に切り替えると、認証 URL がポップアップで開きます。
    • ユーザーがすでにサービスにログインしている場合は、ユーザーをスコープに直接送信します。
    • ユーザーがログインしていない場合は、ログイン フィールドを表示し、ログインを許可してから、スコープに送信します。
    • ユーザーがアカウントを持っていない場合は、アカウントを作成するためのリンクを提供します。
  2. ユーザーがスコープを承認します。
  3. Mirror API にリクエストを送信して GDK アカウントを挿入します。
    • 妥当なデフォルトが選択された状態で、設定ページにユーザーを誘導します。
    • 確認ページをユーザーに送信します。追加の構成が不要な場合は、ポップアップを閉じます。

アカウントとカスタム スコープでのミラーリングまたはハイブリッド

  1. MyGlass に切り替えると、認証 URL がポップアップで開きます。
    • ユーザーがすでにサービスにログインしている場合は、ユーザーを内部スコープに誘導します。
    • ユーザーがログインしていない場合は、ログイン フィールドを表示してユーザーにログインを許可し、内部スコープに送信します。
    • ユーザーがアカウントを持っていない場合は、アカウントを作成するためのリンクを提供します。
  2. ユーザーがカスタム スコープを受け入れたら、Google のスコープにお客様を送信します。
  3. Mirror API にリクエストを送信して GDK アカウントを挿入します。
    • 妥当なデフォルトが選択された状態で、設定ページにユーザーを誘導します。
    • 確認ページをユーザーに送信します。追加の構成が不要な場合は、ポップアップを閉じます。

Android/iPhone アプリのミラーリングまたはハイブリッド

  1. MyGlass で切り替えると、認証 URL がポップアップで開きます。
  2. これにより、ユーザーは承認するスコープに直接送信されます。
  3. ユーザーがスコープを受け入れた後:
    • ユーザーがコンパニオン アプリを持っていて認証されている場合は、ポップアップ ウィンドウを閉じます。
    • まだ公開されていない場合は、Google Play ストアまたは iOS ストアからアプリをダウンロードするように指示するインタースティシャルに誘導します。
  4. アプリをインストールして認証したら、ポップアップ ウィンドウを閉じます。

GDK とアカウントなし

このフローに必要なのは、MyGlass で Glassware を切り替えることだけです。

アカウントでの GDK

  1. MyGlass に切り替えると、認証 URL がポップアップで開きます。
    • ユーザーがすでにサービスにログインしている場合は、確認画面に移動します。
    • ユーザーがログインしていない場合は、ログイン フィールドを表示してユーザーにログインさせ、確認画面に誘導します。
    • ユーザーがアカウントを持っていない場合は、アカウントを作成するためのリンクを提供します。
  2. ユーザーがスコープを承認します。
  3. Mirror API にリクエストを送信して GDK アカウントを挿入します。
  4. 確認画面を表示し、短時間表示した後にその画面を閉じます。

アカウントの作成例

可能であれば、Mirror API のクライアント ライブラリを使用します。これにより、mirror.accounts.insert を呼び出してアカウントを作成しやすくなります。

未加工の HTTP の例

以下の例は、リクエストの URL と、想定される JSON 本文の例を示しています。サービス アカウントに代わって未加工の HTTP リクエストを作成するのはより複雑です(詳細については、サーバー間アプリケーションでの OAuth 2.0 の使用をご覧ください)。このため、この処理を容易にするために、Google API のクライアント ライブラリのいずれかを使用することをおすすめします。

リクエスト方法と URL:

POST https://www.googleapis.com/mirror/v1/accounts/{userToken}/com.example.myapp/username%40email.com

リクエスト本文:

{
    "features": ["a", "b", "c"],
    "userData": [
        { "key": "realName", "value": "Rusty Shackleford" },
        { "key": "foo", "value": "bar" }
    ],
    "authTokens": [
        { "type": "your_token_type", "authToken": "zT419Ma3X2pBr0L..." }
    ]
}

{userToken} は、認証フローの実装の手順 1 で認証 URL に渡されたトークンに置き換えます。

Java の例

次の例は、Java クライアント ライブラリを使用して mirror.accounts.insert を呼び出す方法を示しています。

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.mirror.Mirror;
import com.google.api.services.mirror.model.Account;
import com.google.api.services.mirror.model.AuthToken;
import com.google.common.collect.Lists;
...

/** Email of the Service Account */
private static final String SERVICE_ACCOUNT_EMAIL =
    "<some-id>@developer.gserviceaccount.com";

/** Path to the Service Account's Private Key file */
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH =
    "/path/to/<public_key_fingerprint>-privatekey.p12";

/** The account type, usually based on your company or app's package. */
private static final String ACCOUNT_TYPE = "com.example.myapp";

/** The Mirror API scopes needed to access the API. */
private static final String MIRROR_ACCOUNT_SCOPES =
    "https://www.googleapis.com/auth/glass.thirdpartyauth";

/**
 * Build and returns a Mirror service object authorized with the service accounts.
 *
 * @return Mirror service object that is ready to make requests.
 */
public static Mirror getMirrorService() throws GeneralSecurityException,
    IOException, URISyntaxException {
  HttpTransport httpTransport = new NetHttpTransport();
  JacksonFactory jsonFactory = new JacksonFactory();
  GoogleCredential credential = new GoogleCredential.Builder()
      .setTransport(httpTransport)
      .setJsonFactory(jsonFactory)
      .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
      .setServiceAccountScopes(MIRROR_ACCOUNT_SCOPES)
      .setServiceAccountPrivateKeyFromP12File(
          new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
      .build();
  Mirror service = new Mirror.Builder(httpTransport, jsonFactory, null)
      .setHttpRequestInitializer(credential).build();
  return service;
}

/**
 * Creates an account and causes it to be synced up with the user's Glass.
 * This example only supports one auth token; modify it if you need to add
 * more than one, or to add features, user data, or the password field.
 *
 * @param mirror the service returned by getMirrorService()
 * @param userToken the user token sent to your auth callback URL
 * @param accountName the account name for this particular user
 * @param authTokenType the type of the auth token (chosen by you)
 * @param authToken the auth token
 */
public static void createAccount(Mirror mirror, String userToken, String accountName,
    String authTokenType, String authToken) {
  try {
    Account account = new Account();
    List<AuthToken> authTokens = Lists.newArrayList(
        new AuthToken().setType(authTokenType).setAuthToken(authToken));
    account.setAuthTokens(authTokens);
    mirror.accounts().insert(
        userToken, ACCOUNT_TYPE, accountName, account).execute();
  } catch (IOException e) {
    e.printStackTrace();
  }
}

Glass でのアカウントの取得

Glass で Account オブジェクトを取得して使用する方法は、標準の Android AccountManager を使用する場合と同様です。

  1. AndroidManifest.xml ファイルで次のマニフェスト権限を宣言します。

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    
  2. Glassware のアカウントを取得します。

    AccountManager accountManager = AccountManager.get(mContext);
    // Use your Glassware's account type.
    Account[] accounts = accountManager.getAccountsByType("com.example");
    
    // Pick an account from the list of returned accounts.
    
  3. Account から認証トークンを取得します。

    // Your auth token type.
    final String AUTH_TOKEN_TYPE = "oauth2:https://www.example.com/auth/login";
    
    accountManager.getAuthToken(account, AUTH_TOKEN_TYPE, null, activity, new AccountManagerCallback<Bundle>() {
        public void run(AccountManagerFuture<Bundle> future) {
            try {
                String token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
                // Use the token.
            } catch (Exception e) {
                // Handle exception.
            }
        }
    }, null);