Skip to content

Commit abb92cb

Browse files
authored
Merge branch 'eu-digital-identity-wallet:main' into feat/add_transaction_data_to_request_screen
2 parents b88b598 + a473f63 commit abb92cb

File tree

14 files changed

+379
-197
lines changed

14 files changed

+379
-197
lines changed

core-logic/src/demo/java/eu/europa/ec/corelogic/config/WalletCoreConfigImpl.kt

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,14 @@ internal class WalletCoreConfigImpl(
2828
private val context: Context
2929
) : WalletCoreConfig {
3030

31-
private companion object {
32-
const val VCI_ISSUER_URL = "https://issuer.eudiw.dev"
33-
const val VCI_CLIENT_ID = "wallet-dev"
34-
const val AUTHENTICATION_REQUIRED = false
35-
}
36-
3731
private var _config: EudiWalletConfig? = null
3832

3933
override val config: EudiWalletConfig
4034
get() {
4135
if (_config == null) {
4236
_config = EudiWalletConfig {
4337
configureDocumentKeyCreation(
44-
userAuthenticationRequired = AUTHENTICATION_REQUIRED,
38+
userAuthenticationRequired = false,
4539
userAuthenticationTimeout = 30_000L,
4640
useStrongBoxForKeys = true
4741
)
@@ -64,14 +58,6 @@ internal class WalletCoreConfigImpl(
6458
)
6559
}
6660

67-
configureOpenId4Vci {
68-
withIssuerUrl(issuerUrl = VCI_ISSUER_URL)
69-
withClientId(clientId = VCI_CLIENT_ID)
70-
withAuthFlowRedirectionURI(BuildConfig.ISSUE_AUTHORIZATION_DEEPLINK)
71-
withParUsage(OpenId4VciManager.Config.ParUsage.IF_SUPPORTED)
72-
withUseDPoPIfSupported(true)
73-
}
74-
7561
configureReaderTrustStore(
7662
context,
7763
R.raw.pidissuerca02_cz,
@@ -87,4 +73,22 @@ internal class WalletCoreConfigImpl(
8773
}
8874
return _config!!
8975
}
76+
77+
override val vciConfig: List<OpenId4VciManager.Config>
78+
get() = listOf(
79+
OpenId4VciManager.Config.Builder()
80+
.withIssuerUrl(issuerUrl = "https://issuer.eudiw.dev")
81+
.withClientId(clientId = "wallet-dev")
82+
.withAuthFlowRedirectionURI(BuildConfig.ISSUE_AUTHORIZATION_DEEPLINK)
83+
.withParUsage(OpenId4VciManager.Config.ParUsage.IF_SUPPORTED)
84+
.withUseDPoPIfSupported(true)
85+
.build(),
86+
OpenId4VciManager.Config.Builder()
87+
.withIssuerUrl(issuerUrl = "https://issuer-backend.eudiw.dev")
88+
.withClientId(clientId = "wallet-dev")
89+
.withAuthFlowRedirectionURI(BuildConfig.ISSUE_AUTHORIZATION_DEEPLINK)
90+
.withParUsage(OpenId4VciManager.Config.ParUsage.IF_SUPPORTED)
91+
.withUseDPoPIfSupported(true)
92+
.build()
93+
)
9094
}

core-logic/src/dev/java/eu/europa/ec/corelogic/config/WalletCoreConfigImpl.kt

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,14 @@ internal class WalletCoreConfigImpl(
2828
private val context: Context
2929
) : WalletCoreConfig {
3030

31-
private companion object {
32-
const val VCI_ISSUER_URL = "https://dev.issuer.eudiw.dev"
33-
const val VCI_CLIENT_ID = "wallet-dev"
34-
const val AUTHENTICATION_REQUIRED = false
35-
}
36-
3731
private var _config: EudiWalletConfig? = null
3832

3933
override val config: EudiWalletConfig
4034
get() {
4135
if (_config == null) {
4236
_config = EudiWalletConfig {
4337
configureDocumentKeyCreation(
44-
userAuthenticationRequired = AUTHENTICATION_REQUIRED,
38+
userAuthenticationRequired = false,
4539
userAuthenticationTimeout = 30_000L,
4640
useStrongBoxForKeys = true
4741
)
@@ -64,14 +58,6 @@ internal class WalletCoreConfigImpl(
6458
)
6559
}
6660

67-
configureOpenId4Vci {
68-
withIssuerUrl(issuerUrl = VCI_ISSUER_URL)
69-
withClientId(clientId = VCI_CLIENT_ID)
70-
withAuthFlowRedirectionURI(BuildConfig.ISSUE_AUTHORIZATION_DEEPLINK)
71-
withParUsage(OpenId4VciManager.Config.ParUsage.IF_SUPPORTED)
72-
withUseDPoPIfSupported(true)
73-
}
74-
7561
configureReaderTrustStore(
7662
context,
7763
R.raw.pidissuerca02_cz,
@@ -87,4 +73,22 @@ internal class WalletCoreConfigImpl(
8773
}
8874
return _config!!
8975
}
76+
77+
override val vciConfig: List<OpenId4VciManager.Config>
78+
get() = listOf(
79+
OpenId4VciManager.Config.Builder()
80+
.withIssuerUrl(issuerUrl = "https://dev.issuer.eudiw.dev")
81+
.withClientId(clientId = "wallet-dev")
82+
.withAuthFlowRedirectionURI(BuildConfig.ISSUE_AUTHORIZATION_DEEPLINK)
83+
.withParUsage(OpenId4VciManager.Config.ParUsage.IF_SUPPORTED)
84+
.withUseDPoPIfSupported(true)
85+
.build(),
86+
OpenId4VciManager.Config.Builder()
87+
.withIssuerUrl(issuerUrl = "https://dev.issuer-backend.eudiw.dev")
88+
.withClientId(clientId = "wallet-dev")
89+
.withAuthFlowRedirectionURI(BuildConfig.ISSUE_AUTHORIZATION_DEEPLINK)
90+
.withParUsage(OpenId4VciManager.Config.ParUsage.IF_SUPPORTED)
91+
.withUseDPoPIfSupported(true)
92+
.build()
93+
)
9094
}

core-logic/src/main/java/eu/europa/ec/corelogic/config/WalletCoreConfig.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import eu.europa.ec.corelogic.model.DocumentCategory
2121
import eu.europa.ec.corelogic.model.DocumentIdentifier
2222
import eu.europa.ec.eudi.wallet.EudiWalletConfig
2323
import eu.europa.ec.eudi.wallet.document.CreateDocumentSettings.CredentialPolicy
24+
import eu.europa.ec.eudi.wallet.issue.openid4vci.OpenId4VciManager
2425
import java.time.Duration
2526

2627
interface WalletCoreConfig {
@@ -33,6 +34,11 @@ interface WalletCoreConfig {
3334
*/
3435
val config: EudiWalletConfig
3536

37+
/**
38+
* List of Configurations for Verifiable Credentials Issuance (VCI).
39+
*/
40+
val vciConfig: List<OpenId4VciManager.Config>
41+
3642
/**
3743
* Returns a predefined set of document categories and their associated identifiers.
3844
*

core-logic/src/main/java/eu/europa/ec/corelogic/controller/WalletCoreDocumentsController.kt

Lines changed: 101 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import eu.europa.ec.corelogic.model.FormatType
3232
import eu.europa.ec.corelogic.model.ScopedDocumentDomain
3333
import eu.europa.ec.corelogic.model.TransactionLogDataDomain
3434
import eu.europa.ec.corelogic.model.toDocumentIdentifier
35+
import eu.europa.ec.eudi.openid4vci.CredentialIssuerMetadata
3536
import eu.europa.ec.eudi.openid4vci.MsoMdocCredential
3637
import eu.europa.ec.eudi.openid4vci.SdJwtVcCredential
3738
import eu.europa.ec.eudi.statium.Status
@@ -163,6 +164,7 @@ interface WalletCoreDocumentsController {
163164
fun issueDocument(
164165
issuanceMethod: IssuanceMethod,
165166
configId: String,
167+
issuerId: String
166168
): Flow<IssueDocumentPartialState>
167169

168170
fun issueDocumentsByOfferUri(
@@ -221,8 +223,10 @@ class WalletCoreDocumentsControllerImpl(
221223
private val documentErrorMessage
222224
get() = resourceProvider.getString(R.string.issuance_generic_error)
223225

224-
private val openId4VciManager by lazy {
225-
eudiWallet.createOpenId4VciManager()
226+
private val openId4VciManagers by lazy {
227+
walletCoreConfig.vciConfig.associate { config ->
228+
config.issuerUrl to eudiWallet.createOpenId4VciManager(config = config)
229+
}
226230
}
227231

228232
override fun getAllDocuments(): List<Document> =
@@ -234,35 +238,43 @@ class WalletCoreDocumentsControllerImpl(
234238
override suspend fun getScopedDocuments(locale: Locale): FetchScopedDocumentsPartialState {
235239
return withContext(dispatcher) {
236240
runCatching {
237-
val metadata = openId4VciManager.getIssuerMetadata().getOrThrow()
238241

239-
val documents =
240-
metadata.credentialConfigurationsSupported.map { (id, config) ->
242+
val metadata: Map<String, CredentialIssuerMetadata> =
243+
openId4VciManagers.mapValues { (_, manager) ->
244+
manager.getIssuerMetadata().getOrThrow()
245+
}
241246

242-
val name: String = config.display.getLocalizedDisplayName(
243-
userLocale = locale,
244-
fallback = id.value
245-
)
247+
val documents: List<ScopedDocumentDomain> =
248+
metadata.flatMap { (issuer, meta) ->
249+
meta.credentialConfigurationsSupported.map { (id, config) ->
246250

247-
val isPid: Boolean = when (config) {
248-
is MsoMdocCredential -> config.docType.toDocumentIdentifier() == DocumentIdentifier.MdocPid
249-
is SdJwtVcCredential -> config.type.toDocumentIdentifier() == DocumentIdentifier.SdJwtPid
250-
else -> false
251-
}
251+
val name = config.display.getLocalizedDisplayName(
252+
userLocale = locale,
253+
fallback = id.value
254+
)
252255

253-
val formatType = when (config) {
254-
is MsoMdocCredential -> config.docType
255-
is SdJwtVcCredential -> config.type
256-
else -> null
257-
}
256+
val isPid = when (config) {
257+
is MsoMdocCredential -> config.docType.toDocumentIdentifier() == DocumentIdentifier.MdocPid
258+
is SdJwtVcCredential -> config.type.toDocumentIdentifier() == DocumentIdentifier.SdJwtPid
259+
else -> false
260+
}
258261

259-
ScopedDocumentDomain(
260-
name = name,
261-
configurationId = id.value,
262-
formatType = formatType,
263-
isPid = isPid
264-
)
262+
val formatType = when (config) {
263+
is MsoMdocCredential -> config.docType
264+
is SdJwtVcCredential -> config.type
265+
else -> null
266+
}
267+
268+
ScopedDocumentDomain(
269+
name = name,
270+
configurationId = id.value,
271+
credentialIssuerId = issuer,
272+
formatType = formatType,
273+
isPid = isPid
274+
)
275+
}
265276
}
277+
266278
if (documents.isNotEmpty()) {
267279
FetchScopedDocumentsPartialState.Success(documents = documents)
268280
} else {
@@ -306,11 +318,11 @@ class WalletCoreDocumentsControllerImpl(
306318
override fun issueDocument(
307319
issuanceMethod: IssuanceMethod,
308320
configId: String,
321+
issuerId: String
309322
): Flow<IssueDocumentPartialState> = flow {
310323
when (issuanceMethod) {
311-
312324
IssuanceMethod.OPENID4VCI -> {
313-
issueDocumentWithOpenId4VCI(configId).collect { response ->
325+
issueDocumentWithOpenId4VCI(configId, issuerId).collect { response ->
314326
when (response) {
315327
is IssueDocumentsPartialState.Failure -> emit(
316328
IssueDocumentPartialState.Failure(
@@ -355,11 +367,30 @@ class WalletCoreDocumentsControllerImpl(
355367
txCode: String?,
356368
): Flow<IssueDocumentsPartialState> =
357369
callbackFlow {
358-
openId4VciManager.issueDocumentByOfferUri(
359-
offerUri = offerUri,
360-
onIssueEvent = issuanceCallback(),
361-
txCode = txCode,
362-
)
370+
resolveDocumentOffer(offerUri).collect {
371+
when (it) {
372+
is ResolveDocumentOfferPartialState.Failure -> throw Throwable(it.errorMessage)
373+
is ResolveDocumentOfferPartialState.Success -> {
374+
375+
val issuerId = it
376+
.offer
377+
.credentialOffer
378+
.credentialIssuerIdentifier
379+
.toString()
380+
381+
val manager = openId4VciManagers[issuerId]
382+
?: openId4VciManagers.values.firstOrNull()
383+
384+
require(manager != null) { documentErrorMessage }
385+
386+
manager.issueDocumentByOffer(
387+
offer = it.offer,
388+
onIssueEvent = issuanceCallback(),
389+
txCode = txCode,
390+
)
391+
}
392+
}
393+
}
363394
awaitClose()
364395
}.safeAsync {
365396
IssueDocumentsPartialState.Failure(
@@ -452,30 +483,25 @@ class WalletCoreDocumentsControllerImpl(
452483

453484
override fun resolveDocumentOffer(offerUri: String): Flow<ResolveDocumentOfferPartialState> =
454485
callbackFlow {
455-
openId4VciManager.resolveDocumentOffer(
456-
offerUri = offerUri,
457-
onResolvedOffer = { offerResult ->
458-
when (offerResult) {
459-
is OfferResult.Failure -> {
460-
trySendBlocking(
461-
ResolveDocumentOfferPartialState.Failure(
462-
errorMessage = offerResult.cause.localizedMessage
463-
?: genericErrorMessage
464-
)
465-
)
466-
}
467486

468-
is OfferResult.Success -> {
469-
trySendBlocking(
470-
ResolveDocumentOfferPartialState.Success(
471-
offer = offerResult.offer
472-
)
487+
val manager = openId4VciManagers.values.firstOrNull()
488+
require(manager != null) { genericErrorMessage }
489+
490+
manager.resolveDocumentOffer(offerUri) { result ->
491+
when (result) {
492+
is OfferResult.Failure -> throw Throwable(
493+
result.cause.localizedMessage ?: genericErrorMessage
494+
)
495+
496+
is OfferResult.Success -> {
497+
trySendBlocking(
498+
ResolveDocumentOfferPartialState.Success(
499+
offer = result.offer
473500
)
474-
}
501+
)
475502
}
476503
}
477-
)
478-
504+
}
479505
awaitClose()
480506
}.safeAsync {
481507
ResolveDocumentOfferPartialState.Failure(
@@ -486,7 +512,14 @@ class WalletCoreDocumentsControllerImpl(
486512
override fun issueDeferredDocument(docId: DocumentId): Flow<IssueDeferredDocumentPartialState> =
487513
callbackFlow {
488514
(getDocumentById(docId) as? DeferredDocument)?.let { deferredDoc ->
489-
openId4VciManager.issueDeferredDocument(
515+
516+
val manager = deferredDoc.issuerMetadata?.credentialIssuerIdentifier
517+
?.let(openId4VciManagers::get)
518+
?: openId4VciManagers.values.firstOrNull()
519+
520+
require(manager != null) { documentErrorMessage }
521+
522+
manager.issueDeferredDocument(
490523
deferredDocument = deferredDoc,
491524
executor = null,
492525
onIssueResult = { deferredIssuanceResult ->
@@ -551,7 +584,13 @@ class WalletCoreDocumentsControllerImpl(
551584
}
552585

553586
override fun resumeOpenId4VciWithAuthorization(uri: String) {
554-
openId4VciManager.resumeWithAuthorization(uri)
587+
for (manager in openId4VciManagers.values) {
588+
try {
589+
manager.resumeWithAuthorization(uri)
590+
break
591+
} catch (_: Exception) {
592+
}
593+
}
555594
}
556595

557596
override fun getAllDocumentCategories(): DocumentCategories {
@@ -602,10 +641,16 @@ class WalletCoreDocumentsControllerImpl(
602641
override suspend fun resolveDocumentStatus(document: IssuedDocument): Result<Status> =
603642
eudiWallet.resolveStatus(document)
604643

605-
private fun issueDocumentWithOpenId4VCI(configId: String): Flow<IssueDocumentsPartialState> =
644+
private fun issueDocumentWithOpenId4VCI(
645+
configId: String,
646+
issuerId: String
647+
): Flow<IssueDocumentsPartialState> =
606648
callbackFlow {
607649

608-
openId4VciManager.issueDocumentByConfigurationIdentifier(
650+
val manager = openId4VciManagers[issuerId]
651+
require(manager != null) { documentErrorMessage }
652+
653+
manager.issueDocumentByConfigurationIdentifier(
609654
credentialConfigurationId = configId,
610655
onIssueEvent = issuanceCallback()
611656
)

core-logic/src/main/java/eu/europa/ec/corelogic/model/ScopedDocumentDomain.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package eu.europa.ec.corelogic.model
1919
data class ScopedDocumentDomain(
2020
val name: String,
2121
val configurationId: String,
22+
val credentialIssuerId: String,
2223
val formatType: FormatType?,
2324
val isPid: Boolean
2425
)

0 commit comments

Comments
 (0)