@@ -34,6 +34,7 @@ import {
3434 OID4VCICredentialFormat ,
3535 OpenId4VCIVersion ,
3636 PRE_AUTH_GRANT_LITERAL ,
37+ ProofOfPossession ,
3738 QRCodeOpts ,
3839 StatusListOpts ,
3940 TokenErrorResponse ,
@@ -675,16 +676,70 @@ export class VcIssuer {
675676 try {
676677 if ( format && ! supportedIssuanceFormats . includes ( format ) ) {
677678 throw Error ( `Format ${ format } not supported yet` )
678- } else if ( typeof this . _jwtVerifyCallback !== 'function' && typeof jwtVerifyCallback !== 'function' ) {
679- throw new Error ( JWT_VERIFY_CONFIG_ERROR )
680- } else if ( ! credentialRequest . proof ) {
681- throw Error ( 'Proof of possession is required. No proof value present in credential request' )
682679 }
683680
684- const jwtVerifyResult = jwtVerifyCallback
685- ? await jwtVerifyCallback ( credentialRequest . proof )
686- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
687- await this . _jwtVerifyCallback ! ( credentialRequest . proof )
681+ const verifyFn : JWTVerifyCallback | undefined = jwtVerifyCallback ?? this . _jwtVerifyCallback
682+ if ( typeof verifyFn !== 'function' ) {
683+ throw Error ( JWT_VERIFY_CONFIG_ERROR )
684+ }
685+
686+ const credReq = credentialRequest as CredentialRequestV1_0_15
687+
688+ // Validate request structure (mutually exclusive)
689+ if ( credReq . proof && credReq . proofs ) {
690+ throw Error ( 'Credential request may not contain both proof and proofs parameters' )
691+ }
692+
693+ // Normalize candidates into a single array of ProofOfPossession objects
694+ const proofCandidates : Array < ProofOfPossession > = [ ]
695+
696+ if ( credReq . proof ) {
697+ proofCandidates . push ( credReq . proof )
698+ } else if ( credReq . proofs ) {
699+ // Handle "proofs": prioritize 'jwt' as it's the only fully supported type
700+ if ( Array . isArray ( credReq . proofs . jwt ) ) {
701+ // Map to ProofOfPossession objects, handling both string and object formats
702+ for ( const jwtProof of credReq . proofs . jwt ) {
703+ if ( typeof jwtProof === 'string' ) {
704+ // Handle case where jwt array contains strings instead of ProofOfPossession objects
705+ proofCandidates . push ( {
706+ proof_type : 'jwt' ,
707+ jwt : jwtProof ,
708+ } )
709+ } else if ( jwtProof && typeof jwtProof === 'object' && 'jwt' in jwtProof ) {
710+ // Handle proper ProofOfPossession object
711+ proofCandidates . push ( jwtProof )
712+ }
713+ }
714+ }
715+
716+ // Check if there are no supported proofs found
717+ if ( proofCandidates . length === 0 ) {
718+ const availableTypes = Object . keys ( credReq . proofs ) . join ( ', ' )
719+ throw Error ( `No supported proof types found in request. Available: [${ availableTypes } ]` )
720+ }
721+ } else {
722+ throw Error ( 'Proof of possession is required. No proof or proofs value present in credential request' )
723+ }
724+
725+ // Execute verification
726+ let jwtVerifyResult : JwtVerifyResult | undefined
727+ const validationErrors : string [ ] = [ ]
728+
729+ for ( const proof of proofCandidates ) {
730+ try {
731+ jwtVerifyResult = await verifyFn ( { jwt : proof . jwt } )
732+ break
733+ } catch ( error ) {
734+ const msg = error instanceof Error ? error . message : String ( error )
735+ validationErrors . push ( msg )
736+ }
737+ }
738+
739+ // Check success
740+ if ( ! jwtVerifyResult ) {
741+ throw Error ( `Unable to verify any provided proofs. Errors: ${ validationErrors . join ( '; ' ) } ` )
742+ }
688743
689744 const { didDocument, did, jwt } = jwtVerifyResult
690745 const { header, payload } = jwt
0 commit comments