From 98b43e2d742266136488e33099f7ce01393588d3 Mon Sep 17 00:00:00 2001 From: Peter Sorotokin Date: Sat, 9 Nov 2024 08:39:30 +0100 Subject: [PATCH] Process pre-authorized openid4vci credential offers. Signed-off-by: Peter Sorotokin --- .../knowntypes/EUCertificateOfResidence.kt | 308 ++++++++++++++++++ .../EvidenceRequestPreauthorizedCode.kt | 7 + .../EvidenceResponsePreauthorizedCode.kt | 9 + .../identity/issuance/funke/FunkeAccess.kt | 3 +- .../funke/FunkeIssuingAuthorityState.kt | 56 ++-- .../issuance/funke/FunkeProofingState.kt | 121 ++++--- .../identity/issuance/funke/FunkeUtil.kt | 14 +- .../funke/openid4VciIssuerMetadata.kt | 25 +- .../mdoc/mso/MobileSecurityObjectParser.kt | 3 +- .../resources/resources/generic/card_art.png | Bin 0 -> 51647 bytes .../main/resources/resources/generic/tos.html | 4 + server/src/main/webapp/openid4vci/index.html | 3 +- .../remote/LocalDevelopmentEnvironment.kt | 6 + .../wallet/MainActivity.kt | 56 ++-- .../wallet/ProvisioningViewModel.kt | 65 ++-- .../wallet/WalletApplication.kt | 2 + .../CredentialOfferIssuance.kt | 98 +++--- .../addtowallet/AddToWalletScreen.kt | 37 +-- .../ProvisionCredentialScreen.kt | 21 +- .../main/res/drawable/card_art_generic.png | Bin 0 -> 51647 bytes wallet/src/main/res/values/strings.xml | 9 + 21 files changed, 638 insertions(+), 209 deletions(-) create mode 100644 identity-doctypes/src/commonMain/kotlin/com/android/identity/documenttype/knowntypes/EUCertificateOfResidence.kt create mode 100644 identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceRequestPreauthorizedCode.kt create mode 100644 identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceResponsePreauthorizedCode.kt create mode 100644 server/src/main/resources/resources/generic/card_art.png create mode 100644 server/src/main/resources/resources/generic/tos.html create mode 100644 wallet/src/main/res/drawable/card_art_generic.png diff --git a/identity-doctypes/src/commonMain/kotlin/com/android/identity/documenttype/knowntypes/EUCertificateOfResidence.kt b/identity-doctypes/src/commonMain/kotlin/com/android/identity/documenttype/knowntypes/EUCertificateOfResidence.kt new file mode 100644 index 000000000..19df9cdd7 --- /dev/null +++ b/identity-doctypes/src/commonMain/kotlin/com/android/identity/documenttype/knowntypes/EUCertificateOfResidence.kt @@ -0,0 +1,308 @@ +package com.android.identity.documenttype.knowntypes + +import com.android.identity.cbor.toDataItem +import com.android.identity.cbor.toDataItemFullDate +import com.android.identity.documenttype.DocumentAttributeType +import com.android.identity.documenttype.DocumentType +import com.android.identity.documenttype.Icon + +/** + * Object containing the metadata of the EU Certificate of Residency (COR) document. + * + * TODO: see if this document type still exists and how exactly it is defined. This + * definition is ad hoc and added to facilitate interoperability testing. + */ +object EUCertificateOfResidence { + const val DOCTYPE = "eu.europa.ec.eudi.cor.1" + const val NAMESPACE = "eu.europa.ec.eudi.cor.1" + const val VCT = "https://example.eudi.ec.europa.eu/cor/1" + + /** + * Build the EU Personal ID Document Type. + */ + fun getDocumentType(): DocumentType { + return DocumentType.Builder("EU Certificate of Residency") + .addMdocDocumentType(DOCTYPE) + .addVcDocumentType(VCT) + .addAttribute( + DocumentAttributeType.String, + "family_name", + "Family Name", + "Current last name(s), surname(s), or primary identifier of the COR holder", + true, + NAMESPACE, + Icon.PERSON, + SampleData.FAMILY_NAME.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "given_name", + "Given Names", + "Current first name(s), other name(s), or secondary identifier of the COR holder", + true, + NAMESPACE, + Icon.PERSON, + SampleData.GIVEN_NAME.toDataItem() + ) + .addAttribute( + DocumentAttributeType.Date, + "birth_date", + "Date of Birth", + "Day, month, and year on which the COR holder was born. If unknown, approximate date of birth.", + true, + NAMESPACE, + Icon.TODAY, + SampleData.birthDate.toDataItemFullDate() + ) + .addAttribute( + DocumentAttributeType.Boolean, + "age_over_18", + "Older Than 18", + "Age over 18?", + false, + NAMESPACE, + Icon.TODAY, + SampleData.AGE_OVER_18.toDataItem() + ) + .addAttribute( + DocumentAttributeType.Boolean, + "age_over_21", + "Older Than 21", + "Age over 21?", + false, + NAMESPACE, + Icon.TODAY, + SampleData.AGE_OVER_21.toDataItem() + ) + .addAttribute( + DocumentAttributeType.Date, + "arrival_date", + "Date of Arrival", + "Day, month, and year on which the COR holder arrived to the EU.", + false, + NAMESPACE, + Icon.DATE_RANGE, + SampleData.issueDate.toDataItemFullDate() + ) + .addAttribute( + DocumentAttributeType.String, + "resident_address", + "Resident Address", + "The full address of the place where the COR holder currently resides and/or may be contacted (street/house number, municipality etc.)", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_ADDRESS.toDataItem() + ) + .addAttribute( + DocumentAttributeType.StringOptions(Options.COUNTRY_ISO_3166_1_ALPHA_2), + "resident_country", + "Resident Country", + "The country where the PID User currently resides, as an Alpha-2 country code as specified in ISO 3166-1", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_COUNTRY.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "resident_state", + "Resident State", + "The state, province, district, or local area where the PID User currently resides.", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_STATE.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "resident_city", + "Resident City", + "The city where the COR holder currently resides", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_CITY.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "resident_postal_code", + "Resident Postal Code", + "The postal code of the place where the COR holder currently resides", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_POSTAL_CODE.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "resident_street", + "Resident Street", + "The name of the street where the PID User currently resides.", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_STREET.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "resident_house_number", + "Resident House Number", + "The house number where the PID User currently resides, including any affix or suffix", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_HOUSE_NUMBER.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "birth_place", + "Place of Birth", + "The place where the COR holder was born.", + false, + NAMESPACE, + Icon.PLACE, + SampleData.RESIDENT_CITY.toDataItem() + ) + .addAttribute( + DocumentAttributeType.IntegerOptions(Options.SEX_ISO_IEC_5218), + "gender", + "Gender", + "COR holder’s gender", + false, + NAMESPACE, + Icon.EMERGENCY, + SampleData.SEX_ISO218.toDataItem() + ) + .addAttribute( + DocumentAttributeType.StringOptions(Options.COUNTRY_ISO_3166_1_ALPHA_2), + "nationality", + "Nationality", + "Alpha-2 country code as specified in ISO 3166-1, representing the nationality of the PID User.", + true, + NAMESPACE, + Icon.LANGUAGE, + SampleData.NATIONALITY.toDataItem() + ) + .addAttribute( + DocumentAttributeType.Date, + "issuance_date", + "Date of Issue", + "Date (and possibly time) when the PID was issued.", + true, + NAMESPACE, + Icon.DATE_RANGE, + SampleData.issueDate.toDataItemFullDate() + ) + .addAttribute( + DocumentAttributeType.Date, + "expiry_date", + "Date of Expiry", + "Date (and possibly time) when the PID will expire.", + true, + NAMESPACE, + Icon.CALENDAR_CLOCK, + SampleData.expiryDate.toDataItemFullDate() + ) + .addAttribute( + DocumentAttributeType.String, + "issuing_authority", + "Issuing Authority", + "Name of the administrative authority that has issued this PID instance, or the " + + "ISO 3166 Alpha-2 country code of the respective Member State if there is" + + "no separate authority authorized to issue PIDs.", + true, + NAMESPACE, + Icon.ACCOUNT_BALANCE, + SampleData.ISSUING_AUTHORITY_EU_PID.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "document_number", + "Document Number", + "A number for the PID, assigned by the PID Provider.", + false, + NAMESPACE, + Icon.NUMBERS, + SampleData.DOCUMENT_NUMBER.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "administrative_number", + "Administrative Number", + "A number assigned by the PID Provider for audit control or other purposes.", + false, + NAMESPACE, + Icon.NUMBERS, + SampleData.ADMINISTRATIVE_NUMBER.toDataItem() + ) + .addAttribute( + DocumentAttributeType.String, + "issuing_jurisdiction", + "Issuing Jurisdiction", + "Country subdivision code of the jurisdiction that issued the PID, as defined in " + + "ISO 3166-2:2020, Clause 8. The first part of the code SHALL be the same " + + "as the value for issuing_country.", + false, + NAMESPACE, + Icon.ACCOUNT_BALANCE, + SampleData.ISSUING_JURISDICTION.toDataItem() + ) + .addAttribute( + DocumentAttributeType.StringOptions(Options.COUNTRY_ISO_3166_1_ALPHA_2), + "issuing_country", + "Issuing Country", + "Alpha-2 country code, as defined in ISO 3166-1, of the issuing authority’s " + + "country or territory", + true, + NAMESPACE, + Icon.ACCOUNT_BALANCE, + SampleData.ISSUING_COUNTRY.toDataItem() + ) + .addSampleRequest( + id = "age_over_18", + displayName = "Age Over 18", + mdocDataElements = mapOf( + NAMESPACE to mapOf( + "age_over_18" to false, + ) + ), + vcClaims = listOf("age_over_18") + ) + .addSampleRequest( + id = "mandatory", + displayName = "Mandatory Data Elements", + mdocDataElements = mapOf( + NAMESPACE to mapOf( + "family_name" to false, + "given_name" to false, + "birth_date" to false, + "age_over_18" to false, + "issuance_date" to false, + "expiry_date" to false, + "issuing_authority" to false, + "issuing_country" to false + ) + ), + vcClaims = listOf( + "family_name", + "given_name", + "birth_date", + "age_over_18", + "issuance_date", + "expiry_date", + "issuing_authority", + "issuing_country" + ) + ) + .addSampleRequest( + id = "full", + displayName = "All Data Elements", + mdocDataElements = mapOf( + NAMESPACE to mapOf() + ), + vcClaims = listOf() + ) + .build() + } +} \ No newline at end of file diff --git a/identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceRequestPreauthorizedCode.kt b/identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceRequestPreauthorizedCode.kt new file mode 100644 index 000000000..1b2ef5dac --- /dev/null +++ b/identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceRequestPreauthorizedCode.kt @@ -0,0 +1,7 @@ +package com.android.identity.issuance.evidence + +/** + * Request pre-authorized code from the client. Pre-authorized code is given to a wallet app + * as a part of an OpenId4VCI credential offer. + */ +class EvidenceRequestPreauthorizedCode : EvidenceRequest() \ No newline at end of file diff --git a/identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceResponsePreauthorizedCode.kt b/identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceResponsePreauthorizedCode.kt new file mode 100644 index 000000000..84df820ca --- /dev/null +++ b/identity-issuance/src/main/java/com/android/identity/issuance/evidence/EvidenceResponsePreauthorizedCode.kt @@ -0,0 +1,9 @@ +package com.android.identity.issuance.evidence + +/** + * Provides OpenId4VCI pre-authorized code to the issuer. + */ +data class EvidenceResponsePreauthorizedCode( + val code: String, // preauthorized code + val txCode: String? // transaction code entered by the user (may or may not be required) +) : EvidenceResponse() \ No newline at end of file diff --git a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeAccess.kt b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeAccess.kt index c06c10b3b..b799ed960 100644 --- a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeAccess.kt +++ b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeAccess.kt @@ -17,14 +17,13 @@ import kotlin.time.Duration.Companion.seconds class FunkeAccess( val accessToken: String, val accessTokenExpiration: Instant, - var dpopNonce: String, + var dpopNonce: String?, var cNonce: String?, var refreshToken: String?, ) { companion object { suspend fun parseResponse(tokenResponse: HttpResponse): FunkeAccess { val dpopNonce = tokenResponse.headers["DPoP-Nonce"] - ?: throw IllegalArgumentException("No DPoP nonce in token response") val tokenString = String(tokenResponse.readBytes()) val token = Json.parseToJsonElement(tokenString) as JsonObject val accessToken = getField(token, "access_token").content diff --git a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeIssuingAuthorityState.kt b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeIssuingAuthorityState.kt index f9bda84c5..40cab0696 100644 --- a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeIssuingAuthorityState.kt +++ b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeIssuingAuthorityState.kt @@ -11,6 +11,7 @@ import com.android.identity.document.NameSpacedData import com.android.identity.documenttype.DocumentType import com.android.identity.documenttype.DocumentTypeRepository import com.android.identity.documenttype.knowntypes.DrivingLicense +import com.android.identity.documenttype.knowntypes.EUCertificateOfResidence import com.android.identity.documenttype.knowntypes.EUPersonalID import com.android.identity.documenttype.knowntypes.PhotoID import com.android.identity.flow.annotation.FlowJoin @@ -143,7 +144,7 @@ class FunkeIssuingAuthorityState( } } if (cardArt == null) { - val artPath = "funke/card_art_funke_generic.png" + val artPath = "generic/card_art.png" cardArt = resources.getRawResource(artPath)!!.toByteArray() } val requireUserAuthenticationToViewDocument = false @@ -179,6 +180,7 @@ class FunkeIssuingAuthorityState( addDocumentType(EUPersonalID.getDocumentType()) addDocumentType(DrivingLicense.getDocumentType()) addDocumentType(PhotoID.getDocumentType()) + addDocumentType(EUCertificateOfResidence.getDocumentType()) } } @@ -243,11 +245,10 @@ class FunkeIssuingAuthorityState( @FlowMethod suspend fun proof(env: FlowEnvironment, documentId: String): FunkeProofingState { - val proofingInfo = performPushedAuthorizationRequest(env) val metadata = Openid4VciIssuerMetadata.get(env, credentialIssuerUri) - val authorizationMetadata = metadata.authorizationServers[0] var openid4VpRequest: String? = null - if (proofingInfo.authSession != null && proofingInfo.openid4VpPresentation != null) { + val proofingInfo = performPushedAuthorizationRequest(env) + if (proofingInfo?.authSession != null && proofingInfo.openid4VpPresentation != null) { val httpClient = env.getInterface(HttpClient::class)!! val presentationResponse = httpClient.get(proofingInfo.openid4VpPresentation) {} if (presentationResponse.status == HttpStatusCode.OK) { @@ -262,17 +263,20 @@ class FunkeIssuingAuthorityState( )?.let { WalletApplicationCapabilities.fromCbor(it.toByteArray()) } ?: throw IllegalStateException("WalletApplicationCapabilities not found") + val useGermanId = metadata.authorizationServers.isNotEmpty() + && metadata.authorizationServers[0].useGermanEId return FunkeProofingState( - credentialIssuerUri = credentialIssuerUri, - clientId = clientId, - issuanceClientId = issuanceClientId, - documentId = documentId, - proofingInfo = proofingInfo, - applicationCapabilities = applicationCapabilities, - // Don't show TOS when using browser API - tosAcknowleged = !authorizationMetadata.useGermanEId, - openid4VpRequest = openid4VpRequest - ) + clientId = clientId, + credentialConfigurationId = credentialConfigurationId, + issuanceClientId = issuanceClientId, + documentId = documentId, + credentialIssuerUri = credentialIssuerUri, + proofingInfo = proofingInfo, + applicationCapabilities = applicationCapabilities, + tokenUri = metadata.tokenEndpoint, + openid4VpRequest = openid4VpRequest, + useGermanEId = useGermanId + ) } @FlowJoin @@ -294,7 +298,8 @@ class FunkeIssuingAuthorityState( issuerDocument.state = DocumentCondition.READY if (issuerDocument.documentConfiguration == null) { val metadata = Openid4VciIssuerMetadata.get(env, credentialIssuerUri) - if (metadata.authorizationServers[0].useGermanEId) { + if (metadata.authorizationServers.isNotEmpty() && + metadata.authorizationServers[0].useGermanEId) { val isCloudSecureArea = issuerDocument.secureAreaIdentifier!!.startsWith("CloudSecureArea?") issuerDocument.documentConfiguration = @@ -573,13 +578,13 @@ class FunkeIssuingAuthorityState( } } - private suspend fun performPushedAuthorizationRequest( - env: FlowEnvironment - ): ProofingInfo { + private suspend fun performPushedAuthorizationRequest(env: FlowEnvironment): ProofingInfo? { val metadata = Openid4VciIssuerMetadata.get(env, credentialIssuerUri) + if (metadata.authorizationServers.isEmpty()) { + return null + } val config = metadata.credentialConfigurations[credentialConfigurationId]!! val authorizationMetadata = metadata.authorizationServers[0] - val pkceCodeVerifier = Random.Default.nextBytes(32).toBase64Url() val codeChallenge = Crypto.digest(Algorithm.SHA256, pkceCodeVerifier.toByteArray()).toBase64Url() @@ -733,6 +738,12 @@ class FunkeIssuingAuthorityState( ) } is Openid4VciFormatMdoc -> { + val documentType = documentTypeRepository.getDocumentTypeForMdoc(config.format.docType) + val staticData = if (documentType != null) { + fillInSampleData(documentType).build() + } else { + NameSpacedData.Builder().build() + } DocumentConfiguration( base.displayName, base.typeDisplayName, @@ -740,9 +751,7 @@ class FunkeIssuingAuthorityState( base.requireUserAuthenticationToViewDocument, MdocDocumentConfiguration( config.format.docType, - staticData = fillInSampleData( - documentTypeRepository.getDocumentTypeForMdoc(config.format.docType)!! - ).build() + staticData = staticData ), null ) @@ -773,7 +782,6 @@ class FunkeIssuingAuthorityState( document: FunkeIssuerDocument ) { val metadata = Openid4VciIssuerMetadata.get(env, credentialIssuerUri) - val authorizationMetadata = metadata.authorizationServers[0] var access = document.access!! val nowPlusSlack = Clock.System.now() + 30.seconds if (access.cNonce != null && nowPlusSlack < access.accessTokenExpiration) { @@ -786,7 +794,7 @@ class FunkeIssuingAuthorityState( env = env, clientId = clientId, issuanceClientId = issuanceClientId, - tokenUrl = authorizationMetadata.tokenEndpoint, + tokenUrl = metadata.tokenEndpoint, refreshToken = refreshToken, accessToken = access.accessToken ) diff --git a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeProofingState.kt b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeProofingState.kt index 8c53c2da8..b80b9ff66 100644 --- a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeProofingState.kt +++ b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeProofingState.kt @@ -15,6 +15,7 @@ import com.android.identity.issuance.evidence.EvidenceRequestGermanEid import com.android.identity.issuance.evidence.EvidenceRequestMessage import com.android.identity.issuance.evidence.EvidenceRequestNotificationPermission import com.android.identity.issuance.evidence.EvidenceRequestOpenid4Vp +import com.android.identity.issuance.evidence.EvidenceRequestPreauthorizedCode import com.android.identity.issuance.evidence.EvidenceRequestQuestionMultipleChoice import com.android.identity.issuance.evidence.EvidenceRequestSetupCloudSecureArea import com.android.identity.issuance.evidence.EvidenceRequestWeb @@ -23,6 +24,7 @@ import com.android.identity.issuance.evidence.EvidenceResponseGermanEid import com.android.identity.issuance.evidence.EvidenceResponseMessage import com.android.identity.issuance.evidence.EvidenceResponseNotificationPermission import com.android.identity.issuance.evidence.EvidenceResponseOpenid4Vp +import com.android.identity.issuance.evidence.EvidenceResponsePreauthorizedCode import com.android.identity.issuance.evidence.EvidenceResponseQuestionMultipleChoice import com.android.identity.issuance.evidence.EvidenceResponseSetupCloudSecureArea import com.android.identity.issuance.evidence.EvidenceResponseWeb @@ -42,18 +44,20 @@ import kotlinx.serialization.json.jsonPrimitive import java.net.URI import java.net.URLEncoder - @FlowState( flowInterface = ProofingFlow::class ) @CborSerializable class FunkeProofingState( val credentialIssuerUri: String, + val credentialConfigurationId: String, val clientId: String, val issuanceClientId: String, val documentId: String, - val proofingInfo: ProofingInfo, + val proofingInfo: ProofingInfo?, val applicationCapabilities: WalletApplicationCapabilities, + val tokenUri: String, + val useGermanEId: Boolean = false, var access: FunkeAccess? = null, var secureAreaIdentifier: String? = null, var secureAreaSetupDone: Boolean = false, @@ -69,50 +73,66 @@ class FunkeProofingState( suspend fun getEvidenceRequests(env: FlowEnvironment): List { return if (access == null) { if (!tosAcknowleged) { - val message = env.getInterface(Resources::class)!! - .getStringResource("funke/tos.html")!! - listOf(EvidenceRequestMessage( - message = message, - assets = emptyMap(), - acceptButtonText = "Continue", - rejectButtonText = "Cancel" - )) + val message = if (useGermanEId) { + env.getInterface(Resources::class)!! + .getStringResource("funke/tos.html")!! + } else { + val metadata = Openid4VciIssuerMetadata.get(env, credentialIssuerUri) + val issuingAuthorityName = metadata.display[0].text + val documentName = metadata.credentialConfigurations[credentialConfigurationId]!!.display[0].text + env.getInterface(Resources::class)!! + .getStringResource("generic/tos.html")!! + .replace("\$ISSUER_NAME", issuingAuthorityName) + .replace("\$ID_NAME", documentName) + } + listOf( + EvidenceRequestMessage( + message = message, + assets = emptyMap(), + acceptButtonText = "Continue", + rejectButtonText = "Cancel" + ) + ) } else if (!notificationPermissonRequested) { - listOf(EvidenceRequestNotificationPermission( - permissionNotGrantedMessage = """ - ## Receive notifications? - - If there are updates to your document the issuer will send an updated document - to your device. If you are interested, we can send a notification to make you aware - of when this happens. This requires granting a permission. - - If you previously denied this permission, attempting to grant it again might not do - anything and you may need to go into Settings and manually enable - notifications for this application. - """.trimIndent(), - grantPermissionButtonText = "Grant Permission", - continueWithoutPermissionButtonText = "No Thanks", - assets = mapOf() - )) + listOf( + EvidenceRequestNotificationPermission( + permissionNotGrantedMessage = """ + ## Receive notifications? + + If there are updates to your document the issuer will send an updated document + to your device. If you are interested, we can send a notification to make you aware + of when this happens. This requires granting a permission. + + If you previously denied this permission, attempting to grant it again might not do + anything and you may need to go into Settings and manually enable + notifications for this application. + """.trimIndent(), + grantPermissionButtonText = "Grant Permission", + continueWithoutPermissionButtonText = "No Thanks", + assets = mapOf() + )) } else { + val list = mutableListOf(EvidenceRequestPreauthorizedCode()) val metadata = Openid4VciIssuerMetadata.get(env, credentialIssuerUri) - val authorizationMetadata = metadata.authorizationServers[0] - val authorizeUrl = "${authorizationMetadata.authorizationEndpoint}?" + FormUrlEncoder { - add("client_id", issuanceClientId) - add("request_uri", proofingInfo.requestUri!!) - } - if (authorizationMetadata.useGermanEId) { - listOf(EvidenceRequestGermanEid(authorizeUrl, listOf())) - } else if (openid4VpRequest != null) { - val uri = URI(authorizationMetadata.authorizationChallengeEndpoint!!) - val origin = uri.scheme + ":" + uri.authority - listOf( - EvidenceRequestOpenid4Vp(origin, openid4VpRequest!!), - EvidenceRequestWeb(authorizeUrl, proofingInfo.landingUrl) - ) - } else { - listOf(EvidenceRequestWeb(authorizeUrl, proofingInfo.landingUrl)) + if (proofingInfo != null && metadata.authorizationServers.isNotEmpty()) { + val authorizationMetadata = metadata.authorizationServers[0] + val authorizeUrl = + "${authorizationMetadata.authorizationEndpoint}?" + FormUrlEncoder { + add("client_id", issuanceClientId) + add("request_uri", proofingInfo.requestUri!!) + } + if (authorizationMetadata.useGermanEId) { + list.add(EvidenceRequestGermanEid(authorizeUrl, listOf())) + } else { + if (openid4VpRequest != null) { + val uri = URI(authorizationMetadata.authorizationChallengeEndpoint!!) + val origin = uri.scheme + ":" + uri.authority + list.add(EvidenceRequestOpenid4Vp(origin, openid4VpRequest!!)) + } + list.add(EvidenceRequestWeb(authorizeUrl, proofingInfo.landingUrl)) + } } + return list } } else if (secureAreaIdentifier == null) { listOf( @@ -190,6 +210,19 @@ class FunkeProofingState( is EvidenceResponseOpenid4Vp -> { processOpenid4VpResponse(env, evidenceResponse.response) } + is EvidenceResponsePreauthorizedCode -> { + this.access = FunkeUtil.obtainToken( + env = env, + tokenUrl = tokenUri, + clientId = clientId, + issuanceClientId = issuanceClientId, + preauthorizedCode = evidenceResponse.code, + txCode = evidenceResponse.txCode, + codeVerifier = proofingInfo?.pkceCodeVerifier, + dpopNonce = null + ) + Logger.i(TAG, "Token request: success") + } else -> throw IllegalArgumentException("Unexpected evidence type") } } @@ -235,7 +268,7 @@ class FunkeProofingState( clientId = clientId, issuanceClientId = issuanceClientId, authorizationCode = authCode, - codeVerifier = proofingInfo.pkceCodeVerifier, + codeVerifier = proofingInfo?.pkceCodeVerifier, dpopNonce = dpopNonce ) Logger.i(TAG, "Token request: success") @@ -263,7 +296,7 @@ class FunkeProofingState( val dpop = FunkeUtil.generateDPoP(env, clientId, authorizationMetadata.authorizationChallengeEndpoint!!, null, null) val challengeRequest = FormUrlEncoder { - add("auth_session", proofingInfo.authSession!!) + add("auth_session", proofingInfo!!.authSession!!) add("presentation_during_issuance_session", presentationCode) }.toString() val challengeResponse = httpClient.post( diff --git a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeUtil.kt b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeUtil.kt index 9a7849f68..3c3bf975a 100644 --- a/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeUtil.kt +++ b/identity-issuance/src/main/java/com/android/identity/issuance/funke/FunkeUtil.kt @@ -90,18 +90,20 @@ internal object FunkeUtil { refreshToken: String? = null, accessToken: String? = null, authorizationCode: String? = null, + preauthorizedCode: String? = null, + txCode: String? = null, // pin or other transaction code codeVerifier: String? = null, dpopNonce: String? = null ): FunkeAccess { - if (refreshToken == null && authorizationCode == null) { - throw IllegalArgumentException("Neither authorization code, nor refresh token provided") + if (refreshToken == null && authorizationCode == null && preauthorizedCode == null) { + throw IllegalArgumentException("No authorizations provided") } val httpClient = env.getInterface(HttpClient::class)!! var currentDpopNonce = dpopNonce // When dpop nonce is null, this loop will run twice, first request will return with error, // but will provide fresh, dpop nonce and the second request will get fresh access data. while (true) { - val dpop = FunkeUtil.generateDPoP(env, clientId, tokenUrl, currentDpopNonce, null) + val dpop = generateDPoP(env, clientId, tokenUrl, currentDpopNonce, null) val tokenRequest = FormUrlEncoder { if (refreshToken != null) { add("grant_type", "refresh_token") @@ -110,6 +112,12 @@ internal object FunkeUtil { if (authorizationCode != null) { add("grant_type", "authorization_code") add("code", authorizationCode) + } else if (preauthorizedCode != null) { + add("grant_type", "urn:ietf:params:oauth:grant-type:pre-authorized_code") + add("pre-authorized_code", preauthorizedCode) + if (txCode != null) { + add("tx_code", txCode) + } } if (codeVerifier != null) { add("code_verifier", codeVerifier) diff --git a/identity-issuance/src/main/java/com/android/identity/issuance/funke/openid4VciIssuerMetadata.kt b/identity-issuance/src/main/java/com/android/identity/issuance/funke/openid4VciIssuerMetadata.kt index 2d6b8e98b..e82421792 100644 --- a/identity-issuance/src/main/java/com/android/identity/issuance/funke/openid4VciIssuerMetadata.kt +++ b/identity-issuance/src/main/java/com/android/identity/issuance/funke/openid4VciIssuerMetadata.kt @@ -2,6 +2,7 @@ package com.android.identity.issuance.funke import com.android.identity.flow.server.FlowEnvironment import com.android.identity.issuance.common.cache +import com.android.identity.util.Logger import io.ktor.client.HttpClient import io.ktor.client.request.get import io.ktor.client.statement.readBytes @@ -22,9 +23,12 @@ internal data class Openid4VciIssuerMetadata( val credentialEndpoint: String, val display: List, val credentialConfigurations: Map, - val authorizationServers: List + val authorizationServers: List, + val tokenEndpoint: String // fallback token endpoint, not standard ) { companion object { + const val TAG = "Openid4VciIssuerMetadata" + suspend fun get(env: FlowEnvironment, issuerUrl: String): Openid4VciIssuerMetadata { return env.cache(Openid4VciIssuerMetadata::class, issuerUrl) { _, _ -> val httpClient = env.getInterface(HttpClient::class)!! @@ -48,7 +52,10 @@ internal data class Openid4VciIssuerMetadata( "$authorizationServerUrl/.well-known/oauth-authorization-server" val authorizationMetadataRequest = httpClient.get(authorizationMetadataUrl) {} if (authorizationMetadataRequest.status != HttpStatusCode.OK) { - throw IllegalStateException("Invalid authorization server, no $authorizationMetadataUrl") + if (authorizationServerUrl != issuerUrl) { + Logger.e(TAG, "Invalid authorization server '$authorizationServerUrl'") + } + continue } val authorizationMetadataText = String(authorizationMetadataRequest.readBytes()) val authorizationMetadata = @@ -58,8 +65,11 @@ internal data class Openid4VciIssuerMetadata( authorizationMetadataList.add(authorizationMetadata) } } - if (authorizationMetadataList.isEmpty()) { - throw IllegalStateException("No compatible authorization server found in $issuerMetadataUrl") + val tokenEndpoint = if (authorizationMetadataList.isEmpty()) { + credentialMetadata["token_endpoint"]?.jsonPrimitive?.content + ?: "$issuerUrl/token" + } else { + authorizationMetadataList[0].tokenEndpoint } Openid4VciIssuerMetadata( credentialIssuer = credentialMetadata["credential_issuer"]?.jsonPrimitive?.content ?: issuerUrl, @@ -71,7 +81,7 @@ internal data class Openid4VciIssuerMetadata( val obj = it.value.jsonObject Openid4VciCredentialConfiguration( id = it.key, - scope = obj["scope"]!!.jsonPrimitive.content, + scope = obj["scope"]?.jsonPrimitive?.content, cryptographicBindingMethod = preferred( obj["cryptographic_binding_methods_supported"]!!.jsonArray, SUPPORTED_BINDING_METHODS @@ -84,7 +94,8 @@ internal data class Openid4VciIssuerMetadata( format = extractFormat(obj), display = extractDisplay(obj["display"]) ) - } + }, + tokenEndpoint = tokenEndpoint ) } } @@ -210,7 +221,7 @@ internal data class Openid4VciCredentialConfiguration( val format: Openid4VciFormat?, val display: List ) { - val isSupported: Boolean get() = scope != null && cryptographicBindingMethod != null && + val isSupported: Boolean get() = cryptographicBindingMethod != null && credentialSigningAlgorithm != null && proofType != null && format != null } diff --git a/identity-mdoc/src/commonMain/kotlin/com/android/identity/mdoc/mso/MobileSecurityObjectParser.kt b/identity-mdoc/src/commonMain/kotlin/com/android/identity/mdoc/mso/MobileSecurityObjectParser.kt index c35d26584..9e2870fb7 100644 --- a/identity-mdoc/src/commonMain/kotlin/com/android/identity/mdoc/mso/MobileSecurityObjectParser.kt +++ b/identity-mdoc/src/commonMain/kotlin/com/android/identity/mdoc/mso/MobileSecurityObjectParser.kt @@ -18,6 +18,7 @@ package com.android.identity.mdoc.mso import com.android.identity.cbor.Cbor import com.android.identity.cbor.CborArray import com.android.identity.cbor.DataItem +import com.android.identity.cbor.Tagged import com.android.identity.crypto.EcPublicKey import kotlinx.datetime.Instant @@ -205,8 +206,6 @@ class MobileSecurityObjectParser( expectedUpdate = Instant.fromEpochMilliseconds( validityInfo["expectedUpdate"].asDateTimeString.toEpochMilliseconds()) - } else { - expectedUpdate = null } require(validFrom >= signed) { "The validFrom timestamp should be equal or later than the signed timestamp" diff --git a/server/src/main/resources/resources/generic/card_art.png b/server/src/main/resources/resources/generic/card_art.png new file mode 100644 index 0000000000000000000000000000000000000000..64b670a66a85b36aa643ef6b154cf865c3dcd46a GIT binary patch literal 51647 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%0A6blC;3KwljR6VtKmGozfb+8G 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 $ID_NAME credential +

In the following screens, information will be collected for provisioning of $ID_NAME + from $ISSUER_NAME.

+

The created $ID_NAME will be bound to this device.

diff --git a/server/src/main/webapp/openid4vci/index.html b/server/src/main/webapp/openid4vci/index.html index baf56b5a2..b9255b16a 100644 --- a/server/src/main/webapp/openid4vci/index.html +++ b/server/src/main/webapp/openid4vci/index.html @@ -8,4 +8,5 @@ - \ No newline at end of file + + diff --git a/wallet/src/main/java/com/android/identity/issuance/remote/LocalDevelopmentEnvironment.kt b/wallet/src/main/java/com/android/identity/issuance/remote/LocalDevelopmentEnvironment.kt index 94366ebf1..0f7c2691b 100644 --- a/wallet/src/main/java/com/android/identity/issuance/remote/LocalDevelopmentEnvironment.kt +++ b/wallet/src/main/java/com/android/identity/issuance/remote/LocalDevelopmentEnvironment.kt @@ -169,6 +169,10 @@ internal class LocalDevelopmentEnvironment( R.drawable.funke_logo, Bitmap.CompressFormat.PNG ) + "generic/card_art.png" -> bitmapData( + R.drawable.card_art_generic, + Bitmap.CompressFormat.PNG + ) "img_erika_portrait.jpf" -> ByteString(getRawResourceAsBytes(R.raw.img_erika_portrait)) "img_erika_signature.jpf" -> @@ -199,6 +203,8 @@ internal class LocalDevelopmentEnvironment( context.resources.getString(R.string.utopia_local_issuing_authority_photoid_tos) "funke/tos.html" -> context.resources.getString(R.string.funke_issuing_authority_tos) + "generic/tos.html" -> + context.resources.getString(R.string.generic_issuing_authority_tos) else -> null } } diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/MainActivity.kt b/wallet/src/main/java/com/android/identity_credential/wallet/MainActivity.kt index 8ba84d878..9f272a8a9 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/MainActivity.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/MainActivity.kt @@ -26,17 +26,20 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.MutableLiveData import androidx.navigation.compose.rememberNavController import com.android.identity_credential.wallet.credentialoffer.extractCredentialIssuerData -import com.android.identity_credential.wallet.credentialoffer.initiateCredentialOfferIssuance +import com.android.identity_credential.wallet.navigation.WalletDestination import com.android.identity_credential.wallet.navigation.WalletNavigation import com.android.identity_credential.wallet.navigation.navigateTo import com.android.identity_credential.wallet.ui.theme.IdentityCredentialTheme import com.android.identity_credential.wallet.util.getUrlQueryFromCustomSchemeUrl +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class MainActivity : FragmentActivity() { companion object { @@ -46,6 +49,7 @@ class MainActivity : FragmentActivity() { private lateinit var application: WalletApplication private val qrEngagementViewModel: QrEngagementViewModel by viewModels() private val provisioningViewModel: ProvisioningViewModel by viewModels() + private val routeRequest = MutableLiveData(null) private val permissionTracker: PermissionTracker = if (Build.VERSION.SDK_INT >= 31) { PermissionTracker(this, mapOf( @@ -89,31 +93,14 @@ class MainActivity : FragmentActivity() { color = MaterialTheme.colorScheme.background ) { val navController = rememberNavController() - // observe whether a new intent was received with credential offer url - val credentialOfferIntentPayload by provisioningViewModel.newCredentialOfferIntentReceived.collectAsState() - // if not null, execute once - LaunchedEffect(credentialOfferIntentPayload) { - if (credentialOfferIntentPayload != null) { - val credentialIssuerUri = credentialOfferIntentPayload!!.first - val credentialIssuerConfigurationId = - credentialOfferIntentPayload!!.second - initiateCredentialOfferIssuance( - walletServerProvider = application.walletServerProvider, - provisioningViewModel = provisioningViewModel, - settingsModel = application.settingsModel, - documentStore = application.documentStore, - onNavigate = { routeWithArgs -> - navigateTo(navController, routeWithArgs) - }, - credentialIssuerUri = credentialIssuerUri, - credentialIssuerConfigurationId = credentialIssuerConfigurationId, - ) - // reset the state (consume the Url) - provisioningViewModel.onNewCredentialOfferIntent(null, null) + val route = routeRequest.observeAsState() + LaunchedEffect(route.value) { + if (route.value != null) { + navigateTo(navController, routeRequest.value!!) + routeRequest.value = null } } - WalletNavigation( navController, application = application, @@ -152,12 +139,19 @@ class MainActivity : FragmentActivity() { if (intent.action == Intent.ACTION_VIEW) { // perform recomposition only if deep link url starts with oid4vci credential offer scheme if (intent.dataString?.startsWith(WalletApplication.OID4VCI_CREDENTIAL_OFFER_URL_SCHEME) == true) { - val decodedQuery = getUrlQueryFromCustomSchemeUrl(intent.dataString!!) - extractCredentialIssuerData(decodedQuery).let { (credentialIssuerUri, credentialConfigurationId) -> - provisioningViewModel.onNewCredentialOfferIntent( - credentialIssuerUri, - credentialConfigurationId - ) + CoroutineScope(Dispatchers.Main).launch { + val query = getUrlQueryFromCustomSchemeUrl(intent.dataString!!) + val offer = extractCredentialIssuerData(query) + if (offer != null) { + provisioningViewModel.start( + walletServerProvider = application.walletServerProvider, + documentStore = application.documentStore, + settingsModel = application.settingsModel, + issuerIdentifier = null, + openid4VciCredentialOffer = offer + ) + routeRequest.value = WalletDestination.ProvisionDocument.route + } } } } diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/ProvisioningViewModel.kt b/wallet/src/main/java/com/android/identity_credential/wallet/ProvisioningViewModel.kt index 2c49e5eb6..6c6ed0444 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/ProvisioningViewModel.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/ProvisioningViewModel.kt @@ -19,15 +19,14 @@ import com.android.identity.issuance.RegistrationResponse import com.android.identity.issuance.evidence.EvidenceRequest import com.android.identity.issuance.evidence.EvidenceRequestIcaoNfcTunnel import com.android.identity.issuance.evidence.EvidenceRequestOpenid4Vp +import com.android.identity.issuance.evidence.EvidenceRequestPreauthorizedCode import com.android.identity.issuance.evidence.EvidenceResponse import com.android.identity.issuance.evidence.EvidenceResponseIcaoNfcTunnel +import com.android.identity.issuance.evidence.EvidenceResponsePreauthorizedCode import com.android.identity.issuance.remote.WalletServerProvider import com.android.identity.util.Logger import com.android.identity.util.fromBase64Url import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.datetime.Clock @@ -55,30 +54,7 @@ class ProvisioningViewModel : ViewModel() { private lateinit var issuer: IssuingAuthority - /* - Backing field + StateFlow that is observed on MainActivity's composable whose value is updated - from onNewIntent() when a OID4VCI Credential Offer deep link is intercepted in MainActivity. - */ - private val _newCredentialOfferIntentReceived = MutableStateFlow?>(null) - val newCredentialOfferIntentReceived: StateFlow?> = - _newCredentialOfferIntentReceived.asStateFlow() - - /** - * Trigger a recomposition of MainActivity from onNewIntent() after an OID4VCI deep link is - * intercepted. If both [credentialIssuerUri] and [credentialConfigurationId] are [null] then - * set observable value to [null] rather than Pair(null,null). - */ - fun onNewCredentialOfferIntent( - credentialIssuerUri: String?, - credentialConfigurationId: String? - ) { - if (credentialIssuerUri != null && credentialConfigurationId != null) { - _newCredentialOfferIntentReceived.value = - Pair(credentialIssuerUri, credentialConfigurationId) - } else { - _newCredentialOfferIntentReceived.value = null - } - } + var openid4VciCredentialOffer: Openid4VciCredentialOffer? = null fun reset() { state.value = State.IDLE @@ -110,18 +86,17 @@ class ProvisioningViewModel : ViewModel() { settingsModel: SettingsModel, // PID-based mdoc or sd-jwt issuerIdentifier: String?, - // OID4VCI credential offer - credentialIssuerUri: String? = null, - credentialIssuerConfigurationId: String? = null, + openid4VciCredentialOffer: Openid4VciCredentialOffer? = null, ) { this.documentStore = documentStore this.settingsModel = settingsModel + this.openid4VciCredentialOffer = openid4VciCredentialOffer viewModelScope.launch(Dispatchers.IO) { try { - if (credentialIssuerUri != null) { + if (openid4VciCredentialOffer != null) { issuer = walletServerProvider.createOpenid4VciIssuingAuthorityByUri( - credentialIssuerUri, - credentialIssuerConfigurationId!! + openid4VciCredentialOffer.issuerUri, + openid4VciCredentialOffer.configurationId ) } else { issuer = walletServerProvider.getIssuingAuthority(issuerIdentifier!!) @@ -156,6 +131,7 @@ class ProvisioningViewModel : ViewModel() { evidenceRequests = proofingFlow!!.getEvidenceRequests() currentEvidenceRequestIndex = 0 Logger.d(TAG, "ers0 ${evidenceRequests!!.size}") + if (evidenceRequests!!.size == 0) { state.value = State.PROOFING_COMPLETE document!!.let { @@ -204,14 +180,29 @@ class ProvisioningViewModel : ViewModel() { evidenceRequests = proofingFlow!!.getEvidenceRequests() currentEvidenceRequestIndex = 0 + Logger.d(TAG, "ers1 ${evidenceRequests!!.size}") - if (evidenceRequests!!.size == 0) { + if (evidenceRequests!!.isEmpty()) { state.value = State.PROOFING_COMPLETE document!!.refreshState(walletServerProvider) documentStore!!.addDocument(document!!) proofingFlow!!.complete() document!!.refreshState(walletServerProvider) } else { + if (evidenceRequests!![0] is EvidenceRequestPreauthorizedCode) { + if (openid4VciCredentialOffer?.preauthorizedCode != null) { + Logger.d(TAG, "handling pre-authorized code") + proofingFlow!!.sendEvidence( + EvidenceResponsePreauthorizedCode( + code = openid4VciCredentialOffer!!.preauthorizedCode!!, + txCode = null // We don't support it yet + ) + ) + evidenceRequests = proofingFlow!!.getEvidenceRequests() + } else { + currentEvidenceRequestIndex++ + } + } selectViableEvidenceRequest() state.value = State.EVIDENCE_REQUESTS_READY } @@ -364,4 +355,10 @@ class ProvisioningViewModel : ViewModel() { CredentialFormat.SD_JWT_VC -> documentConfiguration.sdJwtVcDocumentConfiguration != null } } + + data class Openid4VciCredentialOffer( + val issuerUri: String, + val configurationId: String, + val preauthorizedCode: String? + ) } \ No newline at end of file diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/WalletApplication.kt b/wallet/src/main/java/com/android/identity_credential/wallet/WalletApplication.kt index 10097c2b1..da33c9ae9 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/WalletApplication.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/WalletApplication.kt @@ -36,6 +36,7 @@ import com.android.identity.documenttype.DocumentTypeRepository import com.android.identity.documenttype.knowntypes.DrivingLicense import com.android.identity.documenttype.knowntypes.EUPersonalID import com.android.identity.crypto.X509Cert +import com.android.identity.documenttype.knowntypes.EUCertificateOfResidence import com.android.identity.documenttype.knowntypes.PhotoID import com.android.identity.issuance.DocumentExtensions.documentConfiguration import com.android.identity.issuance.WalletApplicationCapabilities @@ -131,6 +132,7 @@ class WalletApplication : Application() { documentTypeRepository.addDocumentType(DrivingLicense.getDocumentType()) documentTypeRepository.addDocumentType(EUPersonalID.getDocumentType()) documentTypeRepository.addDocumentType(PhotoID.getDocumentType()) + documentTypeRepository.addDocumentType(EUCertificateOfResidence.getDocumentType()) // init storage val storageFile = Path(applicationContext.noBackupFilesDir.path, "identity.bin") diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/credentialoffer/CredentialOfferIssuance.kt b/wallet/src/main/java/com/android/identity_credential/wallet/credentialoffer/CredentialOfferIssuance.kt index 3a02bdb0d..bf9c01341 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/credentialoffer/CredentialOfferIssuance.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/credentialoffer/CredentialOfferIssuance.kt @@ -1,50 +1,68 @@ package com.android.identity_credential.wallet.credentialoffer -import com.android.identity.document.DocumentStore -import com.android.identity.issuance.remote.WalletServerProvider +import com.android.identity.util.Logger import com.android.identity_credential.wallet.ProvisioningViewModel -import com.android.identity_credential.wallet.SettingsModel -import com.android.identity_credential.wallet.navigation.WalletDestination -import org.json.JSONObject - -/** - * Initiate the process of setting a dynamic Credential Issuer defined in the credential offer - * payload that produces [credentialIssuerUri] Url of Issuer for obtaining credentials and - * [credentialIssuerConfigurationId] for the Credential Id (such as "pid-mso-mdoc" or "pid-sd-jwt"). - */ -fun initiateCredentialOfferIssuance( - walletServerProvider: WalletServerProvider, - provisioningViewModel: ProvisioningViewModel, - settingsModel: SettingsModel, - documentStore: DocumentStore, - onNavigate: (String) -> Unit, - credentialIssuerUri: String, - credentialIssuerConfigurationId: String, -) { - provisioningViewModel.start( - walletServerProvider = walletServerProvider, - documentStore = documentStore, - settingsModel = settingsModel, - issuerIdentifier = null, - credentialIssuerUri = credentialIssuerUri, - credentialIssuerConfigurationId = credentialIssuerConfigurationId - ) - onNavigate(WalletDestination.ProvisionDocument.route) -} +import io.ktor.client.HttpClient +import io.ktor.client.request.get +import io.ktor.client.statement.readBytes +import io.ktor.http.HttpStatusCode +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import java.net.URLDecoder /** * Parse the Url Query component of an OID4VCI credential offer Url (from a deep link or Qr scan) * and return a [Pair] containing the Credential Issuer Uri and Credential (Config) Id that are * used for initiating the OID4VCI Credential Offer Issuance flow above [initiateCredentialOfferIssuance]. */ -fun extractCredentialIssuerData(urlQueryComponent: String): Pair { - val jsonPayload = JSONObject("{$urlQueryComponent}") - val credentialOffer = jsonPayload.getJSONObject("credential_offer") - // extract Credential Issuer Uri and Credential (Config) Id - val credentialIssuerUri = credentialOffer.getString("credential_issuer") - val credentialConfigurationIds = - credentialOffer.getJSONArray("credential_configuration_ids") - // TODO should there be a future implementation addressing all specified ids in credential_configuration_ids ? - val credentialConfigurationId = credentialConfigurationIds.getString(0) - return Pair(credentialIssuerUri, credentialConfigurationId) +suspend fun extractCredentialIssuerData( + urlQueryComponent: String +): ProvisioningViewModel.Openid4VciCredentialOffer? { + try { + val params = urlQueryComponent.split('&').map { + val index = it.indexOf('=') + val name = if (index < 0) "" else it.substring(0, index) + Pair( + URLDecoder.decode(name, "UTF-8"), + URLDecoder.decode(it.substring(index + 1), "UTF-8") + ) + }.toMap() + var credentialOfferString = params["credential_offer"] + if (credentialOfferString == null) { + val url = params["credential_offer_uri"] + if (url == null) { + Logger.e("CredentialOffer", "Could not parse offer") + return null + } + val response = HttpClient().get(url) {} + if (response.status != HttpStatusCode.OK) { + Logger.e("CredentialOffer", "Error retrieving '$url'") + return null + } + credentialOfferString = String(response.readBytes()) + } + val json = Json.parseToJsonElement(credentialOfferString).jsonObject + // extract Credential Issuer Uri and Credential (Config) Id + val credentialIssuerUri = json["credential_issuer"]!!.jsonPrimitive.content + val credentialConfigurationIds = json["credential_configuration_ids"]!!.jsonArray + // Right now only use the first configuration id + val credentialConfigurationId = credentialConfigurationIds[0].jsonPrimitive.content + var preauthorizedCode: String? = null + if (json.containsKey("grants")) { + val codeObject = json["grants"]!!.jsonObject["urn:ietf:params:oauth:grant-type:pre-authorized_code"] + if (codeObject != null) { + preauthorizedCode = codeObject.jsonObject["pre-authorized_code"]?.jsonPrimitive?.content + } + } + return ProvisioningViewModel.Openid4VciCredentialOffer( + credentialIssuerUri, + credentialConfigurationId, + preauthorizedCode + ) + } catch (err: Exception) { + Logger.e("CredentialOffer", "Parsing error", err) + return null + } } \ No newline at end of file diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt index a3e81266a..68a77b9a4 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt @@ -39,11 +39,13 @@ import com.android.identity_credential.wallet.R import com.android.identity_credential.wallet.SettingsModel import com.android.identity_credential.wallet.WalletApplication import com.android.identity_credential.wallet.credentialoffer.extractCredentialIssuerData -import com.android.identity_credential.wallet.credentialoffer.initiateCredentialOfferIssuance import com.android.identity_credential.wallet.navigation.WalletDestination import com.android.identity_credential.wallet.ui.ScreenWithAppBarAndBackButton import com.android.identity_credential.wallet.ui.qrscanner.ScanQrDialog import com.android.identity_credential.wallet.util.getUrlQueryFromCustomSchemeUrl +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch private const val TAG = "AddToWalletScreen" @@ -135,24 +137,21 @@ fun AddToWalletScreen( // filter only for OID4VCI Url schemes. if (qrCodeTextUrl.startsWith(WalletApplication.OID4VCI_CREDENTIAL_OFFER_URL_SCHEME)) { // scanned text is expected to be an encoded Url - val decodedQuery = getUrlQueryFromCustomSchemeUrl(qrCodeTextUrl) - // extract Credential Issuer Uri (issuing authority path) and credential id (pid-mso-mdoc, pid-sd-jwt) - extractCredentialIssuerData(decodedQuery).let { (credentialIssuerUri, credentialConfigurationId) -> - // initiate getting issuing authority dynamically from specified Issuer Uri and Credential Id - initiateCredentialOfferIssuance( - walletServerProvider = walletServerProvider, - provisioningViewModel = provisioningViewModel, - settingsModel = settingsModel, - documentStore = documentStore, - onNavigate = { route -> - // hoist the actual navigation on the parent composable - // because calling onNavigate.invoke(route) does not - // trigger a recomposition - navigateToOnComposable.value = route - }, - credentialIssuerUri = credentialIssuerUri, - credentialIssuerConfigurationId = credentialConfigurationId, - ) + CoroutineScope(Dispatchers.Main).launch { + val query = getUrlQueryFromCustomSchemeUrl(qrCodeTextUrl) + // extract Credential Issuer Uri (issuing authority path) and credential id (pid-mso-mdoc, pid-sd-jwt) + val offer = extractCredentialIssuerData(query) + if (offer != null) { + // initiate getting issuing authority dynamically from specified Issuer Uri and Credential Id + provisioningViewModel.start( + walletServerProvider = walletServerProvider, + settingsModel = settingsModel, + documentStore = documentStore, + issuerIdentifier = null, + openid4VciCredentialOffer = offer, + ) + onNavigate(WalletDestination.ProvisionDocument.route) + } } } }, diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/provisioncredential/ProvisionCredentialScreen.kt b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/provisioncredential/ProvisionCredentialScreen.kt index c7c976627..86e5732c8 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/provisioncredential/ProvisionCredentialScreen.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/provisioncredential/ProvisionCredentialScreen.kt @@ -15,9 +15,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -40,6 +38,7 @@ import com.android.identity.issuance.evidence.EvidenceRequestIcaoPassiveAuthenti import com.android.identity.issuance.evidence.EvidenceRequestMessage import com.android.identity.issuance.evidence.EvidenceRequestNotificationPermission import com.android.identity.issuance.evidence.EvidenceRequestOpenid4Vp +import com.android.identity.issuance.evidence.EvidenceRequestPreauthorizedCode import com.android.identity.issuance.evidence.EvidenceRequestQuestionMultipleChoice import com.android.identity.issuance.evidence.EvidenceRequestQuestionString import com.android.identity.issuance.evidence.EvidenceRequestSelfieVideo @@ -56,6 +55,7 @@ import com.android.identity.mdoc.response.DeviceResponseGenerator import com.android.identity.securearea.SecureAreaRepository import com.android.identity.trustmanagement.TrustPoint import com.android.identity.util.Constants +import com.android.identity.util.Logger import com.android.identity.util.fromBase64Url import com.android.identity.util.toBase64Url import com.android.identity_credential.wallet.PermissionTracker @@ -82,6 +82,7 @@ import org.json.JSONObject import java.util.StringTokenizer import kotlin.random.Random +private const val TAG = "ProvisionCredentialScreen" @Composable fun ProvisionDocumentScreen( @@ -272,6 +273,22 @@ fun ProvisionDocumentScreen( application = application ) } + + is EvidenceRequestPreauthorizedCode -> { + // should have been processed by the model internally + Logger.e(TAG, "Unexpected evidence request type: EvidenceRequestPreauthorizedCode") + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + Text( + modifier = Modifier.padding(8.dp), + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center, + text = stringResource(R.string.provisioning_request_unexpected) + ) + } + } } } diff --git a/wallet/src/main/res/drawable/card_art_generic.png b/wallet/src/main/res/drawable/card_art_generic.png new file mode 100644 index 0000000000000000000000000000000000000000..64b670a66a85b36aa643ef6b154cf865c3dcd46a GIT binary patch literal 51647 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%0A6blC;3KwljR6VtKmGozfb+8G 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 evidence… Error provisioning: %1$s Unexpected state: %1$s + Unexpected request type Reused Credential @@ -499,6 +500,14 @@ this device.

]]> + + Provisioning $ID_NAME credential +

In the following screens, information will be collected for provisioning of $ID_NAME + from $ISSUER_NAME.

+

The created $ID_NAME will be bound to this device.

+ ]]>
+ Scan Credential Offer QR code for OpenID for Verifiable Credential Issuance (draft #14)