1+ using System . Collections ;
12using System . IdentityModel . Tokens . Jwt ;
23using System . Security . Cryptography ;
34using System . Security . Cryptography . X509Certificates ;
45using LanguageExt ;
6+ using Newtonsoft . Json . Linq ;
57using Org . BouncyCastle . X509 ;
68using WalletFramework . Core . Functional ;
79using WalletFramework . Core . X509 ;
10+ using WalletFramework . Oid4Vc . Oid4Vp . DcApi . Models ;
811using WalletFramework . Oid4Vc . Oid4Vp . Errors ;
912using WalletFramework . Oid4Vc . Oid4Vp . Extensions ;
13+ using static WalletFramework . Core . Functional . ValidationFun ;
1014using X509Certificate = Org . BouncyCastle . X509 . X509Certificate ;
1115using static WalletFramework . Oid4Vc . Oid4Vp . Models . AuthorizationRequest ;
1216using static Newtonsoft . Json . Linq . JArray ;
@@ -24,12 +28,12 @@ public readonly struct RequestObject
2428 /// <summary>
2529 /// The client ID scheme used to obtain and validate metadata of the verifier.
2630 /// </summary>
27- public ClientIdScheme ClientIdScheme => AuthorizationRequest . ClientIdScheme ;
31+ public ClientIdScheme ClientIdScheme => AuthorizationRequest . ClientIdScheme ! ;
2832
2933 /// <summary>
3034 /// The client ID of the verifier.
3135 /// </summary>
32- public string ClientId => AuthorizationRequest . ClientId ;
36+ public string ClientId => AuthorizationRequest . ClientId ! ;
3337
3438 private AuthorizationRequest AuthorizationRequest { get ; init ; }
3539
@@ -66,19 +70,20 @@ public static Validation<AuthorizationRequestCancellation, RequestObject> FromSt
6670 return new AuthorizationRequestCancellation ( Option < Uri > . None , [ error ] ) ;
6771 }
6872
69- walletNonce . IfSome ( nonce =>
73+ walletNonce . IfSome ( nonce =>
7074 {
7175 if ( jwt . Payload . TryGetValue ( "wallet_nonce" , out var nonceValue ) )
7276 {
7377 if ( nonceValue . ToString ( ) != nonce )
74- throw new InvalidOperationException ( "wallet_nonce in request object does not match the provided wallet_nonce" ) ;
78+ throw new InvalidOperationException (
79+ "wallet_nonce in request object does not match the provided wallet_nonce" ) ;
7580 }
7681 else
7782 {
7883 throw new InvalidOperationException ( "wallet_nonce is required but not present in the Request Object" ) ;
7984 }
8085 } ) ;
81-
86+
8287 var json = jwt . Payload . SerializeToJson ( ) ;
8388
8489 return
@@ -90,7 +95,7 @@ from authRequest in CreateAuthorizationRequest(json)
9095 /// Gets the authorization request from the request object.
9196 /// </summary>
9297 public AuthorizationRequest ToAuthorizationRequest ( ) => AuthorizationRequest ;
93-
98+
9499 internal RequestObject WithX509 ( )
95100 {
96101 var encodedCertificate = this . GetLeafCertificate ( ) . GetEncoded ( ) ;
@@ -118,7 +123,7 @@ internal RequestObject WithX509()
118123 AuthorizationRequest = authRequest
119124 } ;
120125 }
121-
126+
122127 internal RequestObject WithClientMetadata ( Option < ClientMetadata > clientMetadata )
123128 {
124129 var authRequest = AuthorizationRequest with
@@ -138,6 +143,13 @@ internal RequestObject WithClientMetadata(Option<ClientMetadata> clientMetadata)
138143/// </summary>
139144public static class RequestObjectExtensions
140145{
146+ public static RequestObject ValidateClientIdPrefix ( this RequestObject requestObject ) =>
147+ requestObject . ClientIdScheme . Value == ClientIdScheme . ClientIdSchemeValue . RedirectUri
148+ && requestObject . ToAuthorizationRequest ( ) . ResponseUri != requestObject . ClientId
149+ ? throw new InvalidOperationException (
150+ "When client_id_prefix is 'redirect_uri', the response_uri must match the client_id" )
151+ : requestObject ;
152+
141153 /// <summary>
142154 /// Validates the JWT signature.
143155 /// </summary>
@@ -182,22 +194,15 @@ public static RequestObject ValidateTrustChain(this RequestObject requestObject)
182194 else
183195 throw new InvalidOperationException ( "Validation of trust chain failed" ) ;
184196 }
185-
186- public static RequestObject ValidateClientIdPrefix ( this RequestObject requestObject ) =>
187- requestObject . ClientIdScheme . Value == ClientIdScheme . ClientIdSchemeValue . RedirectUri
188- && requestObject . ToAuthorizationRequest ( ) . ResponseUri != requestObject . ClientId
189- ? throw new InvalidOperationException ( "When client_id_prefix is 'redirect_uri', the response_uri must match the client_id" )
190- : requestObject ;
191-
197+
192198 internal static List < X509Certificate > GetCertificates ( this RequestObject requestObject )
193199 {
194200 var x5C = ( ( JwtSecurityToken ) requestObject ) . Header . X5c ;
195- var result = Parse ( x5C ) . Select (
196- certAsJToken =>
197- {
198- var certBytes = FromBase64String ( certAsJToken . ToString ( ) ) ;
199- return new X509CertificateParser ( ) . ReadCertificate ( certBytes ) ;
200- } ) . ToList ( ) ;
201+ var result = Parse ( x5C ) . Select ( certAsJToken =>
202+ {
203+ var certBytes = FromBase64String ( certAsJToken . ToString ( ) ) ;
204+ return new X509CertificateParser ( ) . ReadCertificate ( certBytes ) ;
205+ } ) . ToList ( ) ;
201206
202207 if ( result . Count == 0 )
203208 {
@@ -210,20 +215,49 @@ internal static List<X509Certificate> GetCertificates(this RequestObject request
210215 internal static X509Certificate GetLeafCertificate ( this RequestObject requestObject ) =>
211216 GetCertificates ( requestObject ) . First ( ) ;
212217
218+ internal static Validation < AuthorizationRequestCancellation , RequestObject > ValidateOrigin ( this RequestObject requestObject , Option < Origin > origin )
219+ {
220+ var authRequest = requestObject . ToAuthorizationRequest ( ) ;
221+ var responseUriOption = authRequest . GetResponseUriMaybe ( ) ;
222+
223+ return origin . Match (
224+ value =>
225+ {
226+ var expectedOrigins = authRequest . ExpectedOrigins ;
227+ if ( expectedOrigins ? . Contains ( value ) ?? false )
228+ {
229+ return Prelude . Success < AuthorizationRequestCancellation , RequestObject > ( requestObject ) ;
230+ }
231+ else
232+ {
233+ var expectedOriginsList = string . Join ( ", " , expectedOrigins ? . Select ( o => o . Value ) ?? [ ] ) ;
234+ var error = new OriginMismatchError (
235+ $ "Origin { value } is not present in expected origins: [{ expectedOriginsList } ]") ;
236+ return new AuthorizationRequestCancellation ( responseUriOption , [ error ] ) ;
237+ }
238+ } ,
239+ ( ) =>
240+ {
241+ var error = new OriginMismatchError ( "Origin is required but not provided" ) ;
242+ return new AuthorizationRequestCancellation ( responseUriOption , [ error ] ) ;
243+ }
244+ ) ;
245+ }
246+
213247 private static IEnumerable < string > GetSanDnsNames ( X509Certificate2 certificate )
214248 {
215249 const string sanOid = "2.5.29.17" ;
216250 var sanNames = new List < string > ( ) ;
217251
218252 foreach ( var extension in certificate . Extensions )
219253 {
220- if ( extension . Oid . Value != sanOid )
254+ if ( extension . Oid ! . Value != sanOid )
221255 continue ;
222256
223- var sanExtension = ( AsnEncodedData ) extension ;
257+ AsnEncodedData sanExtension = extension ;
224258 var sanData = sanExtension . Format ( true ) ;
225259
226- foreach ( var line in sanData . Split ( new [ ] { "\r \n " , "\n " , "," } , StringSplitOptions . RemoveEmptyEntries ) )
260+ foreach ( var line in sanData . Split ( [ "\r \n " , "\n " , "," ] , StringSplitOptions . RemoveEmptyEntries ) )
227261 {
228262 sanNames . Add ( line . Split ( ':' , '=' ) . Last ( ) . Trim ( ) ) ;
229263 }
0 commit comments