基于 OAuth 的 App Flip 链接 (App Flip) 将您的 Android 应用程序插入到 Google 帐户链接流程中。传统的帐户链接流程要求用户在浏览器中输入其凭据。使用 App Flip 将用户登录推迟到您的 Android 应用程序,这允许您利用现有授权。如果用户已登录您的应用,则无需重新输入凭据即可关联其帐户。在您的 Android 应用程序上实现 App Flip 需要进行少量的代码更改。
在本文档中,您将了解如何修改您的 Android 应用以支持 App Flip。
试试样品
该应用程序链接翻转的示例应用程序演示了一个App翻转兼容的帐户在Android上连接整合。您可以使用此应用来验证如何响应来自 Google 移动应用的传入 App Flip 意图。
示例应用程序预配置与整合应用翻转测试工具为Android ,你可以用它来验证您的Android应用的使用App翻转整合之前配置帐户与谷歌联系起来。此应用程序模拟启用 App Flip 时由 Google 移动应用程序触发的意图。
怎么运行的
执行 App Flip 集成需要以下步骤:
- 该谷歌应用程序检查,如果您的应用程序安装使用它的包名在设备上。
- Google 应用程序使用包签名检查来验证安装的应用程序是正确的应用程序。
- Google 应用构建了一个 Intent,以在您的应用中启动指定的 Activity。此意图包括链接所需的附加数据。它还通过 Android 框架解析此意图来检查您的应用程序是否支持 App Flip。
- 您的应用会验证请求是否来自 Google 应用。为此,您的应用会检查包签名和提供的客户端 ID。
- 您的应用程序向您的 OAuth 2.0 服务器请求授权码。在此流程结束时,它会向 Google 应用返回授权代码或错误。
- Google 应用程序检索结果并继续关联帐户。如果提供了授权代码,令牌交换会在服务器到服务器之间进行,就像在基于浏览器的 OAuth 链接流中一样。
修改您的 Android 应用以支持 App Flip
要支持 App Flip,请对您的 Android 应用程序进行以下代码更改:
添加
<intent-filter>
您AndroidManifest.xml
文件与您在App翻转意愿字段中输入的值相匹配的操作字符串。<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>
验证调用应用程序的签名。
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 }
从意图参数中提取客户端 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... } }
授权成功后,将生成的授权代码返回给 Google。
// 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()
启动意图的内容
启动您的应用的 Android Intent 包括以下字段:
-
CLIENT_ID
(String
):谷歌client_id
您的应用程序下注册。 -
SCOPE
(String[]
请求范围的列表。 -
REDIRECT_URI
(String
):重定向URL。
响应数据的内容
返回到谷歌应用程序的数据在你的应用程序通过调用设置setResult()
该数据包括以下内容:
-
AUTHORIZATION_CODE
(String
):授权码值。 -
resultCode
(int
):通信的过程的成功或失败以及取下列值之一:-
Activity.RESULT_OK
:表示成功;返回授权码。 -
Activity.RESULT_CANCELLED
:信号用户已经取消了进程。在这种情况下,Google 应用程序将尝试使用您的授权 URL 进行帐户关联。 -
-2
:表示已发生错误。下面描述了不同类型的错误。
-
-
ERROR_TYPE
(int
):错误的类型,这取下列值之一:-
1
:可恢复错误:谷歌应用程序会尝试使用帐户授权URL链接。 -
2
:不可恢复的错误:谷歌应用程序异常退出帐户链接。 -
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
,以确保适当的后退时被trigerred。