@@ -176,10 +176,10 @@ public actor OpenId4VCIService {
176176 return CredentialOptions ( credentialPolicy: . rotateUse, batchSize: metaData. batchCredentialIssuance? . batchSize ?? 1 )
177177 }
178178
179- func getIssuer( offer: CredentialOffer ) async throws -> Issuer {
179+ func getIssuer( offer: CredentialOffer , dpopKeyId : String ? = nil ) async throws -> Issuer {
180180 var dpopConstructor : DPoPConstructorType ? = nil
181181 if config. useDpopIfSupported {
182- dpopConstructor = try await config. makePoPConstructor ( popUsage: . dpop, privateKeyId: issueReq. dpopKeyId, algorithms: offer. authorizationServerMetadata. dpopSigningAlgValuesSupported, keyOptions: config. dpopKeyOptions)
182+ dpopConstructor = try await config. makePoPConstructor ( popUsage: . dpop, privateKeyId: dpopKeyId ?? issueReq. dpopKeyId, algorithms: offer. authorizationServerMetadata. dpopSigningAlgValuesSupported, keyOptions: config. dpopKeyOptions)
183183 }
184184 let vciConfig = try await config. toOpenId4VCIConfig ( credentialIssuerId: offer. credentialIssuerIdentifier. url. absoluteString, clientAttestationPopSigningAlgValuesSupported: offer. authorizationServerMetadata. clientAttestationPopSigningAlgValuesSupported)
185185 return try Issuer ( authorizationServerMetadata: offer. authorizationServerMetadata, issuerMetadata: offer. credentialIssuerMetadata, config: vciConfig, parPoster: Poster ( session: networking) , tokenPoster: Poster ( session: networking) , requesterPoster: Poster ( session: networking) , deferredRequesterPoster: Poster ( session: networking) , notificationPoster: Poster ( session: networking) , noncePoster: Poster ( session: networking) , dpopConstructor: dpopConstructor)
@@ -200,7 +200,7 @@ public actor OpenId4VCIService {
200200 return try Issuer . createDeferredIssuer ( deferredCredentialEndpoint: data. deferredCredentialEndpoint, deferredRequesterPoster: Poster ( session: networking) , config: vciConfig)
201201 }
202202
203- func authorizeOffer( offerUri: String , docTypeModels: [ OfferedDocModel ] , txCodeValue: String ? , authorized: AuthorizedRequest ? ) async throws -> ( AuthorizeRequestOutcome , Issuer , [ CredentialConfiguration ] ) {
203+ func authorizeOffer( offerUri: String , docTypeModels: [ OfferedDocModel ] , txCodeValue: String ? , authorized: AuthorizedRequest ? , backgroundOnly : Bool = false , dpopKeyId : String ? = nil ) async throws -> ( AuthorizeRequestOutcome , Issuer , [ CredentialConfiguration ] ) {
204204 guard let offer = Self . credentialOfferCache [ offerUri] else {
205205 throw PresentationSession . makeError ( str: " offerUri \( offerUri) not resolved. resolveOfferDocTypes must be called first " )
206206 }
@@ -211,7 +211,7 @@ public actor OpenId4VCIService {
211211 let code : Grants . PreAuthorizedCode ? = switch offer. grants { case . preAuthorizedCode( let preAuthorizedCode) : preAuthorizedCode; case . both( _, let preAuthorizedCode) : preAuthorizedCode; case . authorizationCode( _) , . none: nil }
212212 let txCodeSpec : TxCode ? = code? . txCode
213213 let preAuthorizedCode : String ? = code? . preAuthorizedCode
214- let issuer = try await getIssuer ( offer: offer)
214+ let issuer = try await getIssuer ( offer: offer, dpopKeyId : dpopKeyId )
215215 if preAuthorizedCode != nil && txCodeSpec != nil && txCodeValue == nil {
216216 throw PresentationSession . makeError ( str: " A transaction code is required for this offer " )
217217 }
@@ -227,6 +227,9 @@ public actor OpenId4VCIService {
227227 }
228228 catch {
229229 logger. error ( " Failed to refresh provided authorized request: \( error) . " )
230+ if backgroundOnly {
231+ throw PresentationSession . makeError ( str: " Background reissuance not possible. " , localizationKey: " background_reissue_not_possible " )
232+ }
230233 }
231234 }
232235 if let preAuthorizedCode, let authCode = try ? IssuanceAuthorization ( preAuthorizationCode: preAuthorizedCode, txCode: txCodeSpec) {
@@ -302,13 +305,13 @@ public actor OpenId4VCIService {
302305 /// - keyOptions: Key options (secure area name and other options) for the document issuing (optional)
303306 /// - promptMessage: Prompt message for biometric authentication (optional)
304307 /// - Returns: Array of issued documents. They are saved in storage.
305- @discardableResult func reissueDocument( documentId: WalletStorage . Document . ID , docMetadata: DocMetadata , authorized: AuthorizedRequest ? = nil , credentialOptions: CredentialOptions ? = nil , keyOptions: KeyOptions ? = nil , promptMessage: String ? = nil ) async throws -> [ WalletStorage . Document ] {
308+ @discardableResult func reissueDocument( documentId: WalletStorage . Document . ID , docMetadata: DocMetadata , authorized: AuthorizedRequest ? = nil , credentialOptions: CredentialOptions ? = nil , keyOptions: KeyOptions ? = nil , promptMessage: String ? = nil , backgroundOnly : Bool = false ) async throws -> [ WalletStorage . Document ] {
306309 let ( credentialConfigurations, offer) = try await buildCredentialOffer ( for: [ . identifier( docMetadata. configurationIdentifier) ] )
307310 let credentialConfiguration = credentialConfigurations. first!
308311 let offerUri = UUID ( ) . uuidString
309312 Self . credentialOfferCache [ offerUri] = offer
310313 let docTypes = [ makeOfferedDocModel ( from: credentialConfiguration, credentialOptions: credentialOptions, keyOptions: keyOptions) ]
311- return try await issueDocumentsByOfferUrl ( offerUri: offerUri, docTypes: docTypes, authorized: authorized, documentId: documentId, txCodeValue: nil , promptMessage: promptMessage)
314+ return try await issueDocumentsByOfferUrl ( offerUri: offerUri, docTypes: docTypes, authorized: authorized, documentId: documentId, txCodeValue: nil , promptMessage: promptMessage, backgroundOnly : backgroundOnly , dpopKeyId : docMetadata . dpopKeyId )
312315 }
313316
314317 /// Issue multiple documents using OpenId4Vci protocol
@@ -341,7 +344,7 @@ public actor OpenId4VCIService {
341344 /// - txCodeValue: Transaction code given to user (if available)
342345 /// - promptMessage: prompt message for biometric authentication (optional)
343346 /// - Returns: Array of issued and stored documents
344- func issueDocumentsByOfferUrl( offerUri: String , docTypes: [ OfferedDocModel ] , authorized: AuthorizedRequest ? , documentId: String ? , txCodeValue: String ? = nil , promptMessage: String ? = nil ) async throws -> [ WalletStorage . Document ] {
347+ func issueDocumentsByOfferUrl( offerUri: String , docTypes: [ OfferedDocModel ] , authorized: AuthorizedRequest ? , documentId: String ? , txCodeValue: String ? = nil , promptMessage: String ? = nil , backgroundOnly : Bool = false , dpopKeyId : String ? = nil ) async throws -> [ WalletStorage . Document ] {
345348 if docTypes. isEmpty { return [ ] }
346349 guard let offer = Self . credentialOfferCache [ offerUri] else {
347350 throw PresentationSession . makeError ( str: " Offer URI not resolved: \( offerUri) " )
@@ -356,13 +359,13 @@ public actor OpenId4VCIService {
356359 try await svc. prepareIssuing ( id: id, docTypeIdentifier: docTypeIdentifier, displayName: i > 0 ? nil : docTypes. map ( \. displayName) . joined ( separator: " , " ) , credentialOptions: usedCredentialOptions, keyOptions: docTypeModel. keyOptions, disablePrompt: i > 0 , promptMessage: promptMessage)
357360 openId4VCIServices. append ( svc)
358361 }
359- let ( auth, issuer, credentialInfos) = try await openId4VCIServices. first!. authorizeOffer ( offerUri: offerUri, docTypeModels: docTypes, txCodeValue: txCodeValue, authorized: authorized)
362+ let ( auth, issuer, credentialInfos) = try await openId4VCIServices. first!. authorizeOffer ( offerUri: offerUri, docTypeModels: docTypes, txCodeValue: txCodeValue, authorized: authorized, backgroundOnly : backgroundOnly , dpopKeyId : dpopKeyId )
360363 let documents = try await withThrowingTaskGroup ( of: WalletStorage . Document. self) { group in
361364 for (i, openId4VCIService) in openId4VCIServices. enumerated ( ) {
362365 group. addTask {
363366 let ( bindingKeys, publicKeys) = try await openId4VCIService. initSecurityKeys ( credentialInfos [ i] )
364367 let docData = try await openId4VCIService. issueDocumentByOfferUrl ( issuer: issuer, offer: offer, authorizedOutcome: auth, configuration: credentialInfos [ i] , bindingKeys: bindingKeys, publicKeys: publicKeys, promptMessage: promptMessage)
365- return try await self . finalizeIssuing ( issueOutcome: docData, docType: docTypes [ i] . docTypeOrVct, format: credentialInfos [ i] . format, issueReq: openId4VCIService. issueReq, deleteId: documentId)
368+ return try await self . finalizeIssuing ( issueOutcome: docData, docType: docTypes [ i] . docTypeOrVct, format: credentialInfos [ i] . format, issueReq: openId4VCIService. issueReq, deleteId: documentId, dpopKeyId : dpopKeyId )
366369 }
367370 }
368371 var result = [ WalletStorage . Document] ( )
@@ -756,7 +759,8 @@ public actor OpenId4VCIService {
756759 }
757760 }
758761
759- func finalizeIssuing( issueOutcome: IssuanceOutcome , docType: String ? , format: DocDataFormat , issueReq: IssueRequest , deleteId: String ? ) async throws -> WalletStorage . Document {
762+ func finalizeIssuing( issueOutcome: IssuanceOutcome , docType: String ? , format: DocDataFormat , issueReq: IssueRequest , deleteId: String ? , dpopKeyId: String ? = nil ) async throws -> WalletStorage . Document {
763+ let savedDpopKeyId = dpopKeyId ?? issueReq. dpopKeyId
760764 var dataToSave : Data ; var docTypeToSave = " "
761765 var docMetadata : DocMetadata ; var displayName : String ?
762766 let pds = issueOutcome. pendingOrDeferredStatus
@@ -767,7 +771,7 @@ public actor OpenId4VCIService {
767771 case . issued( let dataPair, let cc, let authorized) :
768772 guard dataPair. first != nil else { throw PresentationSession . makeError ( str: " Empty issued data array " ) }
769773 dataToSave = issueOutcome. getDataToSave ( index: 0 , format: format)
770- docMetadata = cc. convertToDocMetadata ( authorized: authorized, keyOptions: issueReq. keyOptions, credentialOptions: issueReq. credentialOptions)
774+ docMetadata = cc. convertToDocMetadata ( authorized: authorized, keyOptions: issueReq. keyOptions, credentialOptions: issueReq. credentialOptions, dpopKeyId : savedDpopKeyId )
771775 let docTypeOrVctOrScope = docType ?? cc. docType ?? cc. scope ?? " "
772776 dkInfo. batchSize = dataPair. count
773777 docTypeToSave = if format == . cbor, dataToSave. count > 0 { ( try IssuerSigned ( data: [ UInt8] ( dataToSave) ) ) . issuerAuth. mso. docType } else if format == . sdjwt, dataToSave. count > 0 { StorageManager . getVctFromSdJwt ( docData: dataToSave) ?? docTypeOrVctOrScope } else { docTypeOrVctOrScope }
@@ -778,12 +782,12 @@ public actor OpenId4VCIService {
778782 }
779783 case . deferred( let deferredIssuanceModel) :
780784 dataToSave = try JSONEncoder ( ) . encode ( deferredIssuanceModel)
781- docMetadata = deferredIssuanceModel. configuration. convertToDocMetadata ( )
785+ docMetadata = deferredIssuanceModel. configuration. convertToDocMetadata ( dpopKeyId : savedDpopKeyId )
782786 docTypeToSave = docType ?? " DEFERRED "
783787 displayName = deferredIssuanceModel. configuration. display. getName ( uiCulture)
784788 case . pending( let pendingAuthModel) :
785789 dataToSave = try JSONEncoder ( ) . encode ( pendingAuthModel)
786- docMetadata = pendingAuthModel. configuration. convertToDocMetadata ( )
790+ docMetadata = pendingAuthModel. configuration. convertToDocMetadata ( dpopKeyId : savedDpopKeyId )
787791 docTypeToSave = docType ?? " PENDING "
788792 displayName = pendingAuthModel. configuration. display. getName ( uiCulture)
789793 }
0 commit comments