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

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

本指南提供了有关 Wear OS 应用的推荐身份验证方法 Credential Manager 的说明。

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

初步注意事项

在开始实现之前,请考虑以下几点。

访客模式

不要对所有功能都要求进行身份验证,而是尽可能向用户提供更多功能,而无需用户登录。

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

某些设备可能会保持解锁状态更长时间

在搭载 Wear OS 5 或更高版本的受支持设备上,系统会检测用户是否将设备戴在手腕上。如果用户关闭了手腕检测功能,然后将设备从手腕上取下,系统会比平时更长时间地保持设备解锁状态。

如果您的应用需要更高的安全性(例如在显示可能敏感或私密的数据时),请先检查是否启用了手腕检测功能:

fun isWristDetectionAutoLockingEnabled(context: Context): Boolean {
    // Use the keyguard manager to check for the presence of a lock mechanism
    val keyguardManager = context.getSystemService<KeyguardManager>()
    val isSecured = keyguardManager?.isDeviceSecure == true

    // Use OEM-specific system settings to verify that on-body autolock is enabled.
    val isWristDetectionOn = android.provider.Settings.Global.getInt(
        context.contentResolver, PIXEL_WRIST_AUTOLOCK_SETTING_STATE,
        0
    ) == 1

    return isSecured && isWristDetectionOn
}

如果此方法的返回值为 false,请提示用户先登录到应用中的账号,然后再显示用户专属内容。

Credential Manager

Credential Manager 是推荐用于 Wear OS 身份验证 的 API。它为用户提供了一个更安全的环境,以便用户在独立设置中登录 Wear OS 应用,而无需连接配对的手机,也无需记住密码。

本文档概述了开发者需要使用 Credential Manager 解决方案实现的信息,以及它托管的标准身份验证机制,包括:

  • 通行密钥
  • 密码
  • 联合身份(例如“使用 Google 账号登录”)

本指南还提供了有关如何将其他可接受的 Wear OS 身份验证方法(数据层令牌共享OAuth)迁移为 Credential Manager 的备份的说明,以及有关如何处理从现已废弃的独立 Google 登录按钮 到嵌入式 Credential Manager 版本的过渡的特殊说明。

Wear OS 限制和差异

开发者应注意 Wear OS 上的以下限制和差异:

  • Credential Manager 适用于 Wear OS 3 及更高版本。
  • 无法在 Wear OS 上创建凭据
  • 不支持恢复凭据,也不支持混合登录流程。
  • 只有具有 Wear OS 集成的凭据提供程序才能从移动设备重复使用。

Wear OS 上的通行密钥

强烈建议开发者在其 Wear OS Credential Manager 实现中实现通行密钥。 通行密钥是面向最终用户身份验证的新行业标准,它们 可为用户带来多项显著优势

通行密钥更简单

  • 用户可以选择要登录的账号, 而无需输入用户名。
  • 用户可以使用设备的屏幕锁定进行身份验证。
  • 创建并注册通行密钥后,用户可以无缝切换到新设备并立即使用,而无需重新注册。

通行密钥更安全

  • 开发者只需将公钥保存到服务器,而无需保存密码,这意味着恶意行为者入侵服务器的价值要低得多,并且在发生违规行为时需要清理的内容也少得多。
  • 通行密钥提供防钓鱼保护。通行密钥仅适用于其注册的网站和应用;用户不会被诱骗在欺骗性网站上进行身份验证,因为浏览器或操作系统会处理验证。
  • 通行密钥减少了发送短信的需求,从而提高了身份验证的经济效益。

实现通行密钥

包括所有实现类型的设置和指南。

设置

  1. 在应用模块的 build.gradle 文件中将目标 API 级别设置为 35:

    android {
        defaultConfig {
            targetSdk(35)
        }
    }
    
  2. 使用 androidx.credentials版本参考中的最新稳定版本,将以下行添加到应用或模块的 build.gradle 文件中。

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

内置身份验证方法

由于 Credential Manager 是一个统一的 API,因此 Wear OS 的实现步骤与其他任何设备类型相同。

请按照移动设备说明 开始操作,并实现通行密钥和密码支持。

向 Credential Manager 添加 “使用 Google 账号登录”支持 的步骤是针对移动开发,但在 Wear OS 上也是相同的。

请注意,由于无法在 Wear OS 上创建凭据,因此您无需实现移动设备说明中提到的凭据创建方法。

备用身份验证方法

Wear OS 应用还有另外两种可接受的身份验证方法:OAuth 2.0(任一变体)和移动身份验证令牌数据层共享。虽然这些方法在 Credential Manager API 中没有集成点,但它们可以作为后备方案包含在 Credential Manager 的用户体验流程中,以防用户关闭 Credential Manager 屏幕。

如需处理用户关闭 Credential Manager 屏幕的操作,请捕获 NoCredentialException作为您的 GetCredential 逻辑的一部分,然后导航到您自己的自定义身份验证界面。

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

然后,您的自定义身份验证界面可以提供登录用户体验指南中所述的任何其他可接受的身份验证 方法。

数据层令牌共享

手机上的配套应用可以使用 Wearable Data Layer API 将身份验证数据安全地传输到 Wear OS 应用。可以采用消息或数据项的形式传输凭据。

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

重要提示: 您的 Wear OS 应用必须至少提供另外一种身份验证方法,因为此选项仅适用于安装了相应移动应用且与 Android 设备完成配对的手表。对于没有安装相应移动应用或所用 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.
                    handleSignInSequence(token)
                }
            }
        }
    }

    /** placeholder sign in handler. */
    fun handleSignInSequence(token: String?) {}
}

如需详细了解如何使用穿戴式设备数据层,请参阅 在 Wear OS 上发送和同步数据

使用 OAuth 2.0

Wear OS 支持两个基于 OAuth 2.0 的流程,后面几个部分对这些流程进行了介绍:

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

如需有效使用 PKCE,请使用 RemoteAuthClient. 然后,如需执行从 Wear OS 应用到 OAuth 提供方的身份验证请求, 请创建 OAuthRequest 对象。此对象包含一个网址和一个 CodeChallenge对象,该网址指向用于获取令牌的 OAuth 端点。

以下代码展示了创建身份验证请求的示例:

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

此请求会触发对配套应用的调用,然后,配套应用会在用户手机的网络浏览器中显示授权界面。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 将响应传递给您的 Wear OS 应用。

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

设备授权

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