在穿戴式设备上进行身份验证

Wear OS 应用可以独立运行,而无需配套应用。这意味着 Wear OS 应用从互联网访问数据时需要自行管理身份验证。但手表的屏幕尺寸较小,输入功能较少,这限制了 Wear OS 应用可以使用的身份验证选项。

本指南将介绍为 Wear OS 应用推荐的身份验证方法,以及在这些方法不适用于应用用例时的替代方法。

如需详细了解如何设计良好的登录体验,请参阅登录用户体验指南

访客模式

不要对所有功能都要求进行身份验证,而是应尽可能在不要求用户登录的情况下为其提供更多功能。

用户发现并安装您的 Wear 应用时,可能以前并没有使用过该移动应用,因此他们可能没有账号,也可能不知道该应用提供哪些功能。确保访客模式功能准确展示应用的功能。

建议的身份验证方法

使用以下身份验证方法,使独立 Wear OS 应用能够获取用户身份验证凭据。

使用数据层传递令牌

手机上的配套应用可以使用穿戴式设备数据层将身份验证数据安全地传输到 Wear OS 应用。可以采用消息或数据项的形式传输凭据。

这种类型的身份验证通常不需要用户执行任何操作。不过,请避免在没有通知用户其正在登录的情况下执行身份验证。您可以使用可关闭的简单屏幕通知用户,向他们显示正在从移动设备传输他们的账号凭据。

重要提示:您的 Wear 应用必须至少提供另外一种身份验证方法,因为此选项仅适用于安装了相应移动应用且与 Android 设备完成配对的手表。对于没有安装相应移动应用或所用 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 的流程,后面几个部分对这些流程进行了详细介绍:

  • 采用 PKCE(用于代码交换的证明密钥)的授权代码授权,如 RFC 7636 中所定义
  • 设备授权,如 RFC 8628 中所定义

注意:如需确保您的应用在手表进入氛围模式时不会关闭,请在执行身份验证的 activity 中使用 AmbientModeSupport.attach 启用“始终开启”功能。如需详细了解氛围模式下的最佳实践,请参阅让应用始终显示在 Wear 上

用于代码交换的证明密钥 (PKCE)

如需有效地使用 PKCE,请使用 RemoteAuthClient

如需执行从 Wear OS 应用到 OAuth 提供方的身份验证请求,请创建 OAuthRequest 对象。此对象包含一个网址和一个 CodeChallenge 对象,该网址指向用于获取令牌的 OAuth 端点。以下代码展示了创建身份验证请求的示例:

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

此请求会触发对配套应用的调用,然后,配套应用会在用户手机的网络浏览器中显示授权界面。OAuth 2.0 提供方会验证用户身份,并请求用户同意所请求的权限。系统会将响应发送到自动生成的重定向网址。

授权成功或失败后,OAuth 2.0 服务器会重定向到请求中指定的网址。如果用户批准了访问请求,响应中就会包含授权代码。如果用户未批准请求,响应中会包含错误消息。

响应采用查询字符串的形式,并且类似于以下示例之一:

  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

这会加载一个引导用户前往配套应用的页面。配套应用会验证响应网址,并使用 onAuthorizationResponse API 将响应传递给第三方手表应用。

然后,手表应用可用授权代码来换取访问令牌。

注意:构建 OAuthRequest 后,您可以通过访问 redirectUrl 找到重定向网址。

设备授权

使用设备授权时,用户需要在另一部设备上打开验证 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 应用,请使用通用链接在您的应用中拦截此 intent,而不要依赖浏览器向令牌授权。

其他身份验证方法

Wear OS 支持其他登录方法,如以下部分所述。

Google 登录功能

Google 登录功能可让用户使用其现有的 Google 账号登录。此功能可提供出色的用户体验,并且易于提供支持,尤其是您已在手持设备应用中实现了此功能时。

除了上述推荐的身份验证方法之外,Google 登录功能是紧随其后的次选解决方案,因为它在 iOS 上也运行良好。下文介绍如何完成基本的 Google 登录功能集成。

前提条件

若要将 Google 登录功能集成到您的 Wear OS 应用中,您必须先配置一个 Google API 控制台项目,并设置您的 Android Studio 项目。如需了解详情,请参阅开始将 Google 登录功能集成到您的 Android 应用中

如果您在与后端服务器通信的应用或网站上使用 Google 登录功能,则还需满足另外两个前提条件:
  • 为后端服务器创建一个 OAuth 2.0 Web 应用客户端 ID。此客户端 ID 不同于您的应用的客户端 ID。如需了解详情,请参阅启用服务器端访问
  • 使用 HTTPS 发送用户的 ID 令牌,以便在该服务器上安全地识别当前已登录的用户。如需了解如何在后端服务器上对用户进行身份验证,请参阅用后端服务器进行身份验证

将 Google 登录功能集成到您的应用中

查看并实现以下步骤(后面几部分会进行详述),以便将 Google 登录功能集成到您的 Wear OS 应用中:

  1. 配置 Google 登录
  2. 添加 Google 登录按钮
  3. 用户点按登录按钮后启动登录流程

配置 Google 登录功能并构建 GoogleApiClient 对象

在登录 activity 的 onCreate() 方法中,将 Google 登录功能配置为请求应用所需的用户数据。然后,创建一个 GoogleApiClient 对象,并使其可以访问 Google Sign-In API 以及您指定的选项。这些步骤如以下示例所示:

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

创建登录 intent 并启动登录流程

使用 getSignInIntent() 方法创建登录 intent,以处理 onCLick() 方法中的登录按钮点按操作。然后,使用 startActivityForResult() 方法启动 intent。

  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 上的 Android Wear Google 登录示例

自定义代码身份验证

作为前述身份验证方法的替代方案,您可以要求用户通过其他设备(例如手机或平板电脑)进行身份验证,然后获取短时效数字代码。然后,用户要在 Wear OS 设备上输入代码以确认其身份,并会收到身份验证令牌。

在此身份验证流程中,要么使用应用的登录模块,要么手动将第三方身份验证提供程序的登录方法集成到应用的代码中。 虽然这种身份验证方法需要手动操作和额外的工作来提高安全性,但如果您需要在您的独立 Wear OS 应用中更早地进行身份验证,不妨采用这种方法。

此设置的身份验证流程如下:

  1. 用户使用 Wear OS 应用执行一项需要授权的操作。
  2. Wear OS 应用向用户呈现身份验证屏幕,并指示用户从指定网址输入代码。
  3. 用户切换到移动设备、平板电脑或 PC,启动浏览器,前往 Wear OS 应用中指定的网址,然后登录。
  4. 用户收到一个短时效数字代码,然后使用 Wear OS 设备自带键盘将其输入到 Wear OS 应用身份验证屏幕:

  5. 从此时开始,您可以使用输入的代码作为证据来证明这是正确的用户,并使用该代码来换取 Wear OS 设备上存储并加密的身份验证令牌,以便后续进行经过身份验证的调用。

注意:用户生成的代码必须是纯数字,不得包含任何字母字符。

此身份验证流程如下图所示: