Skip to content

Commit ef10492

Browse files
committed
DeferredDocuments ReIssuance
1 parent 4b169e2 commit ef10492

7 files changed

Lines changed: 285 additions & 572 deletions

File tree

wallet-core/src/main/java/eu/europa/ec/eudi/wallet/EudiWallet.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -373,14 +373,14 @@ interface EudiWallet : SampleDocumentManager, PresentationManager, DocumentStatu
373373
val documentManagerToUse =
374374
(documentManager ?: getDefaultDocumentManager(storage, secureAreas))
375375
.let { manager ->
376-
// Wrap with reissuance metadata cleanup (if OpenId4VCI is configured)
377-
if (config.openId4VciConfig != null) {
378-
DocumentManagerWithReissuance(
379-
delegate = manager,
380-
reissuanceStorage = reissuanceStorage,
381-
logger = loggerToUse
382-
)
383-
} else manager
376+
// Always wrap with reissuance metadata cleanup.
377+
// OpenId4VciManager may be created later via createOpenId4VciManager(),
378+
// so we cannot gate this on config.openId4VciConfig being set at build time.
379+
DocumentManagerWithReissuance(
380+
delegate = manager,
381+
reissuanceStorage = reissuanceStorage,
382+
logger = loggerToUse
383+
)
384384
}
385385
.let { manager ->
386386
if (config.dcapiConfig?.enabled == true) {

wallet-core/src/main/java/eu/europa/ec/eudi/wallet/issue/openid4vci/DefaultOpenId4VciManager.kt

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import eu.europa.ec.eudi.wallet.internal.wrappedWithContentNegotiation
4040
import eu.europa.ec.eudi.wallet.internal.wrappedWithLogging
4141
import eu.europa.ec.eudi.wallet.issue.openid4vci.IssueEvent.Companion.failure
4242
import eu.europa.ec.eudi.wallet.issue.openid4vci.OpenId4VciManager.Companion.TAG
43+
import eu.europa.ec.eudi.wallet.issue.openid4vci.dpop.DPopConfig
4344
import eu.europa.ec.eudi.wallet.issue.openid4vci.reissue.ReissuanceConfig
4445
import eu.europa.ec.eudi.wallet.issue.openid4vci.reissue.ReissuanceIssuer
4546
import eu.europa.ec.eudi.wallet.logging.Logger
@@ -231,9 +232,18 @@ internal class DefaultOpenId4VciManager(
231232
launch(executor, onIssueResult) { coroutineScope, callback ->
232233
try {
233234

235+
// Resolve DPopConfig so DeferredContext can recreate the DPoP signer
236+
val resolvedDpopConfig = when (val cfg = config.dpopConfig) {
237+
DPopConfig.Disabled -> null
238+
DPopConfig.Default -> DPopConfig.Default.make(context)
239+
is DPopConfig.Custom -> cfg
240+
}
241+
234242
val deferredContext = DeferredContext.fromBytes(
235243
deferredDocument.relatedData,
236-
walletAttestationKeyManager
244+
walletAttestationKeyManager,
245+
dpopConfig = resolvedDpopConfig,
246+
logger = logger,
237247
)
238248
val clientAttestationPopKeyId = deferredContext.clientAttestationPopKeyId
239249
when {
@@ -250,17 +260,20 @@ internal class DefaultOpenId4VciManager(
250260

251261
ProcessDeferredOutcome(
252262
documentManager = documentManager,
253-
walletKeyManager = walletAttestationKeyManager,
254-
clientAttestationPopKeyId = clientAttestationPopKeyId,
255263
callback = callback,
256264
deferredContext = ctx?.let {
257265
DeferredContext(
258266
issuanceContext = it,
259267
keyAliases = deferredContext.keyAliases,
260-
clientAttestationPopKeyId = clientAttestationPopKeyId
268+
clientAttestationPopKeyId = clientAttestationPopKeyId,
269+
dPoPKeyAlias = deferredContext.dPoPKeyAlias,
270+
credentialConfigurationIdentifier = deferredContext.credentialConfigurationIdentifier,
271+
credentialEndpoint = deferredContext.credentialEndpoint,
272+
replacesDocumentId = deferredContext.replacesDocumentId,
261273
)
262274
} ?: deferredContext,
263-
logger = logger
275+
logger = logger,
276+
reissuanceStorage = reissuanceStorage,
264277
).process(deferredDocument, deferredContext.keyAliases, outcome)
265278
}
266279
}
@@ -354,33 +367,34 @@ internal class DefaultOpenId4VciManager(
354367

355368
// Process the response: store new document, then delete old one
356369
val issuedDocumentIds = mutableListOf<DocumentId>()
370+
val deferredDocumentIds = mutableListOf<DocumentId>()
357371
ProcessResponse(
358372
documentManager = documentManager,
359-
deferredContextFactory = DeferredContextFactory(issuer, authorizedRequest),
360-
walletKeyManager = walletAttestationKeyManager,
373+
deferredContextFactory = DeferredContextFactory(issuer, authorizedRequest, issuerCreator.dpopKeyAlias),
361374
clientAttestationPopKeyId = issuerCreator.clientAttestationPopKeyId,
362375
listener = listener,
363376
issuedDocumentIds = issuedDocumentIds,
377+
deferredDocumentIds = deferredDocumentIds,
364378
logger = logger,
365379
authorizedRequest = authorizedRequest,
366380
issuer = issuer,
367381
documentToConfigurationMap = requestMap,
368382
dpopKeyAlias = issuerCreator.dpopKeyAlias,
369383
reissuanceStorage = reissuanceStorage,
370384
clientAuthentication = issuerCreator.clientAuthentication,
385+
replacesDocumentId = documentId,
371386
).process(response)
372387

373-
// If new document(s) issued successfully, delete the old document
374-
// and migrate the re-issuance metadata
388+
// If new document(s) issued successfully, delete the old document.
389+
// If the outcome was deferred, the old document stays alive — the
390+
// replacesDocumentId stored in the deferred context will trigger
391+
// deletion when issueDeferredDocument() eventually succeeds.
375392
if (issuedDocumentIds.isNotEmpty()) {
376-
// Delete the old document — the DocumentManagerWithReissuance wrapper
377-
// automatically cleans up re-issuance metadata for the old document.
378-
// New metadata was already stored by ProcessResponse.storeReissuanceMetadata.
379393
documentManager.deleteDocumentById(documentId)
380394
logger?.d(TAG, "Deleted old document $documentId after re-issuance")
381395
}
382396

383-
listener(IssueEvent.Finished(issuedDocumentIds))
397+
listener(IssueEvent.Finished(issuedDocumentIds + deferredDocumentIds))
384398

385399
} catch (e: Throwable) {
386400
logger?.e(TAG, "Something went wrong with reissuance of $documentId", e)
@@ -440,6 +454,7 @@ internal class DefaultOpenId4VciManager(
440454
var authorizedRequest = issuerAuthorization.authorize(issuer, txCode)
441455
listener(IssueEvent.Started(offer.offeredDocuments.size))
442456
val issuedDocumentIds = mutableListOf<DocumentId>()
457+
val deferredDocumentIds = mutableListOf<DocumentId>()
443458
val documentCreator = DocumentCreator(
444459
documentManager = documentManager,
445460
listener = listener,
@@ -453,11 +468,11 @@ internal class DefaultOpenId4VciManager(
453468
}
454469
ProcessResponse(
455470
documentManager = documentManager,
456-
deferredContextFactory = DeferredContextFactory(issuer, authorizedRequest),
457-
walletKeyManager = walletAttestationKeyManager,
471+
deferredContextFactory = DeferredContextFactory(issuer, authorizedRequest, issuerCreator.dpopKeyAlias),
458472
clientAttestationPopKeyId = issuerCreator.clientAttestationPopKeyId,
459473
listener = listener,
460474
issuedDocumentIds = issuedDocumentIds,
475+
deferredDocumentIds = deferredDocumentIds,
461476
logger = logger,
462477
authorizedRequest = authorizedRequest,
463478
issuer = issuer,
@@ -466,7 +481,7 @@ internal class DefaultOpenId4VciManager(
466481
reissuanceStorage = reissuanceStorage,
467482
clientAuthentication = issuerCreator.clientAuthentication,
468483
).process(response)
469-
listener(IssueEvent.Finished(issuedDocumentIds))
484+
listener(IssueEvent.Finished(issuedDocumentIds + deferredDocumentIds))
470485
}
471486

472487
/**

0 commit comments

Comments
 (0)