|
1 | 1 | package id.waltid.openid4vp.wallet.request |
2 | 2 |
|
| 3 | +import id.walt.verifier.openid.models.authorization.RequestUriHttpMethod |
| 4 | +import id.walt.webdatafetching.WebDataFetcher |
| 5 | +import io.ktor.client.HttpClient |
| 6 | +import io.ktor.client.engine.mock.MockEngine |
| 7 | +import io.ktor.client.engine.mock.respond |
| 8 | +import io.ktor.client.request.HttpRequestData |
3 | 9 | import io.ktor.http.URLBuilder |
| 10 | +import io.ktor.http.ContentType |
| 11 | +import io.ktor.http.HttpHeaders |
| 12 | +import io.ktor.http.HttpMethod |
| 13 | +import io.ktor.http.content.OutgoingContent |
| 14 | +import io.ktor.http.contentType |
| 15 | +import io.ktor.http.headersOf |
| 16 | +import io.ktor.http.parseQueryString |
4 | 17 | import kotlinx.coroutines.runBlocking |
| 18 | +import kotlinx.serialization.json.Json |
| 19 | +import kotlinx.serialization.json.jsonArray |
| 20 | +import kotlinx.serialization.json.jsonObject |
| 21 | +import kotlinx.serialization.json.jsonPrimitive |
5 | 22 | import java.util.Base64 |
6 | 23 | import kotlin.test.Test |
7 | 24 | import kotlin.test.assertEquals |
8 | 25 | import kotlin.test.assertFailsWith |
| 26 | +import kotlin.test.assertFalse |
9 | 27 | import kotlin.test.assertIs |
10 | 28 |
|
11 | 29 | class AuthorizationRequestResolverJvmTest { |
12 | 30 |
|
| 31 | + @Test |
| 32 | + fun `request uri post wallet metadata declares supported response and client id capabilities`() { |
| 33 | + val metadata = Json.parseToJsonElement( |
| 34 | + AuthorizationRequestResolver.buildRequestUriPostWalletMetadata( |
| 35 | + vpFormatsSupported = jsonObjectOf("dc+sd-jwt" to jsonObjectOf()), |
| 36 | + ), |
| 37 | + ).jsonObject |
| 38 | + |
| 39 | + assertEquals( |
| 40 | + listOf("vp_token", "vp_token id_token"), |
| 41 | + metadata.getValue("response_types_supported").jsonArray.map { it.jsonPrimitive.content }, |
| 42 | + ) |
| 43 | + assertEquals( |
| 44 | + listOf("fragment", "query", "direct_post", "direct_post.jwt", "form_post"), |
| 45 | + metadata.getValue("response_modes_supported").jsonArray.map { it.jsonPrimitive.content }, |
| 46 | + ) |
| 47 | + val expectedClientIdPrefixes = |
| 48 | + listOf( |
| 49 | + "redirect_uri", |
| 50 | + "x509_san_dns", |
| 51 | + "x509_hash", |
| 52 | + "decentralized_identifier", |
| 53 | + "verifier_attestation", |
| 54 | + ) |
| 55 | + assertFalse("client_id_schemes_supported" in metadata) |
| 56 | + assertEquals( |
| 57 | + expectedClientIdPrefixes, |
| 58 | + metadata.getValue("client_id_prefixes_supported").jsonArray.map { it.jsonPrimitive.content }, |
| 59 | + ) |
| 60 | + assertEquals( |
| 61 | + jsonObjectOf("dc+sd-jwt" to jsonObjectOf()), |
| 62 | + metadata.getValue("vp_formats_supported").jsonObject, |
| 63 | + ) |
| 64 | + } |
| 65 | + |
| 66 | + @Test |
| 67 | + fun `request uri post fetch sends wallet metadata and wallet nonce as form fields`() = runBlocking { |
| 68 | + var capturedRequest: HttpRequestData? = null |
| 69 | + val client = HttpClient(MockEngine) { |
| 70 | + engine { |
| 71 | + addHandler { request -> |
| 72 | + capturedRequest = request |
| 73 | + respond( |
| 74 | + content = "signed-request-object", |
| 75 | + headers = headersOf(HttpHeaders.ContentType, "application/oauth-authz-req+jwt"), |
| 76 | + ) |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + val fetcher = WebDataFetcher.wrapping(client, id = "authorization-request-resolver-test") |
| 81 | + val walletMetadata = AuthorizationRequestResolver.buildRequestUriPostWalletMetadata( |
| 82 | + vpFormatsSupported = jsonObjectOf("dc+sd-jwt" to jsonObjectOf()), |
| 83 | + ) |
| 84 | + |
| 85 | + val response = AuthorizationRequestResolver.fetchRequestUriWithWebDataFetcher( |
| 86 | + webResolveAuthReq = fetcher, |
| 87 | + requestUri = "https://verifier.example/request.jwt", |
| 88 | + requestUriMethod = RequestUriHttpMethod.POST, |
| 89 | + requestUriPostWalletMetadata = walletMetadata, |
| 90 | + ) |
| 91 | + val request = requireNotNull(capturedRequest) |
| 92 | + val form = parseQueryString(request.bodyText()) |
| 93 | + |
| 94 | + assertEquals(HttpMethod.Post, request.method) |
| 95 | + assertEquals(ContentType.Application.FormUrlEncoded, request.body.contentType) |
| 96 | + assertEquals(walletMetadata, form["wallet_metadata"]) |
| 97 | + assertEquals(response.walletNonce, form["wallet_nonce"]) |
| 98 | + assertFalse(response.walletNonce.isNullOrBlank()) |
| 99 | + assertEquals("signed-request-object", response.body) |
| 100 | + } |
| 101 | + |
13 | 102 | @Test |
14 | 103 | fun `unsigned request object is rejected when policy requires signed request objects`() { |
15 | 104 | val requestObject = unsignedJwt( |
@@ -71,4 +160,10 @@ class AuthorizationRequestResolverJvmTest { |
71 | 160 | Base64.getUrlEncoder().withoutPadding().encodeToString(segment.toByteArray()) |
72 | 161 | } + "." |
73 | 162 | } |
| 163 | + |
| 164 | + private fun jsonObjectOf(vararg pairs: Pair<String, kotlinx.serialization.json.JsonElement>) = |
| 165 | + kotlinx.serialization.json.JsonObject(mapOf(*pairs)) |
| 166 | + |
| 167 | + private fun HttpRequestData.bodyText(): String = |
| 168 | + (body as OutgoingContent.ByteArrayContent).bytes().decodeToString() |
74 | 169 | } |
0 commit comments