@@ -73,34 +73,37 @@ internal class RequestAuthenticator(siopOpenId4VPConfig: SiopOpenId4VPConfig, ht
73
73
private val clientAuthenticator = ClientAuthenticator (siopOpenId4VPConfig)
74
74
private val signatureVerifier = JarJwtSignatureVerifier (siopOpenId4VPConfig, httpClient)
75
75
76
- suspend fun authenticate (request : FetchedRequest ): AuthenticatedRequest = coroutineScope {
76
+ suspend fun authenticate (request : ReceivedRequest ): AuthenticatedRequest = coroutineScope {
77
77
val client = clientAuthenticator.authenticateClient(request)
78
78
when (request) {
79
- is FetchedRequest . Plain -> {
79
+ is ReceivedRequest . Unsigned -> {
80
80
AuthenticatedRequest (client, request.requestObject)
81
81
}
82
82
83
- is FetchedRequest .JwtSecured -> {
84
- with (signatureVerifier) { verifySignature(client, request.jwt) }
85
- AuthenticatedRequest (client, request.jwt.requestObject())
83
+ is ReceivedRequest .Signed -> {
84
+ val signedJwt = request.ensureSingleSignedRequest()
85
+ with (signatureVerifier) { verifySignature(client, signedJwt) }
86
+ AuthenticatedRequest (client, signedJwt.requestObject())
86
87
}
87
88
}
88
89
}
89
90
}
90
91
91
92
internal class ClientAuthenticator (private val siopOpenId4VPConfig : SiopOpenId4VPConfig ) {
92
- suspend fun authenticateClient (request : FetchedRequest ): AuthenticatedClient {
93
+ suspend fun authenticateClient (request : ReceivedRequest ): AuthenticatedClient {
93
94
val requestObject = when (request) {
94
- is FetchedRequest .JwtSecured -> request.jwt.requestObject()
95
- is FetchedRequest .Plain -> request.requestObject
95
+ is ReceivedRequest .Signed -> {
96
+ request.ensureSingleSignedRequest().requestObject()
97
+ }
98
+ is ReceivedRequest .Unsigned -> request.requestObject
96
99
}
97
100
98
101
val (originalClientId, clientIdScheme) = originalClientIdAndScheme(requestObject)
99
102
return when (clientIdScheme) {
100
103
is Preregistered -> {
101
104
val registeredClient = clientIdScheme.clients[originalClientId]
102
105
ensureNotNull(registeredClient) { RequestValidationError .InvalidClientId .asException() }
103
- if (request is FetchedRequest . JwtSecured ) {
106
+ if (request is ReceivedRequest . Signed ) {
104
107
ensureNotNull(registeredClient.jarConfig) {
105
108
invalidScheme(" $registeredClient cannot place signed request" )
106
109
}
@@ -109,7 +112,7 @@ internal class ClientAuthenticator(private val siopOpenId4VPConfig: SiopOpenId4V
109
112
}
110
113
111
114
SupportedClientIdScheme .RedirectUri -> {
112
- ensure(request is FetchedRequest . Plain ) {
115
+ ensure(request is ReceivedRequest . Unsigned ) {
113
116
invalidScheme(" ${clientIdScheme.scheme()} cannot be used in signed request" )
114
117
}
115
118
val originalClientIdAsUri =
@@ -118,7 +121,7 @@ internal class ClientAuthenticator(private val siopOpenId4VPConfig: SiopOpenId4V
118
121
}
119
122
120
123
is SupportedClientIdScheme .X509SanDns -> {
121
- ensure(request is FetchedRequest . JwtSecured ) {
124
+ ensure(request is ReceivedRequest . Signed ) {
122
125
invalidScheme(" ${clientIdScheme.scheme()} cannot be used in unsigned request" )
123
126
}
124
127
val chain = x5c(originalClientId, request, clientIdScheme.trust) {
@@ -129,7 +132,7 @@ internal class ClientAuthenticator(private val siopOpenId4VPConfig: SiopOpenId4V
129
132
}
130
133
131
134
is SupportedClientIdScheme .X509SanUri -> {
132
- ensure(request is FetchedRequest . JwtSecured ) {
135
+ ensure(request is ReceivedRequest . Signed ) {
133
136
invalidScheme(" ${clientIdScheme.scheme()} cannot be used in unsigned request" )
134
137
}
135
138
val chain = x5c(originalClientId, request, clientIdScheme.trust) {
@@ -142,7 +145,7 @@ internal class ClientAuthenticator(private val siopOpenId4VPConfig: SiopOpenId4V
142
145
}
143
146
144
147
is SupportedClientIdScheme .DID -> {
145
- ensure(request is FetchedRequest . JwtSecured ) {
148
+ ensure(request is ReceivedRequest . Signed ) {
146
149
invalidScheme(" ${clientIdScheme.scheme()} cannot be used in unsigned request" )
147
150
}
148
151
val originalClientIdAsDID = ensureNotNull(DID .parse(originalClientId).getOrNull()) {
@@ -153,7 +156,7 @@ internal class ClientAuthenticator(private val siopOpenId4VPConfig: SiopOpenId4V
153
156
}
154
157
155
158
is SupportedClientIdScheme .VerifierAttestation -> {
156
- ensure(request is FetchedRequest . JwtSecured ) {
159
+ ensure(request is ReceivedRequest . Signed ) {
157
160
invalidScheme(" ${clientIdScheme.scheme()} cannot be used in unsigned request" )
158
161
}
159
162
val attestedClaims =
@@ -174,11 +177,12 @@ internal class ClientAuthenticator(private val siopOpenId4VPConfig: SiopOpenId4V
174
177
175
178
private fun x5c (
176
179
originalClientId : OriginalClientId ,
177
- request : FetchedRequest . JwtSecured ,
180
+ request : ReceivedRequest . Signed ,
178
181
trust : X509CertificateTrust ,
179
182
subjectAlternativeNames : X509Certificate .() -> List <String >,
180
183
): List <X509Certificate > {
181
- val x5c = request.jwt.header?.x509CertChain
184
+ val jwt = request.ensureSingleSignedRequest()
185
+ val x5c = jwt.header?.x509CertChain
182
186
ensureNotNull(x5c) { invalidJarJwt(" Missing x5c" ) }
183
187
val pubCertChain = x5c.mapNotNull { runCatching { X509CertUtils .parse(it.decode()) }.getOrNull() }
184
188
ensure(pubCertChain.isNotEmpty()) { invalidJarJwt(" Invalid x5c" ) }
@@ -192,13 +196,21 @@ internal class ClientAuthenticator(private val siopOpenId4VPConfig: SiopOpenId4V
192
196
}
193
197
}
194
198
199
+ private fun ReceivedRequest.Signed.ensureSingleSignedRequest (): SignedJWT {
200
+ val signedJwts = toSignedJwts()
201
+ return ensure(signedJwts.size == 1 ) {
202
+ invalidJarJwt(" Multi-signed authorization requests are not yet supported" )
203
+ }.let { signedJwts[0 ] }
204
+ }
205
+
195
206
private suspend fun lookupKeyByDID (
196
- request : FetchedRequest . JwtSecured ,
207
+ request : ReceivedRequest . Signed ,
197
208
clientId : DID ,
198
209
lookupPublicKeyByDIDUrl : LookupPublicKeyByDIDUrl ,
199
210
): PublicKey = withContext(Dispatchers .IO ) {
200
211
val keyUrl: AbsoluteDIDUrl = run {
201
- val kid = ensureNotNull(request.jwt.header?.keyID) {
212
+ val jwt = request.ensureSingleSignedRequest()
213
+ val kid = ensureNotNull(jwt.header?.keyID) {
202
214
invalidJarJwt(" Missing kid for client_id $clientId " )
203
215
}
204
216
ensureNotNull(AbsoluteDIDUrl .parse(kid).getOrNull()) {
@@ -217,15 +229,16 @@ private suspend fun lookupKeyByDID(
217
229
private fun verifierAttestation (
218
230
clock : Clock ,
219
231
supportedScheme : SupportedClientIdScheme .VerifierAttestation ,
220
- request : FetchedRequest . JwtSecured ,
232
+ request : ReceivedRequest . Signed ,
221
233
originalClientId : OriginalClientId ,
222
234
): VerifierAttestationClaims {
223
235
val (trust, skew) = supportedScheme
224
236
fun invalidVerifierAttestationJwt (cause : String? ) =
225
237
invalidJarJwt(" Invalid VerifierAttestation JWT. Details: $cause " )
226
238
227
239
val verifierAttestationJwt = run {
228
- val jwtString = request.jwt.header.customParams[" jwt" ]
240
+ val jwt = request.ensureSingleSignedRequest()
241
+ val jwtString = jwt.header.customParams[" jwt" ]
229
242
ensureNotNull(jwtString) { invalidJarJwt(" Missing jwt JOSE Header" ) }
230
243
ensure(jwtString is String ) { invalidJarJwt(" jwt JOSE Header doesn't contain a JWT" ) }
231
244
0 commit comments