Skip to content

Commit 68211e2

Browse files
authored
Fido: Add extra to pre-select transport and disable instant (#3463)
1 parent e8a7e9c commit 68211e2

1 file changed

Lines changed: 32 additions & 9 deletions

File tree

play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivity.kt

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
6262
val source: String?
6363
get() = intent.getStringExtra(KEY_SOURCE)
6464

65+
enum class AllowedInstantLevel {
66+
NONE, PRESELECT, INSTANT
67+
}
68+
6569
private val service: GmsService
6670
get() = GmsService.byServiceId(intent.getIntExtra(KEY_SERVICE, GmsService.UNKNOWN.SERVICE_ID))
6771
private val database by lazy { Database(this) }
@@ -79,6 +83,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
7983
lateinit var callerSignature: String
8084
private lateinit var navHostFragment: NavHostFragment
8185
private var preselectedCredentialId: String? = null
86+
private var preselectedTransport: Transport? = null
8287

8388
private inline fun <reified T : TransportHandler> getTransportHandler(): T? =
8489
transportHandlers.filterIsInstance<T>().firstOrNull { it.isSupported }
@@ -106,14 +111,23 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
106111
this.callerSignature = packageManager.getFirstSignatureDigest(callerPackage, "SHA-256")?.toBase64()
107112
?: return finishWithError(UNKNOWN_ERR, "Could not determine signature of app")
108113
this.preselectedCredentialId = intent.getStringExtra(KEY_CREDENTIAL_ID)
114+
this.preselectedTransport = intent.getStringExtra(KEY_PRESELECTED_TRANSPORT)?.let {
115+
runCatching { Transport.valueOf(it) }.getOrNull()
116+
}
109117

110118
Log.d(TAG, "onCreate caller=$callerPackage options=$options preselectedCredentialId=$preselectedCredentialId")
111119

120+
// By default, we allow instants. This extra may be used by app using fido-core library
121+
// To avoid instant.
122+
val allowInstant = AllowedInstantLevel.entries.getOrNull(
123+
intent.getIntExtra(KEY_ALLOW_INSTANT, AllowedInstantLevel.INSTANT.ordinal)
124+
) ?: AllowedInstantLevel.INSTANT
125+
112126
val requiresPrivilege =
113127
source == SOURCE_BROWSER && !database.isPrivileged(callerPackage, callerSignature)
114128

115129
// Check if we can directly open screen lock handling
116-
if (!requiresPrivilege) {
130+
if (allowInstant == AllowedInstantLevel.INSTANT && !requiresPrivilege) {
117131
val instantTransport = transportHandlers.firstOrNull {
118132
it.isSupported && it.shouldBeUsedInstantly(options, preselectedCredentialId)
119133
}
@@ -128,7 +142,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
128142
setContentView(R.layout.fido_authenticator_activity)
129143

130144
lifecycleScope.launchWhenCreated {
131-
handleRequest(options)
145+
handleRequest(options, allowInstant)
132146
}
133147
} catch (e: RequestHandlingException) {
134148
finishWithError(e.errorCode, e.message ?: e.errorCode.name)
@@ -139,7 +153,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
139153
}
140154

141155
@RequiresApi(24)
142-
suspend fun handleRequest(options: RequestOptions, allowInstant: Boolean = true) {
156+
suspend fun handleRequest(options: RequestOptions, allowInstantLevel: AllowedInstantLevel = AllowedInstantLevel.INSTANT) {
143157
try {
144158
val origin = getOrigin(this, options, callerPackage)
145159
options.checkIsValid(this, origin, callerPackage)
@@ -154,7 +168,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
154168
val noLocalUserForSignInstantBlock = options.type == RequestOptionsType.SIGN && database.getKnownRegistrationInfo(options.rpId).isEmpty()
155169

156170
// Check if we can directly open screen lock handling
157-
if (!requiresPrivilege && allowInstant && !noLocalUserForSignInstantBlock) {
171+
if (!requiresPrivilege && allowInstantLevel == AllowedInstantLevel.INSTANT && !noLocalUserForSignInstantBlock) {
158172
val instantTransport = transportHandlers.firstOrNull {
159173
it.isSupported && it.shouldBeUsedInstantly(options, preselectedCredentialId)
160174
}
@@ -171,7 +185,11 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
171185
this.requiresPrivilege = requiresPrivilege
172186
this.supportedTransports = transportHandlers.filter { it.isSupported }.map { it.transport }.toSet()
173187
}.arguments
174-
val next = if (!requiresPrivilege) {
188+
val next = if (requiresPrivilege) {
189+
null
190+
} else if (allowInstantLevel == AllowedInstantLevel.NONE) {
191+
R.id.transportSelectionFragment
192+
} else {
175193
val knownRegistrationTransports = mutableSetOf<Transport>()
176194
val allowedTransports = mutableSetOf<Transport>()
177195
if (options.type == RequestOptionsType.SIGN) {
@@ -203,7 +221,12 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
203221
}
204222
}
205223
}
206-
val preselectedTransport = knownRegistrationTransports.singleOrNull() ?: allowedTransports.singleOrNull()
224+
// We do not control if preselectedTransport is in allowedTransports, or if
225+
// allowedTransports is empty, as allowedTransports is a *hint*.
226+
// This is particularly useful if the application sends a credential with
227+
// transport=internal, but we login with another device (via hybrid connection)
228+
val preselectedTransport = preselectedTransport
229+
?: knownRegistrationTransports.singleOrNull() ?: allowedTransports.singleOrNull()
207230
if (database.wasUsed()) {
208231
when (preselectedTransport) {
209232
USB -> R.id.usbFragment
@@ -215,8 +238,6 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
215238
} else {
216239
null
217240
}
218-
} else {
219-
null
220241
}
221242
navHostFragment = NavHostFragment()
222243
supportFragmentManager.commit {
@@ -321,7 +342,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
321342
} catch (e: SecurityException) {
322343
Log.w(TAG, e)
323344
if (instant) {
324-
handleRequest(options, false)
345+
handleRequest(options, AllowedInstantLevel.PRESELECT)
325346
} else {
326347
finishWithError(SECURITY_ERR, e.message ?: e.javaClass.simpleName)
327348
}
@@ -381,9 +402,11 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback {
381402
const val KEY_SERVICE = "service"
382403
const val KEY_SOURCE = "source"
383404
const val KEY_TYPE = "type"
405+
const val KEY_ALLOW_INSTANT = "allowInstant"
384406
const val KEY_OPTIONS = "options"
385407
const val KEY_USER_JSON = "userInfo"
386408
const val KEY_CREDENTIAL_ID = "credential"
409+
const val KEY_PRESELECTED_TRANSPORT = "transport"
387410
val REQUIRED_EXTRAS = setOf(KEY_SOURCE, KEY_TYPE, KEY_OPTIONS)
388411

389412
const val SOURCE_BROWSER = "browser"

0 commit comments

Comments
 (0)