ウェアラブルでの認証: 認証情報マネージャー

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

このガイドでは、Wear OS アプリで推奨される認証方法である Credential Manager の手順について説明します。

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

予備的な考慮事項

実装を開始する前に、次の点を考慮してください。

ゲストモード

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

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

一部のデバイスではロックが解除された状態が長く続くことがあります

Wear OS 5 以降を搭載するサポート対象デバイスでは、ユーザーがデバイスを手首に装着しているかどうかが検出されます。ユーザーが手首検出をオフにしてデバイスを手首から外すと、システムはデバイスのロックを通常よりも長い時間解除したままにします。

アプリでより高いレベルのセキュリティが必要な場合(機密情報や個人情報が表示される可能性がある場合など)は、まず手首検出が有効になっているかどうかを確認します。

fun isWristDetectionAutoLockingEnabled(context: Context): Boolean {

このメソッドの戻り値が false の場合は、ユーザー固有のコンテンツを表示する前に、アプリのアカウントにログインするようユーザーに促します。

認証情報マネージャー

Wear OS での認証には、認証情報マネージャー API を使用することをおすすめします。これにより、ペア設定されたスマートフォンを接続したり、パスワードを記憶したりすることなく、スタンドアロン設定で Wear OS アプリケーションにログインするための、より安全な環境が提供されます。

このドキュメントでは、デベロッパーがホストする標準の認証メカニズム(次のもの)を使用して、Credential Manager ソリューションを実装するために必要な情報について説明します。

  • パスキー
  • パスワード
  • フェデレーション ID(Google でログインなど)

このガイドでは、認証情報マネージャーのバックアップとして、他の許容される Wear OS 認証方法(データレイヤ トークン共有OAuth)を移行する方法についても説明します。また、現在非推奨となっているスタンドアロンの「Google でログイン」ボタンから、埋め込み型の認証情報マネージャー バージョンへの移行を処理するための特別な手順についても説明します。

Wear OS の制限事項と相違点

デベロッパーは、Wear OS の次の制限事項と相違点に注意する必要があります。

  • 認証情報マネージャーは Wear OS 4 以降でのみ利用できます。
  • Wear OS で認証情報を作成できない
  • 「認証情報の復元」とハイブリッド ログインフローはどちらもサポートされていません。
  • モバイルから再利用できるのは、Wear OS と統合された認証情報プロバイダのみです。

Wear OS のパスキー

デベロッパーは、Wear OS の Credential Manager の実装でパスキーを実装することが強く推奨されます。パスキーはエンドユーザー認証の新しい業界標準であり、ユーザーにとっていくつかの大きなメリットがあります。

パスキーは簡単

  • ログインに使用するアカウントを選択できます。 ユーザー名を入力する必要はありません。
  • ユーザーはデバイスの画面ロックを使用して認証できます。
  • パスキーを作成して登録すると、ユーザーは新しいデバイスにシームレスに切り替えて、再登録することなくすぐに使用できます。

パスキーは安全

  • デベロッパーはパスワードを保存する代わりに公開鍵のみをサーバーに保存するため、悪意のあるユーザーがサーバーをハッキングする価値が大幅に減り、侵害が発生した場合のクリーンアップも大幅に減ります。
  • パスキーは、フィッシング対策に有効な保護機能です。パスキーは登録したウェブサイトやアプリでのみ機能します。ブラウザまたは OS が検証を行うため、ユーザーが不正なサイトで認証を行うように騙されることはありません。
  • パスキーを使用すると SMS を送信する必要がなくなるため、認証の費用対効果が高まります。

パスキーを実装する

すべての実装タイプのセットアップとガイダンスが含まれています。

設定

  1. アプリケーション モジュールの build.gradle ファイルで、ターゲット API レベルを 35 に設定します。

    android {
        defaultConfig {
            targetSdkVersion(35)
        }
    }
    
  2. androidx.credentials リリースの参照から最新の安定版を使用して、アプリまたはモジュールの build.gradle ファイルに次の行を追加します。

    androidx.credentials:credentials:1.5.0
    androidx.credentials:credentials-play-services-auth:1.5.0
    

組み込みの認証方法

Credential Manager は統合 API であるため、Wear OS の実装手順は他のデバイスタイプと同じです。

モバイル向けの手順に沿って、パスキーとパスワードのサポートの実装を開始してください。

認証情報マネージャーに「Google でログイン」のサポートを追加する手順はモバイル開発を対象としていますが、Wear OS でも同じ手順です。

Wear OS では認証情報を作成できないため、モバイルの手順で説明されている認証情報作成メソッドを実装する必要はありません。

バックアップ認証方法

Wear OS アプリで許容される認証方法は、OAuth 2.0(いずれかのバリエーション)とモバイル認証トークン データレイヤ共有の 2 つです。これらのメソッドは Credential Manager API に統合ポイントはありませんが、ユーザーが Credential Manager 画面を閉じるときにフォールバックとして Credential Manager の UX フローに含めることができます。

認証情報マネージャー画面を閉じるユーザー アクションを処理するには、GetCredential ロジックの一部として NoCredentialException をキャッチし、独自のカスタム認証 UI に移動します。

try {
    val getCredentialResponse: GetCredentialResponse =
        credentialManager.getCredential(activity, createGetCredentialRequest())
    return authenticate(getCredentialResponse.credential)
} catch (_: GetCredentialCancellationException) {
    navigateToSecondaryAuthentication()
}

カスタム認証 UI は、ログイン UX ガイドで説明されている他の許容可能な認証方法を提供できます。

データレイヤートークンの共有

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

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

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

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

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

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

class AuthDataListenerService : WearableListenerService() {
    override fun onDataChanged(dataEvents: DataEventBuffer) {
        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 an interstitial screen to notify the user that they're 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(DAG)
Proof Key for Code Exchange(PKCE)

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

次のコードは、認証リクエストの作成例を示しています。

val oauthRequest = OAuthRequest.Builder(context)
    .setAuthProviderUrl(uri)
    .setCodeChallenge(codeChallenge)
    .setClientId(CLIENT_ID)
    .build()

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

RemoteAuthClient.create(context).sendAuthorizationRequest(
    request = oauthRequest,
    executor = { command -> command?.run() },
    clientCallback = object : RemoteAuthClient.Callback() {
        override fun onAuthorizationResponse(
            request: OAuthRequest,
            response: OAuthResponse
        ) {
            // Extract the token from the response, store it, and use it in requests.
            continuation.resume(parseCodeFromResponse(response))
        }
        override fun onAuthorizationError(request: OAuthRequest, errorCode: Int) {
            // Handle Errors
            continuation.resume(Result.failure(IOException("Authorization failed")))
        }
    }
)

このリクエストによってコンパニオン アプリの呼び出しがトリガーされ、ユーザーのスマートフォンのウェブブラウザに認証 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 を使用して Wear OS アプリにレスポンスを中継します。

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

Device Authorization Grant

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

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

// Request access from the authorization server and receive Device Authorization Response.
private fun verifyDeviceAuthGrant(verificationUri: String) {
    RemoteActivityHelper(context).startRemoteActivity(
        Intent(Intent.ACTION_VIEW).apply {
            addCategory(Intent.CATEGORY_BROWSABLE)
            data = Uri.parse(verificationUri)
        },
        null
    )
}

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