ウェアラブルでの認証

Wear OS アプリはコンパニオン アプリがなくてもスタンドアロンで動作できます。つまり、Wear OS アプリは、インターネットからデータにアクセスする際、独自に認証を管理する必要があります。しかし、スマートウォッチは画面サイズが小さく、入力機能が限定されているため、Wear OS アプリで使用できる認証オプションは限られています。

このガイドでは、Wear OS アプリで推奨される認証方法と、それらの方法がアプリのユースケースに適合しない場合の代替方法について説明します。

優れたログイン エクスペリエンスを設計する方法について詳しくは、ログイン UX ガイドをご覧ください。

ゲストモード

すべての機能で認証を求めるのではなく、ログインを必要としない機能をできるだけ多くユーザーに提供してください。

モバイルアプリを使用したことがないユーザーが Wear アプリを見つけてインストールする場合があります。そうしたユーザーはアカウントを持っていないことや、アカウントで提供される機能を知らないことがあります。ゲストモード機能によってアプリの機能が正確に紹介されることを確認してください。

推奨される認証方法

スタンドアロンの Wear OS アプリでユーザー認証情報を取得できるようにするには、次の認証方法を使用します。

データレイヤを使用してトークンを渡す

スマートフォンのコンパニオン アプリは、ウェアラブル データレイヤを使用して認証データを Wear OS アプリに安全に転送できます。認証情報をメッセージまたはデータアイテムとして転送します。

この種の認証では通常、ユーザーによる操作は必要ありません。ただし、ログイン中であることをユーザーに通知せずに認証を行うことは避けてください。閉じることができる簡単な画面を使用して、アカウントがモバイルから移行されることをユーザーに通知できます。

重要: このオプションは、対応するモバイルアプリがインストールされている場合に、Android とペア設定されたスマートウォッチでしか機能しないため、Wear アプリは他の認証方法を少なくとも 1 つ提供する必要があります。対応するモバイルアプリを持っていないユーザーや、Wear OS デバイスが iOS デバイスとペア設定されているユーザー向けに、代替の認証方法を提供してください。

次の例に示すように、モバイルアプリからデータレイヤを使用してトークンを渡します。

val token = "..." // Auth token to transmit to the wearable device.
val dataClient: DataClient = Wearable.getDataClient(context)
val putDataReq: PutDataRequest = PutDataMapRequest.create("/auth").run {
    dataMap.putString("token", token)
    asPutDataRequest()
}
val putDataTask: Task<DataItem> = dataClient.putDataItem(putDataReq)

次の例に示すように、スマートウォッチ アプリのデータ変更イベントをリッスンします。

val dataClient: DataClient = Wearable.getDataClient(context)
dataClient.addListener{ dataEvents ->
    dataEvents.forEach { event ->
        if (event.type == DataEvent.TYPE_CHANGED) {
            val dataItemPath = event.dataItem.uri.path ?: ""
            if (dataItemPath.startsWith("/auth")) {
                val token = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("token")
                // Display interstitial screen to notify the user they are being signed in.
                // Then, store the token and use it in network requests.
            }
        }
    }
}

ウェアラブル データレイヤの使用方法について詳しくは、Wear OS でのデータの送信と同期をご覧ください。

OAuth 2.0 を使用する

Wear OS は 2 つの OAuth 2.0 ベースのフローをサポートしています。これらについては、この後のセクションで説明します。

  • RFC 7636 で定義されている、Proof Key for Code Exchange(PKCE)による Authorization Code Grant
  • RFC 8628 で定義されている Device Authorization Grant

注: スマートウォッチが常に画面表示モードになったときにアプリがシャットダウンしないようにするには、認証を行うアクティビティで AmbientModeSupport.attach を使用して常時オンを有効にします。常に画面表示モードに関するおすすめの方法については、Wear でアプリを表示したままにするをご覧ください。

Proof Key for Code Exchange(PKCE)

PKCE を効果的に使用するには、RemoteAuthClient を使用します。

Wear OS アプリから OAuth プロバイダに認証リクエストを行うには、OAuthRequest オブジェクトを作成します。このオブジェクトは、トークンを取得するための OAuth エンドポイントへの URL と CodeChallenge オブジェクトで構成されます。次のコードは、認証リクエストの作成例を示しています。

val request = OAuthRequest.Builder(this.applicationContext)
    .setAuthProviderUrl(Uri.parse("https://...."))
    .setClientId(clientId)
    .setCodeChallenge(codeChallenge)
    .build()

