diff options
22 files changed, 930 insertions, 9 deletions
diff --git a/core/java/android/credentials/Credential.java b/core/java/android/credentials/Credential.java index fed259254239..db89170b0cd7 100644 --- a/core/java/android/credentials/Credential.java +++ b/core/java/android/credentials/Credential.java @@ -36,7 +36,8 @@ public final class Credential implements Parcelable { * * @hide */ - @NonNull public static final String TYPE_PASSWORD = "android.credentials.TYPE_PASSWORD"; + @NonNull public static final String TYPE_PASSWORD_CREDENTIAL = + "android.credentials.TYPE_PASSWORD_CREDENTIAL"; /** * The credential type. diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 56fb1a91aa90..0988cba2f424 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -36,7 +36,7 @@ import com.android.credentialmanager.createflow.CreateScreenState import com.android.credentialmanager.createflow.RequestDisplayInfo import com.android.credentialmanager.getflow.GetCredentialUiState import com.android.credentialmanager.getflow.GetScreenState -import com.android.credentialmanager.jetpack.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL +import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL // Consider repo per screen, similar to view model? class CredentialManagerRepo( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt index 8e30208e75d9..aeea46a85caf 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt @@ -39,8 +39,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap import com.android.credentialmanager.R -import com.android.credentialmanager.jetpack.CredentialEntryUi.Companion.TYPE_PASSWORD_CREDENTIAL -import com.android.credentialmanager.jetpack.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL +import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PASSWORD_CREDENTIAL +import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL import com.android.credentialmanager.ui.theme.Grey100 import com.android.credentialmanager.ui.theme.Shapes import com.android.credentialmanager.ui.theme.Typography diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt new file mode 100644 index 000000000000..7e7dbde8655a --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.credentials.Credential +import android.os.Bundle + +/** + * Base request class for registering a credential. + * + * @property type the credential type + * @property data the request data in the [Bundle] format + * @property requireSystemProvider true if must only be fulfilled by a system provider and false + * otherwise + */ +open class CreateCredentialRequest( + val type: String, + val data: Bundle, + val requireSystemProvider: Boolean, +) { + companion object { + @JvmStatic + fun createFrom(from: android.credentials.CreateCredentialRequest): CreateCredentialRequest { + return try { + when (from.type) { + Credential.TYPE_PASSWORD_CREDENTIAL -> + CreatePasswordRequest.createFrom(from.data) + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> + CreatePublicKeyCredentialBaseRequest.createFrom(from.data) + else -> + CreateCredentialRequest(from.type, from.data, from.requireSystemProvider()) + } + } catch (e: FrameworkClassParsingException) { + CreateCredentialRequest(from.type, from.data, from.requireSystemProvider()) + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt new file mode 100644 index 000000000000..f0da9f9d1866 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.credentials.Credential +import android.os.Bundle + +/** + * A request to save the user password credential with their password provider. + * + * @property id the user id associated with the password + * @property password the password + * @throws NullPointerException If [id] is null + * @throws NullPointerException If [password] is null + * @throws IllegalArgumentException If [password] is empty + */ +class CreatePasswordRequest constructor( + val id: String, + val password: String, +) : CreateCredentialRequest( + Credential.TYPE_PASSWORD_CREDENTIAL, + toBundle(id, password), + false, +) { + + init { + require(password.isNotEmpty()) { "password should not be empty" } + } + + companion object { + const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID" + const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD" + + @JvmStatic + internal fun toBundle(id: String, password: String): Bundle { + val bundle = Bundle() + bundle.putString(BUNDLE_KEY_ID, id) + bundle.putString(BUNDLE_KEY_PASSWORD, password) + return bundle + } + + @JvmStatic + fun createFrom(data: Bundle): CreatePasswordRequest { + try { + val id = data.getString(BUNDLE_KEY_ID) + val password = data.getString(BUNDLE_KEY_PASSWORD) + return CreatePasswordRequest(id!!, password!!) + } catch (e: Exception) { + throw FrameworkClassParsingException() + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt new file mode 100644 index 000000000000..26d61f9eb7a9 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * Base request class for registering a public key credential. + * + * @property requestJson The request in JSON format + * @throws NullPointerException If [requestJson] is null. This is handled by the Kotlin runtime + * @throws IllegalArgumentException If [requestJson] is empty + * + * @hide + */ +abstract class CreatePublicKeyCredentialBaseRequest constructor( + val requestJson: String, + type: String, + data: Bundle, + requireSystemProvider: Boolean, +) : CreateCredentialRequest(type, data, requireSystemProvider) { + + init { + require(requestJson.isNotEmpty()) { "request json must not be empty" } + } + + companion object { + const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON" + const val BUNDLE_KEY_SUBTYPE = "androidx.credentials.BUNDLE_KEY_SUBTYPE" + + @JvmStatic + fun createFrom(data: Bundle): CreatePublicKeyCredentialBaseRequest { + return when (data.getString(BUNDLE_KEY_SUBTYPE)) { + CreatePublicKeyCredentialRequest + .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST -> + CreatePublicKeyCredentialRequestPrivileged.createFrom(data) + CreatePublicKeyCredentialRequestPrivileged + .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIVILEGED -> + CreatePublicKeyCredentialRequestPrivileged.createFrom(data) + else -> throw FrameworkClassParsingException() + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt new file mode 100644 index 000000000000..2eda90b827dc --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * A request to register a passkey from the user's public key credential provider. + * + * @property requestJson the request in JSON format + * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request, + * true by default + * @throws NullPointerException If [requestJson] or [allowHybrid] is null. This is handled by the + * Kotlin runtime + * @throws IllegalArgumentException If [requestJson] is empty + * + * @hide + */ +class CreatePublicKeyCredentialRequest @JvmOverloads constructor( + requestJson: String, + @get:JvmName("allowHybrid") + val allowHybrid: Boolean = true +) : CreatePublicKeyCredentialBaseRequest( + requestJson, + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, + toBundle(requestJson, allowHybrid), + false, +) { + companion object { + const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID" + const val BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST = + "androidx.credentials.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST" + + @JvmStatic + internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle { + val bundle = Bundle() + bundle.putString(BUNDLE_KEY_SUBTYPE, + BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST) + bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson) + bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid) + return bundle + } + + @JvmStatic + fun createFrom(data: Bundle): CreatePublicKeyCredentialRequest { + try { + val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON) + val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID) + return CreatePublicKeyCredentialRequest(requestJson!!, (allowHybrid!!) as Boolean) + } catch (e: Exception) { + throw FrameworkClassParsingException() + } + } + } +}
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt new file mode 100644 index 000000000000..36324f83a7e5 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * A privileged request to register a passkey from the user’s public key credential provider, where + * the caller can modify the rp. Only callers with privileged permission, e.g. user’s default + * brower, caBLE, can use this. + * + * @property requestJson the privileged request in JSON format + * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request, + * true by default + * @property rp the expected true RP ID which will override the one in the [requestJson] + * @property clientDataHash a hash that is used to verify the [rp] Identity + * @throws NullPointerException If any of [allowHybrid], [requestJson], [rp], or [clientDataHash] is + * null. This is handled by the Kotlin runtime + * @throws IllegalArgumentException If any of [requestJson], [rp], or [clientDataHash] is empty + * + * @hide + */ +class CreatePublicKeyCredentialRequestPrivileged @JvmOverloads constructor( + requestJson: String, + val rp: String, + val clientDataHash: String, + @get:JvmName("allowHybrid") + val allowHybrid: Boolean = true +) : CreatePublicKeyCredentialBaseRequest( + requestJson, + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, + toBundle(requestJson, rp, clientDataHash, allowHybrid), + false, +) { + + init { + require(rp.isNotEmpty()) { "rp must not be empty" } + require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" } + } + + /** A builder for [CreatePublicKeyCredentialRequestPrivileged]. */ + class Builder(var requestJson: String, var rp: String, var clientDataHash: String) { + + private var allowHybrid: Boolean = true + + /** + * Sets the privileged request in JSON format. + */ + fun setRequestJson(requestJson: String): Builder { + this.requestJson = requestJson + return this + } + + /** + * Sets whether hybrid credentials are allowed to fulfill this request, true by default. + */ + fun setAllowHybrid(allowHybrid: Boolean): Builder { + this.allowHybrid = allowHybrid + return this + } + + /** + * Sets the expected true RP ID which will override the one in the [requestJson]. + */ + fun setRp(rp: String): Builder { + this.rp = rp + return this + } + + /** + * Sets a hash that is used to verify the [rp] Identity. + */ + fun setClientDataHash(clientDataHash: String): Builder { + this.clientDataHash = clientDataHash + return this + } + + /** Builds a [CreatePublicKeyCredentialRequestPrivileged]. */ + fun build(): CreatePublicKeyCredentialRequestPrivileged { + return CreatePublicKeyCredentialRequestPrivileged(this.requestJson, + this.rp, this.clientDataHash, this.allowHybrid) + } + } + + companion object { + const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP" + const val BUNDLE_KEY_CLIENT_DATA_HASH = + "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH" + const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID" + const val BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIVILEGED = + "androidx.credentials.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_" + + "PRIVILEGED" + + @JvmStatic + internal fun toBundle( + requestJson: String, + rp: String, + clientDataHash: String, + allowHybrid: Boolean + ): Bundle { + val bundle = Bundle() + bundle.putString(BUNDLE_KEY_SUBTYPE, + BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIVILEGED) + bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson) + bundle.putString(BUNDLE_KEY_RP, rp) + bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash) + bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid) + return bundle + } + + @JvmStatic + fun createFrom(data: Bundle): CreatePublicKeyCredentialRequestPrivileged { + try { + val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON) + val rp = data.getString(BUNDLE_KEY_RP) + val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH) + val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID) + return CreatePublicKeyCredentialRequestPrivileged( + requestJson!!, + rp!!, + clientDataHash!!, + (allowHybrid!!) as Boolean, + ) + } catch (e: Exception) { + throw FrameworkClassParsingException() + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt new file mode 100644 index 000000000000..ee08e9e30649 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * Base class for a credential with which the user consented to authenticate to the app. + * + * @property type the credential type + * @property data the credential data in the [Bundle] format. + */ +open class Credential(val type: String, val data: Bundle) diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt new file mode 100644 index 000000000000..497c272750ac --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +/** + * Internal exception used to indicate a parsing error while converting from a framework type to + * a jetpack type. + * + * @hide + */ +internal class FrameworkClassParsingException : Exception()
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt new file mode 100644 index 000000000000..eb65241ed4df --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.credentials.Credential +import android.os.Bundle + +/** + * Base request class for getting a registered credential. + * + * @property type the credential type + * @property data the request data in the [Bundle] format + * @property requireSystemProvider true if must only be fulfilled by a system provider and false + * otherwise + */ +open class GetCredentialOption( + val type: String, + val data: Bundle, + val requireSystemProvider: Boolean, +) { + companion object { + @JvmStatic + fun createFrom(from: android.credentials.GetCredentialOption): GetCredentialOption { + return try { + when (from.type) { + Credential.TYPE_PASSWORD_CREDENTIAL -> + GetPasswordOption.createFrom(from.data) + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> + GetPublicKeyCredentialBaseOption.createFrom(from.data) + else -> + GetCredentialOption(from.type, from.data, from.requireSystemProvider()) + } + } catch (e: FrameworkClassParsingException) { + GetCredentialOption(from.type, from.data, from.requireSystemProvider()) + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt new file mode 100644 index 000000000000..7f9256ed6c75 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +/** + * Encapsulates a request to get a user credential. + * + * @property getCredentialOptions the list of [GetCredentialOption] from which the user can choose + * one to authenticate to the app + * @throws IllegalArgumentException If [getCredentialOptions] is empty + */ +class GetCredentialRequest constructor( + val getCredentialOptions: List<GetCredentialOption>, +) { + + init { + require(getCredentialOptions.isNotEmpty()) { "credentialRequests should not be empty" } + } + + /** A builder for [GetCredentialRequest]. */ + class Builder { + private var getCredentialOptions: MutableList<GetCredentialOption> = mutableListOf() + + /** Adds a specific type of [GetCredentialOption]. */ + fun addGetCredentialOption(getCredentialOption: GetCredentialOption): Builder { + getCredentialOptions.add(getCredentialOption) + return this + } + + /** Sets the list of [GetCredentialOption]. */ + fun setGetCredentialOptions(getCredentialOptions: List<GetCredentialOption>): Builder { + this.getCredentialOptions = getCredentialOptions.toMutableList() + return this + } + + /** + * Builds a [GetCredentialRequest]. + * + * @throws IllegalArgumentException If [getCredentialOptions] is empty + */ + fun build(): GetCredentialRequest { + return GetCredentialRequest(getCredentialOptions.toList()) + } + } + + companion object { + @JvmStatic + fun createFrom(from: android.credentials.GetCredentialRequest): GetCredentialRequest { + return GetCredentialRequest( + from.getCredentialOptions.map {GetCredentialOption.createFrom(it)} + ) + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt new file mode 100644 index 000000000000..2facad17b04e --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.credentials.Credential +import android.os.Bundle + +/** A request to retrieve the user's saved application password from their password provider. */ +class GetPasswordOption : GetCredentialOption( + Credential.TYPE_PASSWORD_CREDENTIAL, + Bundle(), + false, +) { + companion object { + @JvmStatic + fun createFrom(data: Bundle): GetPasswordOption { + return GetPasswordOption() + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialBaseOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialBaseOption.kt new file mode 100644 index 000000000000..9b51b306dd6b --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialBaseOption.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * Base request class for getting a registered public key credential. + * + * @property requestJson the request in JSON format + * @throws NullPointerException If [requestJson] is null - auto handled by the + * Kotlin runtime + * @throws IllegalArgumentException If [requestJson] is empty + * + * @hide + */ +abstract class GetPublicKeyCredentialBaseOption constructor( + val requestJson: String, + type: String, + data: Bundle, + requireSystemProvider: Boolean, +) : GetCredentialOption(type, data, requireSystemProvider) { + + init { + require(requestJson.isNotEmpty()) { "request json must not be empty" } + } + + companion object { + const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON" + const val BUNDLE_KEY_SUBTYPE = "androidx.credentials.BUNDLE_KEY_SUBTYPE" + + @JvmStatic + fun createFrom(data: Bundle): GetPublicKeyCredentialBaseOption { + return when (data.getString(BUNDLE_KEY_SUBTYPE)) { + GetPublicKeyCredentialOption + .BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION -> + GetPublicKeyCredentialOption.createFrom(data) + GetPublicKeyCredentialOptionPrivileged + .BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION_PRIVILEGED -> + GetPublicKeyCredentialOptionPrivileged.createFrom(data) + else -> throw FrameworkClassParsingException() + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt new file mode 100644 index 000000000000..6f13c17f9b6e --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * A request to get passkeys from the user's public key credential provider. + * + * @property requestJson the request in JSON format + * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request, + * true by default + * @throws NullPointerException If [requestJson] or [allowHybrid] is null. It is handled by the + * Kotlin runtime + * @throws IllegalArgumentException If [requestJson] is empty + * + * @hide + */ +class GetPublicKeyCredentialOption @JvmOverloads constructor( + requestJson: String, + @get:JvmName("allowHybrid") + val allowHybrid: Boolean = true, +) : GetPublicKeyCredentialBaseOption( + requestJson, + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, + toBundle(requestJson, allowHybrid), + false +) { + companion object { + const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID" + const val BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION = + "androidx.credentials.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION" + + @JvmStatic + internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle { + val bundle = Bundle() + bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson) + bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid) + return bundle + } + + @JvmStatic + fun createFrom(data: Bundle): GetPublicKeyCredentialOption { + try { + val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON) + val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID) + return GetPublicKeyCredentialOption(requestJson!!, (allowHybrid!!) as Boolean) + } catch (e: Exception) { + throw FrameworkClassParsingException() + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt new file mode 100644 index 000000000000..79c62a1cdfbe --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * A privileged request to get passkeys from the user's public key credential provider. The caller + * can modify the RP. Only callers with privileged permission (e.g. user's public browser or caBLE) + * can use this. + * + * @property requestJson the privileged request in JSON format + * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request, + * true by default + * @property rp the expected true RP ID which will override the one in the [requestJson] + * @property clientDataHash a hash that is used to verify the [rp] Identity + * @throws NullPointerException If any of [allowHybrid], [requestJson], [rp], or [clientDataHash] + * is null. This is handled by the Kotlin runtime + * @throws IllegalArgumentException If any of [requestJson], [rp], or [clientDataHash] is empty + * + * @hide + */ +class GetPublicKeyCredentialOptionPrivileged @JvmOverloads constructor( + requestJson: String, + val rp: String, + val clientDataHash: String, + @get:JvmName("allowHybrid") + val allowHybrid: Boolean = true +) : GetPublicKeyCredentialBaseOption( + requestJson, + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, + toBundle(requestJson, rp, clientDataHash, allowHybrid), + false, +) { + + init { + require(rp.isNotEmpty()) { "rp must not be empty" } + require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" } + } + + /** A builder for [GetPublicKeyCredentialOptionPrivileged]. */ + class Builder(var requestJson: String, var rp: String, var clientDataHash: String) { + + private var allowHybrid: Boolean = true + + /** + * Sets the privileged request in JSON format. + */ + fun setRequestJson(requestJson: String): Builder { + this.requestJson = requestJson + return this + } + + /** + * Sets whether hybrid credentials are allowed to fulfill this request, true by default. + */ + fun setAllowHybrid(allowHybrid: Boolean): Builder { + this.allowHybrid = allowHybrid + return this + } + + /** + * Sets the expected true RP ID which will override the one in the [requestJson]. + */ + fun setRp(rp: String): Builder { + this.rp = rp + return this + } + + /** + * Sets a hash that is used to verify the [rp] Identity. + */ + fun setClientDataHash(clientDataHash: String): Builder { + this.clientDataHash = clientDataHash + return this + } + + /** Builds a [GetPublicKeyCredentialOptionPrivileged]. */ + fun build(): GetPublicKeyCredentialOptionPrivileged { + return GetPublicKeyCredentialOptionPrivileged(this.requestJson, + this.rp, this.clientDataHash, this.allowHybrid) + } + } + + companion object { + const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP" + const val BUNDLE_KEY_CLIENT_DATA_HASH = + "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH" + const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID" + const val BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION_PRIVILEGED = + "androidx.credentials.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION" + + "_PRIVILEGED" + + @JvmStatic + internal fun toBundle( + requestJson: String, + rp: String, + clientDataHash: String, + allowHybrid: Boolean + ): Bundle { + val bundle = Bundle() + bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson) + bundle.putString(BUNDLE_KEY_RP, rp) + bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash) + bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid) + return bundle + } + + @JvmStatic + fun createFrom(data: Bundle): GetPublicKeyCredentialOptionPrivileged { + try { + val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON) + val rp = data.getString(BUNDLE_KEY_RP) + val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH) + val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID) + return GetPublicKeyCredentialOptionPrivileged( + requestJson!!, + rp!!, + clientDataHash!!, + (allowHybrid!!) as Boolean, + ) + } catch (e: Exception) { + throw FrameworkClassParsingException() + } + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt new file mode 100644 index 000000000000..b45a63bcf4ec --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.jetpack.developer + +import android.os.Bundle + +/** + * Represents the user's passkey credential granted by the user for app sign-in. + * + * @property authenticationResponseJson the public key credential authentication response in + * JSON format that follows the standard webauthn json format shown at + * [this w3c link](https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson) + * @throws NullPointerException If [authenticationResponseJson] is null. This is handled by the + * kotlin runtime + * @throws IllegalArgumentException If [authenticationResponseJson] is empty + * + * @hide + */ +class PublicKeyCredential constructor( + val authenticationResponseJson: String +) : Credential( + TYPE_PUBLIC_KEY_CREDENTIAL, + toBundle(authenticationResponseJson) +) { + + init { + require(authenticationResponseJson.isNotEmpty()) { + "authentication response JSON must not be empty" } + } + companion object { + /** The type value for public key credential related operations. */ + const val TYPE_PUBLIC_KEY_CREDENTIAL: String = + "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" + const val BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON = + "androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON" + + @JvmStatic + internal fun toBundle(authenticationResponseJson: String): Bundle { + val bundle = Bundle() + bundle.putString(BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, authenticationResponseJson) + return bundle + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/ActionUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt index d4341b498fe0..1e639fe6bd55 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/ActionUi.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.credentialmanager.jetpack +package com.android.credentialmanager.jetpack.provider import android.app.slice.Slice import android.credentials.ui.Entry diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt index d6f1b5f5c8e9..12ab436e1507 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.credentialmanager.jetpack +package com.android.credentialmanager.jetpack.provider import android.app.slice.Slice import android.graphics.drawable.Icon diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt index bb3b206500b4..c5dbe66e8dbb 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.credentialmanager.jetpack +package com.android.credentialmanager.jetpack.provider import android.app.slice.Slice import android.credentials.ui.Entry diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt index 7311b7081343..5049503b32c1 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.credentialmanager.jetpack +package com.android.credentialmanager.jetpack.provider import android.app.slice.Slice import android.credentials.ui.Entry diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt index fad3309fb86f..b260cf63587c 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.credentialmanager.jetpack +package com.android.credentialmanager.jetpack.provider import android.app.slice.Slice import android.credentials.ui.Entry |