基于 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 集成需要执行以下步骤:
- Google 应用会使用其 软件包名称。
- Google 应用会使用软件包签名检查来验证已安装的 app 是正确的应用。
- Google 应用会构建一个 intent,用于启动您应用中的指定 activity。 此 intent 包含关联所需的其他数据。它还会检查 通过 Android 框架。
- 您的应用会验证请求是否来自 Google 应用。为此, 您的应用会检查软件包签名和提供的客户端 ID。
- 您的应用从您的 OAuth 2.0 服务器请求授权代码。在 流程结束时,它会将授权代码或错误返回给 Google 应用。
- Google 应用会检索结果,然后继续进行账号关联。如果 提供了授权代码后,系统就会发生令牌交换 与基于浏览器的 OAuth 关联中的方法相同, 。
修改 Android 应用以支持 App Flip
如需支持 App Flip,请对 Android 应用进行以下代码更改:
使用操作将
<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>
验证发起调用的应用的签名。
<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 }
从 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... } }
授权成功后,返回生成的授权代码 。
// Successful result val data = Intent().apply { putExtra("AUTHORIZATION_CODE", authCode) } setResult(Activity.RESULT_OK, data) finish()
如果发生错误,请改为返回错误结果。
// 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
):已在您的应用名下注册的 Googleclient_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_DESCRIPTION
(String
,可选):简单易懂的状态消息 来描述错误
如果出现以下情况,则应为 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
返回错误结果,
确保触发适当的回退