ウェアラブルでの認証

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

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

ゲストモード

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

過去にモバイルアプリを使用したことがないユーザーでも 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 は、以下の OAuth 2.0 ベースのフローをサポートしています。

  • RFC 7636 で定義されている Authorization Code Grant with PKCE(Proof Key for Code Exchange)。
  • 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

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

パスワード ベースの認証

前述の OAuth 2.0 フローのいずれかをサーバーがサポートしていない場合は、ユーザーに対してユーザー名とパスワードのプロンプトを表示します。

利用可能な保存済みパスワードがあればユーザーに提供されるようにするために、自動入力用のヒントは必ず含めてください。次の例では、レイアウト ファイルの EditText にパスワードのヒントを設定しています。

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:autofillHints="password" />

詳しくは、自動入力用にアプリを最適化するをご覧ください。

その他の認証方法

Wear OS は、その他のログイン方法もサポートしています。

Google ログイン

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

Google ログインはおすすめのソリューションです。iOS でも問題なく機能します。次のセクションで、Google ログインの基本的な統合方法について説明します。

前提条件

Google ログインを Wear OS アプリに組み込む前に、Google API コンソール プロジェクトを構成し、Android Studio プロジェクトをセットアップする必要があります。詳しくは、Google ログインの統合ガイドをご覧ください。

注: バックエンド サーバーと通信するアプリやサイトで Google ログインを使用する場合は、バックエンド サーバー用に OAuth 2.0 ウェブ アプリケーションのクライアント ID を作成します。このクライアント ID は、アプリのクライアント ID とは異なります。詳しくは、サーバーサイドのアクセスを有効にするをご覧ください。

重要: アプリがバックエンド サーバーと通信する場合、HTTPS を使用してユーザーの ID トークンを送信することにより、サーバーでログイン中のユーザーを安全に特定します。バックエンド サーバーでユーザーを認証する方法については、バックエンド サーバーで認証するをご覧ください。

Google ログインをアプリに組み込む

Google ログインを Wear OS アプリに組み込む手順は次のとおりです。

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

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

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

Kotlin

// Configure sign-in to request the user's ID, email address, and basic
// profile. The ID and basic profile are included in DEFAULT_SIGN_IN.
// If you need to request additional scopes to access Google APIs, specify them with
// requestScopes().
googleApiClient = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .build()
        .let { signInConfigBuilder ->
            // Build a GoogleApiClient with access to the Google Sign-In API and the
            // options specified in the sign-in configuration.
            GoogleApiClient.Builder(this)
                    .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
                    .addApi(Auth.GOOGLE_SIGN_IN_API, signInConfigBuilder)
                    .build()
        }

Java

// Configure sign-in to request the user's ID, email address, and basic
// profile. The ID and basic profile are included in DEFAULT_SIGN_IN.
// If you need to request additional scopes to access Google APIs, specify them with
// requestScopes().
GoogleSignInOptions.Builder signInConfigBuilder = new GoogleSignInOptions
        .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .build();

// Build a GoogleApiClient with access to the Google Sign-In API and the
// options specified in the sign-in configuration.
googleApiClient = new GoogleApiClient.Builder(this)
        .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
        .addApi(Auth.GOOGLE_SIGN_IN_API, signInConfigBuilder)
        .build();

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);
    

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

ログインボタンがクリックされたら、onCLick() メソッドでログインボタンのタップを処理します(getSignInIntent メソッドでログイン インテントを作成し、startActivityForResult メソッドでインテントを開始します)。

Kotlin

Auth.GoogleSignInApi.getSignInIntent(googleApiClient).also { signInIntent ->
    startActivityForResult(signInIntent, RC_SIGN_IN)
}

Java

Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);

ユーザーは、ログインに使用する Google アカウントを選択するよう求められます。また、profile、email、openid 以外のスコープをリクエストした場合、ユーザーはそれらのリソースへのアクセスを許可するよう求められます。

最後に、アクティビティの onActivityResult メソッドで、getSignInResultFromIntent を使用してログイン結果を取得します。ログイン結果を取得した後、isSuccess メソッドでログインが成功したかどうかを確認できます。ログインが成功した場合は、getSignInAccount メソッドを呼び出して、ログイン中のユーザーに関する情報(ユーザー名など)を含む GoogleSignInAccount オブジェクトを取得できます。

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
public 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 のサンプルアプリをご覧ください。

カスタムコード認証

この認証方法では、ユーザーは外部ソース(モバイル デバイス、タブレット、パソコン)から認証を行う必要があります。また、身元を証明するために入力する一時的なコードを取得し、Wear OS デバイスでそれを認証トークンと交換する必要があります。この方法では、アプリのログイン モジュールを使用するか、サードパーティの認証プロバイダによるログイン方法をアプリのコードに手動で組み込むことにより、Wear OS アプリでユーザーを認証できます。この認証方法では手動での作業やセキュリティ向上のための追加作業が必要になりますが、スタンドアロンの Wear OS アプリで早い段階で認証を行う必要がある場合はこの方法を使用できます。

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

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

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