Skip to content

Commit

Permalink
return DocumentExpired when a Deferred document has expired accessTok…
Browse files Browse the repository at this point in the history
…en and/or refreshToken
  • Loading branch information
vkanellopoulos committed Jul 23, 2024
1 parent 7106c70 commit c661b02
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,29 @@ internal class DefaultOpenId4VciManager(
launch(executor, onIssueResult) { coroutineScope, callback ->
try {
val deferredContext = deferredDocument.relatedData.toDeferredIssuanceContext()
val (ctx, outcome) = DeferredIssuer.queryForDeferredCredential(deferredContext, ktorHttpClientFactory)
.getOrThrow()
ProcessDeferredOutcome(
documentManager = documentManager,
callback = callback,
deferredIssuanceContext = ctx,
logger = logger
).process(deferredDocument, outcome)
when {
deferredContext.hasExpired -> callback(
DeferredIssueResult.DocumentExpired(
documentId = deferredDocument.id,
name = deferredDocument.name,
docType = deferredDocument.docType
)
)

else -> {
val (ctx, outcome) = DeferredIssuer.queryForDeferredCredential(
deferredContext,
ktorHttpClientFactory
)
.getOrThrow()
ProcessDeferredOutcome(
documentManager = documentManager,
callback = callback,
deferredIssuanceContext = ctx,
logger = logger
).process(deferredDocument, outcome)
}
}
} catch (e: Throwable) {
callback(
DeferredIssueResult.DocumentFailed(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,16 @@ sealed interface DeferredIssueResult : OpenId4VciResult {
override val name: String,
override val docType: String,
) : DeferredIssueResult

/**
* Document issuance expired.
* @property documentId the id of the expired document
* @property name the name of the document
* @property docType the document type
*/
data class DocumentExpired(
override val documentId: DocumentId,
override val name: String,
override val docType: String,
) : DeferredIssueResult
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package eu.europa.ec.eudi.wallet.issue.openid4vci

import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.crypto.impl.ECDSA
import eu.europa.ec.eudi.openid4vci.DeferredIssuanceContext
import eu.europa.ec.eudi.wallet.document.CreateDocumentResult
import eu.europa.ec.eudi.wallet.document.DocumentManager
import eu.europa.ec.eudi.wallet.document.UnsignedDocument
Expand All @@ -27,6 +28,7 @@ import org.bouncycastle.util.io.pem.PemObject
import org.bouncycastle.util.io.pem.PemWriter
import java.io.StringWriter
import java.security.PublicKey
import java.time.Instant
import java.util.concurrent.Executor

/**
Expand Down Expand Up @@ -122,16 +124,27 @@ internal inline fun <reified V : OpenId4VciResult> OpenId4VciManager.OnResult<V>
*/
@JvmSynthetic
internal inline fun <reified V : OpenId4VciResult> OpenId4VciManager.OnResult<V>.wrapWithLogging(logger: Logger?): OpenId4VciManager.OnResult<V> {
return when (val l = logger) {
return when (logger) {
null -> this
else -> OpenId4VciManager.OnResult { result: V ->
when (result) {
is OpenId4VciResult.Erroneous -> l.e(TAG, "$result", result.cause)
else -> l.d(TAG, "$result")
is OpenId4VciResult.Erroneous -> logger.e(TAG, "$result", result.cause)
else -> logger.d(TAG, "$result")
}
this.onResult(result)
}
}
}

@get:JvmSynthetic
internal val DeferredIssuanceContext.hasExpired: Boolean
get() = with(authorizedTransaction.authorizedRequest) {
when (val rt = refreshToken) {
null -> accessToken.isExpired(timestamp, Instant.now())
else -> if (accessToken.isExpired(timestamp, Instant.now())) {
rt.isExpired(timestamp, Instant.now())
} else false
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2024 European Commission
*
* 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 eu.europa.ec.eudi.wallet.issue.openid4vci

import eu.europa.ec.eudi.openid4vci.AccessToken
import eu.europa.ec.eudi.openid4vci.DeferredIssuanceContext
import eu.europa.ec.eudi.openid4vci.RefreshToken
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.time.Instant

class DeferredIssuanceContextTest {

@Test
fun `when accessToken has no expiration time DeferredIssuanceContextTest_hasExpired property is false`() {
val deferredIssuanceContext: DeferredIssuanceContext = mockk {
every { authorizedTransaction.authorizedRequest.timestamp } returns Instant.now().minusSeconds(3600)
every { authorizedTransaction.authorizedRequest.accessToken } returns AccessToken(
accessToken = "accessToken",
expiresInSec = null,
useDPoP = false
)
every { authorizedTransaction.authorizedRequest.refreshToken } returns null
}

assertFalse(deferredIssuanceContext.hasExpired)
}

@Test
fun `when accessToken has expired DeferredIssuanceContextTest_hasExpired property is true`() {
val deferredIssuanceContext: DeferredIssuanceContext = mockk {
every { authorizedTransaction.authorizedRequest.timestamp } returns Instant.now().minusSeconds(3600)
every { authorizedTransaction.authorizedRequest.accessToken } returns AccessToken(
accessToken = "accessToken",
expiresInSec = 60L,
useDPoP = false
)
every { authorizedTransaction.authorizedRequest.refreshToken } returns null
}

assertTrue(deferredIssuanceContext.hasExpired)
}

@Test
fun `when accessToken has not expired and there is no refreshToken DeferredIssuanceContextTest_hasExpired property is false`() {
val deferredIssuanceContext: DeferredIssuanceContext = mockk {
every { authorizedTransaction.authorizedRequest.timestamp } returns Instant.now().minusSeconds(3600)
every { authorizedTransaction.authorizedRequest.accessToken } returns AccessToken(
accessToken = "accessToken",
expiresInSec = 6000L,
useDPoP = false
)
every { authorizedTransaction.authorizedRequest.refreshToken } returns null
}

assertFalse(deferredIssuanceContext.hasExpired)
}

@Test
fun `when accessToken has expired and there is refreshToken which has not expired DeferredIssuanceContextTest_hasExpired property is false`() {
val deferredIssuanceContext: DeferredIssuanceContext = mockk {
every { authorizedTransaction.authorizedRequest.timestamp } returns Instant.now().minusSeconds(3600)
every { authorizedTransaction.authorizedRequest.accessToken } returns AccessToken(
accessToken = "accessToken",
expiresInSec = 10L,
useDPoP = false
)
every { authorizedTransaction.authorizedRequest.refreshToken } returns RefreshToken(
refreshToken = "refreshToken",
expiresInSec = 6000L
)
}

assertFalse(deferredIssuanceContext.hasExpired)
}

@Test
fun `when accessToken has expired and there is refreshToken which has expired DeferredIssuanceContextTest_hasExpired property is true`() {
val deferredIssuanceContext: DeferredIssuanceContext = mockk {
every { authorizedTransaction.authorizedRequest.timestamp } returns Instant.now().minusSeconds(3600)
every { authorizedTransaction.authorizedRequest.accessToken } returns AccessToken(
accessToken = "accessToken",
expiresInSec = 10L,
useDPoP = false
)
every { authorizedTransaction.authorizedRequest.refreshToken } returns RefreshToken(
refreshToken = "refreshToken",
expiresInSec = 60L
)
}

assertTrue(deferredIssuanceContext.hasExpired)
}
}

0 comments on commit c661b02

Please sign in to comment.