1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.credentials 18 19 import android.content.ComponentName 20 import android.os.Bundle 21 import androidx.credentials.internal.FrameworkClassParsingException 22 import androidx.credentials.internal.RequestValidationHelper 23 24 /** 25 * A request to get passkeys from the user's public key credential provider. 26 * 27 * @property requestJson the request in JSON format in the standard webauthn web json shown 28 * [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson). 29 * @property clientDataHash a clientDataHash value to sign over in place of assembling and hashing 30 * clientDataJSON during the signature request; meaningful only if you have set the 31 * [GetCredentialRequest.origin] 32 * @property typePriorityHint always sets the priority of this entry to 33 * [CredentialOption.PRIORITY_PASSKEY_OR_SIMILAR], which defines how it appears in the credential 34 * selector, with less precedence than account ordering but more precedence than last used time; 35 * see [CredentialOption] for more information 36 */ 37 class GetPublicKeyCredentialOption 38 private constructor( 39 val requestJson: String, 40 val clientDataHash: ByteArray?, 41 allowedProviders: Set<ComponentName>, 42 requestData: Bundle, 43 candidateQueryData: Bundle, 44 typePriorityCategory: @PriorityHints Int = PRIORITY_PASSKEY_OR_SIMILAR, 45 ) : 46 CredentialOption( 47 type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, 48 requestData = requestData, 49 candidateQueryData = candidateQueryData, 50 isSystemProviderRequired = false, 51 isAutoSelectAllowed = true, 52 allowedProviders = allowedProviders, 53 typePriorityHint = typePriorityCategory, 54 ) { 55 56 /** 57 * Constructs a [GetPublicKeyCredentialOption]. 58 * 59 * @param requestJson the request in JSON format in the standard webauthn web json shown 60 * [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson). 61 * @param clientDataHash a clientDataHash value to sign over in place of assembling and hashing 62 * clientDataJSON during the signature request; set only if you have set the 63 * [GetCredentialRequest.origin] 64 * @param allowedProviders a set of provider service [ComponentName] allowed to receive this 65 * option (Note: a [SecurityException] will be thrown if it is set as non-empty but your app 66 * does not have android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS; for API level < 67 * 34, this property will not take effect and you should control the allowed provider via 68 * [library dependencies](https://developer.android.com/training/sign-in/passkeys#add-dependencies)) 69 * @throws NullPointerException If [requestJson] is null 70 * @throws IllegalArgumentException If [requestJson] is empty, or if it is not a valid JSON 71 */ 72 @JvmOverloads 73 constructor( 74 requestJson: String, 75 clientDataHash: ByteArray? = null, 76 allowedProviders: Set<ComponentName> = emptySet(), 77 ) : this( 78 requestJson = requestJson, 79 clientDataHash = clientDataHash, 80 allowedProviders = allowedProviders, 81 requestData = toRequestDataBundle(requestJson, clientDataHash), 82 candidateQueryData = toRequestDataBundle(requestJson, clientDataHash), 83 ) 84 85 init { <lambda>null86 require(RequestValidationHelper.isValidJSON(requestJson)) { 87 "requestJson must not be empty, and must be a valid JSON" 88 } 89 } 90 91 internal companion object { 92 internal const val BUNDLE_KEY_CLIENT_DATA_HASH = 93 "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH" 94 internal const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON" 95 internal const val BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION = 96 "androidx.credentials.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION" 97 98 @JvmStatic toRequestDataBundlenull99 internal fun toRequestDataBundle( 100 requestJson: String, 101 clientDataHash: ByteArray?, 102 ): Bundle { 103 val bundle = Bundle() 104 bundle.putString( 105 PublicKeyCredential.BUNDLE_KEY_SUBTYPE, 106 BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION 107 ) 108 bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson) 109 bundle.putByteArray(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash) 110 return bundle 111 } 112 113 @Suppress("deprecation") // bundle.get() used for boolean value to prevent default 114 // boolean value from being returned. 115 @JvmStatic createFromnull116 internal fun createFrom( 117 data: Bundle, 118 allowedProviders: Set<ComponentName>, 119 candidateQueryData: Bundle, 120 ): GetPublicKeyCredentialOption { 121 try { 122 val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON) 123 val clientDataHash = data.getByteArray(BUNDLE_KEY_CLIENT_DATA_HASH) 124 return GetPublicKeyCredentialOption( 125 requestJson!!, 126 clientDataHash, 127 allowedProviders, 128 requestData = data, 129 candidateQueryData = candidateQueryData, 130 typePriorityCategory = 131 data.getInt(BUNDLE_KEY_TYPE_PRIORITY_VALUE, PRIORITY_PASSKEY_OR_SIMILAR), 132 ) 133 } catch (e: Exception) { 134 throw FrameworkClassParsingException() 135 } 136 } 137 } 138 } 139