diff --git a/changelog.txt b/changelog.txt index 448973b1d9..1106a8cc80 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,6 +7,7 @@ vNext - [MINOR] Add Child Spans for Interactive Span (#2516) - [MINOR] For MSAL CPP flows, match exact claims when deleting AT with intersecting scopes (#2548) - [MINOR] Replace Deprecated Keystore API for Android 28+ (#2558) +- [PATCH] Make userHandle response field optional (#2560) Version 18.2.2 ---------- diff --git a/common/src/main/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtil.kt b/common/src/main/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtil.kt index 223ee21e63..ed807cbeef 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtil.kt +++ b/common/src/main/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtil.kt @@ -30,13 +30,18 @@ import com.microsoft.identity.common.java.constants.FidoConstants.Companion.WEBA import com.microsoft.identity.common.java.constants.FidoConstants.Companion.WEBAUTHN_RESPONSE_ID_JSON_KEY import com.microsoft.identity.common.java.constants.FidoConstants.Companion.WEBAUTHN_RESPONSE_SIGNATURE_JSON_KEY import com.microsoft.identity.common.java.constants.FidoConstants.Companion.WEBAUTHN_RESPONSE_USER_HANDLE_JSON_KEY +import com.microsoft.identity.common.logging.Logger +import org.json.JSONException import org.json.JSONObject /** * A utility class to help convert to and from strings in WebAuthn json format. */ class WebAuthnJsonUtil { - companion object { + companion object { + + private val TAG = WebAuthnJsonUtil::class.simpleName.toString() + /** * Takes applicable parameters and creates a string representation of * PublicKeyCredentialRequestOptionsJSON (https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson) @@ -74,6 +79,7 @@ class WebAuthnJsonUtil { * @throws JSONException if a value is not present that should be. */ fun extractAuthenticatorAssertionResponseJson(fullResponseJson : String): String { + val methodTag = "$TAG:extractAuthenticatorAssertionResponseJson" val fullResponseJsonObject = JSONObject(fullResponseJson); val authResponseJsonObject = fullResponseJsonObject .getJSONObject(FidoConstants.WEBAUTHN_AUTHENTICATION_ASSERTION_RESPONSE_JSON_KEY) @@ -87,8 +93,17 @@ class WebAuthnJsonUtil { WEBAUTHN_RESPONSE_CLIENT_DATA_JSON_KEY)) assertionResult.put(WEBAUTHN_RESPONSE_SIGNATURE_JSON_KEY, authResponseJsonObject.get( WEBAUTHN_RESPONSE_SIGNATURE_JSON_KEY)) - assertionResult.put(WEBAUTHN_RESPONSE_USER_HANDLE_JSON_KEY, authResponseJsonObject.get( - WEBAUTHN_RESPONSE_USER_HANDLE_JSON_KEY)) + // UserHandle is optional if allowCredentials was provided in the request (username flow). + if (authResponseJsonObject.isNull(WEBAUTHN_RESPONSE_USER_HANDLE_JSON_KEY)) { + Logger.info(methodTag, "UserHandle not found in assertion response.") + } else { + Logger.info(methodTag, "UserHandle was included in assertion response.") + assertionResult.put( + WEBAUTHN_RESPONSE_USER_HANDLE_JSON_KEY, authResponseJsonObject.get( + WEBAUTHN_RESPONSE_USER_HANDLE_JSON_KEY + ) + ) + } return assertionResult.toString() } diff --git a/common/src/test/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtilTest.kt b/common/src/test/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtilTest.kt index 5822ad5eab..86a4f39d01 100644 --- a/common/src/test/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtilTest.kt +++ b/common/src/test/java/com/microsoft/identity/common/internal/fido/WebAuthnJsonUtilTest.kt @@ -67,10 +67,10 @@ class WebAuthnJsonUtilTest { val attestationObject = "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A" val expectedAuthenticationAssertionResponseJsonAllFieldsFilled = """{"authenticatorData":"$authenticatorData","clientDataJSON":"$clientDataJSON","id":"$idAssertionResponse","signature":"$signature","userHandle":"$userHandle"}""" - val expectedAuthenticationAssertionResponseOnlyRequiredFields = """{"authenticatorData":"$authenticatorData","clientDataJSON":"$clientDataJSON","id":"$idAssertionResponse","signature":"$signature","userHandle":"$userHandle"}""" + val expectedAuthenticationAssertionResponseOnlyRequiredFields = """{"authenticatorData":"$authenticatorData","clientDataJSON":"$clientDataJSON","id":"$idAssertionResponse","signature":"$signature"}""" val demoAuthenticationResponseJsonAllFieldsFilled = """{"authenticatorAttachment":"$authenticatorAttachment","clientExtensionResults":{},"id":"KEDetxZcUfinhVi6Za5nZQ","rawId":"KEDetxZcUfinhVi6Za5nZQ","response":{"attestationObject":"$attestationObject","authenticatorData":"$authenticatorData","clientDataJSON":"$clientDataJSON","id":"$idAssertionResponse","signature":"$signature","userHandle":"$userHandle"},"type":"public-key"}""" - val demoAuthenticationResponseJsonOnlyRequiredFields = """{"clientExtensionResults":{},"id":"KEDetxZcUfinhVi6Za5nZQ","rawId":"KEDetxZcUfinhVi6Za5nZQ","response":{"attestationObject":null,"authenticatorData":"$authenticatorData","clientDataJSON":"$clientDataJSON","id":"$idAssertionResponse","signature":"$signature","userHandle":"$userHandle"},"type":"public-key"}""" + val demoAuthenticationResponseJsonOnlyRequiredFields = """{"clientExtensionResults":{},"id":"KEDetxZcUfinhVi6Za5nZQ","rawId":"KEDetxZcUfinhVi6Za5nZQ","response":{"attestationObject":null,"authenticatorData":"$authenticatorData","clientDataJSON":"$clientDataJSON","id":"$idAssertionResponse","signature":"$signature"},"type":"public-key"}""" val demoAuthenticationResponseJsonMissingSignature = """{"clientExtensionResults":{},"id":"KEDetxZcUfinhVi6Za5nZQ","rawId":"KEDetxZcUfinhVi6Za5nZQ","response":{"attestationObject":null,"authenticatorData":"$authenticatorData","clientDataJSON":"$clientDataJSON","id":"$idAssertionResponse","userHandle":"$userHandle"},"type":"public-key"}""" // Demo JWT from https://jwt.io/