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

手表的屏幕尺寸较小,输入功能较少,这限制了 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

如果您有 iOS 应用,请使用通用链接在您的应用中拦截此 intent,而不要依赖浏览器向令牌授权。

其他身份验证方法

Wear OS 还支持其他登录方法。

Google 登录功能

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

Google 登录功能是最受青睐的解决方案,因为它在 iOS 上也运行良好。下一部分将介绍如何完成基本的 Google 登录功能集成。

前提条件

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

注意:如果您在与后端服务器通信的应用或网站上使用 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 以及您指定的选项。

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

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

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

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 帐号。如果请求访问的范围超出了个人资料、电子邮件和 OpenID,系统还会提示用户授予对相关资源的访问权限。

最后,在 activity 的 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 应用向用户呈现身份验证屏幕,并指示用户从指定网址输入代码。
  3. 用户切换到移动设备/平板电脑或 PC,启动浏览器,前往 Wear OS 应用上指定的网址,然后登录。
  4. 用户收到一个短时效代码,然后使用 Wear OS 设备自带键盘将其输入到 Wear OS 应用身份验证屏幕,以进行身份验证:

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