認証リクエストを作成したら、sendAuthorizationRequest() メソッドを使用してコンパニオン アプリに送信します。

val client = RemoteAuthClient.create(this)
client.sendAuthorizationRequest(request,
    { command -> command?.run() },
    object : RemoteAuthClient.Callback() {
        override fun onAuthorizationResponse(
            request: OAuthRequest,
            response: OAuthResponse
        ) {
            // Extract the token from the response, store it and use it in network requests.
        }

        override fun onAuthorizationError(errorCode: Int) {
            // Handle error
        }
    }
)

このリクエストによってコンパニオン アプリの呼び出しがトリガーされ、ユーザーのスマートフォンのウェブブラウザに認証 UI が表示されます。OAuth 2.0 プロバイダがユーザーを認証し、リクエストされた権限についてユーザーの同意を得ます。レスポンスは、自動生成されたリダイレクト URL に送信されます。

認証が成功または失敗した後、OAuth 2.0 サーバーは、リクエストで指定された URL にリダイレクトします。ユーザーがアクセス リクエストを承認すると、レスポンスに認証コードが格納されます。ユーザーがリクエストを承認しないと、レスポンスにエラー メッセージが格納されます。

レスポンスはクエリ文字列の形式を取り、次の例のようになります。

  https://wear.googleapis.com/3p_auth/com.your.package.name?code=xyz
  https://wear.googleapis-cn.com/3p_auth/com.your.package.name?code=xyz

これにより、ユーザーをコンパニオン アプリに誘導するページが読み込まれます。コンパニオン アプリはレスポンス URL を検証し、onAuthorizationResponse API を使用してサードパーティのスマートウォッチ アプリにレスポンスを中継します。

スマートウォッチ アプリは、認証コードをアクセス トークンと交換できます。

注: OAuthRequest が作成されたら、redirectUrl にアクセスしてリダイレクト URL を確認できます。

Device Authorization Grant

Device Authorization Grant を使用する場合、ユーザーは別のデバイスで検証 URI を開きます。次に、認証サーバーはリクエストを承認または拒否するよう求めます。

このプロセスを簡素化するには、次の例に示すように、RemoteActivityHelper を使用して、ユーザーのペア設定されたモバイル デバイスでウェブページを開きます。

// Request access from the authorization server and receive Device Authorization Response.
val verificationUri = "..." // Extracted from the Device Authorization Response.
RemoteActivityHelper.startRemoteActivity(
    this,
    Intent(Intent.ACTION_VIEW)
        .addCategory(Intent.CATEGORY_BROWSABLE)
        .setData(Uri.parse(verificationUri)),
    null
)
// Poll the authorization server to find out if the user completed the user authorization
// step on their mobile device.

iOS アプリの場合は、トークンの認証にブラウザを使用するのではなく、ユニバーサル リンクを使用して、アプリでこのインテントをインターセプトします。

その他の認証方法

Wear OS は、この後のセクションで説明するその他のログイン方法をサポートしています。

Google ログイン

Google ログインでは、ユーザーは既存の Google アカウントを使用してログインできます。特に、Google ログインをハンドヘルド アプリにすでに実装している場合は、最適なユーザー エクスペリエンスが提供され、サポートが容易になります。

Google ログインは、iOS でも適切に動作するので、前述の推奨される認証方法の次に推奨されるソリューションです。次のセクションでは、Google ログインの基本的な統合方法について説明します。

前提条件

Google ログインを Wear OS アプリに統合するには、Google API Console プロジェクトを構成し、Android Studio プロジェクトをセットアップする必要があります。詳しくは、Android アプリへの Google ログインの統合を開始するをご覧ください。

バックエンド サーバーと通信するアプリまたはサイトで Google ログインを使用する場合は、次の 2 つの前提条件があります。
  • バックエンド サーバー用に OAuth 2.0 ウェブアプリのクライアント ID を作成します。このクライアント ID は、アプリのクライアント ID とは異なります。詳しくは、サーバーサイド アクセスの有効化をご覧ください。
  • サーバーで現在ログイン中のユーザーを安全に特定するには、HTTPS を使用してユーザーの ID トークンを送信します。バックエンド サーバーでユーザーを認証する方法については、バックエンド サーバーで認証するをご覧ください。

Google ログインをアプリに統合する

Google ログインを Wear OS アプリに統合するには、この後のセクションで説明している手順を確認して実施します。

  1. Google ログインを構成します
  2. Google ログインボタンを追加します
  3. ログインボタンがタップされたら、ログインフローを開始します

Google ログインを構成し、GoogleApiClient オブジェクトを作成する

ログイン アクティビティの onCreate() メソッドで、アプリに必要なユーザーデータをリクエストするように Google ログインを構成します。次に、Google Sign-In API と指定したオプションへのアクセス権を持つ GoogleApiClient オブジェクトを作成します。次の例はそれらの手順を示しています。

  public class MyNewActivity extends AppCompatActivity {

    private static final int RC_SIGN_IN = 9001;

    private GoogleSignInClient mSignInClient;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        GoogleSignInOptions options =
               new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                        .build();

        mSignInClient = GoogleSignIn.getClient(this, options);
    }
  }

Google ログインボタンをアプリに追加する

Google ログインボタンを追加する手順は次のとおりです。
  1. アプリのレイアウトに SignInButton を追加します。
  2.  <com.google.android.gms.common.SignInButton
     android:id="@+id/sign_in_button"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content" />
    
  3. アプリの onCreate() メソッドでボタンの OnClickListener を登録し、タップされたときにユーザーがログインできるようにします。
  4. Kotlin

    findViewById<View>(R.id.sign_in_button).setOnClickListener(this)
    

    Java

    findViewById(R.id.sign_in_button).setOnClickListener(this);
    

ログイン インテントを作成し、ログインフローを開始する

getSignInIntent() メソッドでログイン インテントを作成することにより、onCLick() メソッドでログインボタンのタップを処理します。次に、startActivityForResult() メソッドでインテントを開始します。

  Intent intent = mSignInClient.getSignInIntent();
  startActivityForResult(intent, RC_SIGN_IN);

The user is prompted to select a Google account to sign in with. If you requested scopes beyond profile, email, and open ID, the user is also prompted to grant access to those resources.

Finally, in the activity's onActivityResult method, retrieve the sign-in result with getSignInResultFromIntent. After you retrieve the sign-in result, you can check whether the sign-in succeeded using the isSuccess method. If sign-in succeeds, you can call the getSignInAccount method to get a GoogleSignInAccount object that contains information about the signed-in user, such as the user's name. These steps are shown in the following example:

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...).
    if (requestCode == RC_SIGN_IN) {
        Auth.GoogleSignInApi.getSignInResultFromIntent(data)?.apply {
            if (isSuccess) {
                // Get account information.
                fullName = signInAccount?.displayName
                mGivenName = signInAccount?.givenName
                mFamilyName = signInAccount?.familyName
                mEmail = signInAccount?.email
            }
        }
    }
}

Java

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...).
    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult signInResult = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        if (signInResult.isSuccess()) {
            GoogleSignInAccount acct = signInResult.getSignInAccount();

            // Get account information.
            fullName = acct.getDisplayName();
            givenName = acct.getGivenName();
            familyName = acct.getFamilyName();
            email = acct.getEmail();
        }
    }
}

Google ログインを実装したアプリの例については、GitHub の Horologist の Google ログイン サンプルをご覧ください。

カスタムコード認証

前述の認証方法の代わりに、スマートフォンやタブレットなどの別のデバイスから認証を行い、一時的な数値コードを取得することをユーザーに要求する方法もあります。この場合、ユーザーは Wear OS デバイスにコードを入力して本人確認を行い、認証トークンを受け取ります。

この認証フローでは、アプリのログイン モジュールを使用するか、サードパーティ認証プロバイダによるログイン方法をアプリのコードに手動で組み込みます。この認証方法は、手動の作業やセキュリティ向上のための追加作業が必要になりますが、スタンドアロンの Wear OS アプリで早期に認証を行う必要がある場合に使用できます。

この場合の認証フローは次のようになります。

  1. ユーザーが、認証を必要とする Wear OS アプリで操作を行います。
  2. Wear OS アプリがユーザーに認証画面を表示し、指定の URL に移動してコードを入力するようユーザーに伝えます。
  3. ユーザーがモバイル デバイス、タブレット、パソコンのいずれかでブラウザを起動し、Wear OS アプリで指定された URL に移動してログインします。
  4. ユーザーが一時的な数値コードを受け取り、Wear OS のオンボード キーボードを使用して Wear OS アプリの認証画面にそのコードを入力します。

  5. この時点から、入力されたコードを正しいユーザーである証拠として使用できるようになります。また、Wear OS デバイスに保存されて安全に保護されている認証トークンとそのコードを交換し、その後の認証要求で使用できるようになります。

注: ユーザーが生成するコードには、英字を含めず数字のみが含まれるようにする必要があります。

次の図はこの認証フローを示しています。