The majority of this PR consists of new classes and their associated unit tests. The new classes are wired up, but are guarded by a flag until the passkey feature release.
It also includes a new dependency which allows us to use lifecycle coroutine scopes, as well as a test dependency (JSONAssert) which allows for cleaner comparisions between JSON objects in Junit tests.
The general steps after user clicks "use a passkey/security key" (or some string like that) in the WebView:
Server sends a redirect uri, which our AzureActiveDirectoryWebViewClient will identify as a passkey protocol redirect uri.
We first try to create the challenge handler. Then we try to create the challenge from the query string parameters in the uri.
We will process the challenge by creating a Json string request in a particular WebAuthn format and invoking the Fido API (which will be Credential Manager, but that implementation will come in the last PR for this feature), which will guide the user through choosing a passkey and providing their biometrics/system PIN.
If successful, we will receive the results back as a JSON string response, which we will parse out to get the object that we need to return to the server. If not successful (exception thrown), we will get the error message details. (Future work will involve putting the exception in a certain format that the server expects; this format is in discussion).
The results will be sent back to the server via an Assertion header.
User will either be logged in, or they will see an error message and will be asked to try again.
AuthFidoChallengeHandler
AuthFidoChallengeHandler takes in FidoChallenge into processChallenge and handles getting the assertion from the FidoManager, which is run in a lifecycle coroutine scope. The class also has a respondToChallenge method which handles responding back to the server with the expected header values.
Unit tests were created for AuthFidoChallengeHandler using test implementations of IFidoManager and WebView. Additional unit tests were created for WebAuthnJsonUtil.
LifecycleScope
We'll need to run the CredMan API logic in a coroutine scope, so I chose to use a Lifecycle scope so that the passkey logic is tied to the lifecycle of the WebView. For more information: https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope
The dependencies added are androidx.lifecycle:lifecycle-runtime-ktx and androidx.lifecycle:lifecycle-runtime-testing.
The addition of the lifecycle dependency will not bring us above the current limit we've set for dependency size for common (10.57 MB -> 10.66 MB, from dependenciesSizeCheck).
Wiring up in AzureActiveDirectoryWebViewClient
The passkey protocol request will come in as a redirect uri. We'll identify the uri in the handleUrl method and create the appropriate classes (challenge handler, challenge, etc.) to handle the challenge and respond back to the server.
Upcoming Work
Actual implementation of CredManFidoManager will be done in a future PR, as we can't add in the Credential Manager API until our main consumer apps have bumped their compileSdk versions to 34 (a requirement of Credential Manager).
Discussion is still occurring as to in what format we should be sending exceptions/errors back to the server. Currently I am just sending the error string/message in the Assertion header, which will not crash anything, but result in a "invalid assertion" message from the server.
Very basic telemetry has been added to the challenge handler, and more investigation will be needed to see if it is possible to determine other information from the WebAuthn response (current investigations on my part have determined that this might not be possible from the response alone).
Summary
The majority of this PR consists of new classes and their associated unit tests. The new classes are wired up, but are guarded by a flag until the passkey feature release. It also includes a new dependency which allows us to use lifecycle coroutine scopes, as well as a test dependency (JSONAssert) which allows for cleaner comparisions between JSON objects in Junit tests.
The general steps after user clicks "use a passkey/security key" (or some string like that) in the WebView:
AuthFidoChallengeHandler
AuthFidoChallengeHandler
takes inFidoChallenge
intoprocessChallenge
and handles getting the assertion from theFidoManager
, which is run in a lifecycle coroutine scope. The class also has arespondToChallenge
method which handles responding back to the server with the expected header values. Unit tests were created forAuthFidoChallengeHandler
using test implementations ofIFidoManager
andWebView
. Additional unit tests were created forWebAuthnJsonUtil
.LifecycleScope
We'll need to run the CredMan API logic in a coroutine scope, so I chose to use a Lifecycle scope so that the passkey logic is tied to the lifecycle of the WebView. For more information: https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope The dependencies added are
androidx.lifecycle:lifecycle-runtime-ktx
andandroidx.lifecycle:lifecycle-runtime-testing
. The addition of the lifecycle dependency will not bring us above the current limit we've set for dependency size for common (10.57 MB -> 10.66 MB, from dependenciesSizeCheck).Wiring up in AzureActiveDirectoryWebViewClient
The passkey protocol request will come in as a redirect uri. We'll identify the uri in the handleUrl method and create the appropriate classes (challenge handler, challenge, etc.) to handle the challenge and respond back to the server.
Upcoming Work
CredManFidoManager
will be done in a future PR, as we can't add in the Credential Manager API until our main consumer apps have bumped their compileSdk versions to 34 (a requirement of Credential Manager).