Android 版 App Flip

基于 OAuth 的应用快速关联 (App Flip) 会将你的 Android 应用插入 Google 账号关联流程。传统的账号关联流程要求 用户在浏览器中输入其凭据。使用应用快速关联会导致用户延迟 登录到您的 Android 应用,以便充分利用现有的 授权。如果用户已登录您的应用,则无需 请重新输入其凭据以关联其账号。尽可能减少代码量 在 Android 应用中实现 App Flip 所需的更改。

在本文档中,您将学习如何修改 Android 应用以支持 应用快速关联。

试用示例

App Flip 关联示例应用 演示了 Android 上与 App Flip 兼容的账号关联集成。您 可以使用此应用来验证如何响应来自以下应用的传入 app flip intent: Google 移动应用。

示例应用已预先配置为与应用快速关联测试工具 Android 可用于验证 Android 应用与应用的集成 先翻转上述设置,然后再配置与 Google 的账号关联。此应用会模拟 intent。

工作原理

执行 app flip 集成需要执行以下步骤:

  1. Google 应用会使用其 软件包名称
  2. Google 应用会使用软件包签名检查来验证已安装的 app 是正确的应用。
  3. Google 应用会构建一个 intent,用于启动您应用中的指定 activity。 此 intent 包含关联所需的其他数据。它还会检查 通过 Android 框架。
  4. 您的应用会验证请求是否来自 Google 应用。为此, 您的应用会检查软件包签名和提供的客户端 ID。
  5. 您的应用从您的 OAuth 2.0 服务器请求授权代码。在 流程结束时,它会将授权代码或错误返回给 Google 应用。
  6. Google 应用会检索结果,然后继续进行账号关联。如果 提供了授权代码后,系统就会发生令牌交换 与基于浏览器的 OAuth 关联中的方法相同, 。

修改 Android 应用以支持 App Flip

如需支持 App Flip,请对 Android 应用进行以下代码更改:

  1. 使用操作将 <intent-filter> 添加到您的 AndroidManifest.xml 文件中 与您在应用快速关联字段中输入的值匹配的字符串。

    <activity android:name="AuthActivity">
      <!-- Handle the app flip intent -->
      <intent-filter>
        <action android:name="INTENT_ACTION_FROM_CONSOLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
    </activity>
    
  2. 验证发起调用的应用的签名。

    <ph type="x-smartling-placeholder">
    private fun verifyFingerprint(
            expectedPackage: String,
            expectedFingerprint: String,
            algorithm: String
    ): Boolean {
    
        callingActivity?.packageName?.let {
            if (expectedPackage == it) {
                val packageInfo =
                    packageManager.getPackageInfo(it, PackageManager.GET_SIGNATURES)
                val signatures = packageInfo.signatures
                val input = ByteArrayInputStream(signatures[0].toByteArray())
    
                val certificateFactory = CertificateFactory.getInstance("X509")
                val certificate =
                    certificateFactory.generateCertificate(input) as X509Certificate
                val md = MessageDigest.getInstance(algorithm)
                val publicKey = md.digest(certificate.encoded)
                val fingerprint = publicKey.joinToString(":") { "%02X".format(it) }
    
                return (expectedFingerprint == fingerprint)
            }
        }
        return false
    }
    
  3. 从 intent 参数中提取客户端 ID 并验证客户端 ID 与预期值一致。

    private const val EXPECTED_CLIENT = "<client-id-from-actions-console>"
    private const val EXPECTED_PACKAGE = "<google-app-package-name>"
    private const val EXPECTED_FINGERPRINT = "<google-app-signature>"
    private const val ALGORITHM = "SHA-256"
    ...
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        val clientId = intent.getStringExtra("CLIENT_ID")
    
        if (clientId == EXPECTED_CLIENT &&
            verifyFingerprint(EXPECTED_PACKAGE, EXPECTED_FINGERPRINT, ALGORITHM)) {
    
            // ...authorize the user...
        }
    }
    
  4. 授权成功后,返回生成的授权代码 。

    // Successful result
    val data = Intent().apply {
        putExtra("AUTHORIZATION_CODE", authCode)
    }
    setResult(Activity.RESULT_OK, data)
    finish()
    
  5. 如果发生错误,请改为返回错误结果。

    // Error result
    val error = Intent().apply {
        putExtra("ERROR_TYPE", 1)
        putExtra("ERROR_CODE", 1)
        putExtra("ERROR_DESCRIPTION", "Invalid Request")
    }
    setResult(-2, error)
    finish()
    

启动 intent 的内容

用于启动应用的 Android intent 包括以下字段:

  • CLIENT_ID (String):已在您的应用名下注册的 Google client_id
  • SCOPE (String[]):请求的范围列表。
  • REDIRECT_URI (String):重定向网址。

响应数据的内容

返回到 Google 应用的数据通过调用 setResult() 在您的应用中设置。 这些数据包括以下内容:

  • AUTHORIZATION_CODE (String):授权代码值。
  • resultCode (int):传达流程成功与否;以及 采用以下某个值: <ph type="x-smartling-placeholder">
      </ph>
    • Activity.RESULT_OK:表示成功;系统就会返回授权代码
    • Activity.RESULT_CANCELLED:表示用户已取消 过程。在这种情况下,Google 应用会尝试使用 您的授权网址。
    • -2:表示出现错误。不同类型的错误 如下所述。
  • ERROR_TYPE (int):错误类型,采用以下类型之一 值: <ph type="x-smartling-placeholder">
      </ph>
    • 1:可恢复的错误:Google 应用将尝试使用如下账号关联账号: 授权网址。
    • 2:不可恢复的错误:Google 应用中止了账号关联。
    • 3:请求参数无效或缺失。
  • ERROR_CODE (int):表示错误性质的整数。要查看 每个错误代码的含义,请参阅 错误代码表
  • ERROR_DESCRIPTIONString,可选):简单易懂的状态消息 来描述错误

如果出现以下情况,则应为 AUTHORIZATION_CODE 提供值: resultCode == Activity.RESULT_OK。在其他所有情况下, “AUTHORIZATION_CODE”必须为空。如果为 resultCode == -2,则 应填充“ERROR_TYPE”值。

错误代码表

下表显示了不同的错误代码,以及每个错误代码是可恢复的错误还是不可恢复的错误:

错误代码 含义 可恢复 不可恢复
1 INVALID_REQUEST
2 NO_INTERNET_CONNECTION
3 OFFLINE_MODE_ACTIVE
4 CONNECTION_TIMEOUT
5 INTERNAL_ERROR
6 AUTHENTICATION_SERVICE_UNAVAILABLE
8 CLIENT_VERIFICATION_FAILED
9 INVALID_CLIENT
10 INVALID_APP_ID
11 INVALID_REQUEST
12 AUTHENTICATION_SERVICE_UNKNOWN_ERROR
13 AUTHENTICATION_DENIED_BY_USER
14 CANCELLED_BY_USER
15 FAILURE_OTHER
16 USER_AUTHENTICATION_FAILED

对于所有错误代码,您必须通过 setResult 返回错误结果, 确保触发适当的回退