11import { OpenID4VCIClient } from '@sphereon/oid4vci-client'
22import {
33 Alg ,
4+ ALG_ERROR ,
45 CredentialOfferSession ,
56 CredentialSupported ,
67 IssuerCredentialSubjectDisplay ,
78 IssueStatus ,
8- STATE_MISSING_ERROR ,
9+ STATE_MISSING_ERROR
910} from '@sphereon/oid4vci-common'
1011import { IProofPurpose , IProofType } from '@sphereon/ssi-types'
1112import { DIDDocument } from 'did-resolver'
@@ -16,13 +17,36 @@ import { MemoryStates } from '../state-manager'
1617
1718const IDENTIPROOF_ISSUER_URL = 'https://issuer.research.identiproof.io'
1819
20+ const verifiableCredential = {
21+ '@context' : [
22+ 'https://www.w3.org/2018/credentials/v1' ,
23+ 'https://w3id.org/security/suites/jws-2020/v1'
24+ ] ,
25+ id : 'http://university.example/credentials/1872' ,
26+ type : [
27+ 'VerifiableCredential' ,
28+ 'ExampleAlumniCredential'
29+ ] ,
30+ issuer : 'https://university.example/issuers/565049' ,
31+ issuanceDate : new Date ( ) . toISOString ( ) ,
32+ credentialSubject : {
33+ id : 'did:example:ebfeb1f712ebc6f1c276e12ec21' ,
34+ alumniOf : {
35+ id : 'did:example:c276e12ec21ebfeb1f712ebc6f1' ,
36+ name : 'Example University'
37+ }
38+ }
39+ }
40+
1941describe ( 'VcIssuer' , ( ) => {
2042 let vcIssuer : VcIssuer < DIDDocument >
2143 const issuerState = 'previously-created-state'
2244 const clientId = 'sphereon:wallet'
2345 const preAuthorizedCode = 'test_code'
2446
25- beforeAll ( async ( ) => {
47+ const jwtVerifyCallback : jest . Mock = jest . fn ( )
48+
49+ beforeEach ( async ( ) => {
2650 jest . clearAllMocks ( )
2751 const credentialsSupported : CredentialSupported = new CredentialSupportedBuilderV1_11 ( )
2852 . withCryptographicSuitesSupported ( 'ES256K' )
@@ -105,29 +129,7 @@ describe('VcIssuer', () => {
105129 } ,
106130 } ) ,
107131 )
108- . withJWTVerifyCallback ( ( ) =>
109- Promise . resolve ( {
110- did : 'did:example:1234' ,
111- kid : 'did:example:1234#auth' ,
112- alg : 'ES256k' ,
113- didDocument : {
114- '@context' : 'https://www.w3.org/ns/did/v1' ,
115- id : 'did:example:1234' ,
116- } ,
117- jwt : {
118- header : {
119- typ : 'openid4vci-proof+jwt' ,
120- alg : Alg . ES256K ,
121- kid : 'test-kid' ,
122- } ,
123- payload : {
124- aud : 'https://credential-issuer' ,
125- iat : + new Date ( ) ,
126- nonce : 'test-nonce' ,
127- } ,
128- } ,
129- } ) ,
130- )
132+ . withJWTVerifyCallback ( jwtVerifyCallback )
131133 . build ( )
132134 } )
133135
@@ -272,6 +274,29 @@ describe('VcIssuer', () => {
272274
273275 // Of course this doesn't work. The state is part of the proof to begin with
274276 it ( 'should fail issuing credential if an invalid state is used' , async ( ) => {
277+ jwtVerifyCallback . mockResolvedValue ( {
278+ did : 'did:example:1234' ,
279+ kid : 'did:example:1234#auth' ,
280+ alg : Alg . ES256K ,
281+ didDocument : {
282+ '@context' : 'https://www.w3.org/ns/did/v1' ,
283+ id : 'did:example:1234' ,
284+ } ,
285+ jwt : {
286+ header : {
287+ typ : 'openid4vci-proof+jwt' ,
288+ alg : Alg . ES256K ,
289+ kid : 'test-kid' ,
290+ } ,
291+ payload : {
292+ aud : IDENTIPROOF_ISSUER_URL ,
293+ iat : + new Date ( ) ,
294+ nonce : 'test-nonce' ,
295+ } ,
296+ }
297+ }
298+ )
299+
275300 await expect (
276301 vcIssuer . issueCredential ( {
277302 credentialRequest : {
@@ -287,23 +312,65 @@ describe('VcIssuer', () => {
287312 ) . rejects . toThrow ( Error ( STATE_MISSING_ERROR + ' (test-nonce)' ) )
288313 } )
289314
290- // Of course this doesn't work. The state is part of the proof to begin with
291- xit ( 'should issue credential if a valid state is passed in' , async ( ) => {
292- await expect (
315+ it . each ( [ ...Object . values < string > ( Alg ) , 'CUSTOM' ] ) ( 'should issue %s signed credential if a valid state is passed in' , async ( alg : string ) => {
316+ jwtVerifyCallback . mockResolvedValue ( {
317+ did : 'did:example:1234' ,
318+ kid : 'did:example:1234#auth' ,
319+ alg : alg ,
320+ didDocument : {
321+ '@context' : 'https://www.w3.org/ns/did/v1' ,
322+ id : 'did:example:1234' ,
323+ } ,
324+ jwt : {
325+ header : {
326+ typ : 'openid4vci-proof+jwt' ,
327+ alg : alg ,
328+ kid : 'test-kid' ,
329+ } ,
330+ payload : {
331+ aud : IDENTIPROOF_ISSUER_URL ,
332+ iat : + new Date ( ) ,
333+ nonce : 'test-nonce' ,
334+ } ,
335+ }
336+ }
337+ )
338+
339+ let createdAt = + new Date ( )
340+ await vcIssuer . cNonces . set ( 'test-nonce' , {
341+ cNonce : 'test-nonce' ,
342+ preAuthorizedCode : 'test-pre-authorized-code' ,
343+ createdAt : createdAt
344+ } )
345+ await vcIssuer . credentialOfferSessions . set ( 'test-pre-authorized-code' , {
346+ createdAt : createdAt ,
347+ preAuthorizedCode : 'test-pre-authorized-code' ,
348+ credentialOffer : {
349+ credential_offer : {
350+ credential_issuer : 'did:key:test' ,
351+ credentials : [ ]
352+ }
353+ } ,
354+ lastUpdatedAt : createdAt ,
355+ status : IssueStatus . ACCESS_TOKEN_CREATED
356+ } )
357+
358+ expect (
293359 vcIssuer . issueCredential ( {
360+ credential : verifiableCredential ,
294361 credentialRequest : {
295362 types : [ 'VerifiableCredential' ] ,
296363 format : 'jwt_vc_json' ,
297364 proof : {
298365 proof_type : 'jwt' ,
299- jwt : 'ye.ye.ye' ,
300- } ,
366+ jwt : 'ye.ye.ye'
367+ }
301368 } ,
302- // issuerState,
303- } ) ,
369+ newCNonce : 'new-test-nonce'
370+ } )
304371 ) . resolves . toEqual ( {
305- c_nonce : expect . any ( String ) ,
306- c_nonce_expires_in : 90000 ,
372+ c_nonce : 'new-test-nonce' ,
373+ c_nonce_expires_in : 300000 ,
307374 credential : {
308375 '@context' : [ 'https://www.w3.org/2018/credentials/v1' ] ,
309376 credentialSubject : { } ,
@@ -314,11 +381,54 @@ describe('VcIssuer', () => {
314381 jwt : 'ye.ye.ye' ,
315382 proofPurpose : 'assertionMethod' ,
316383 type : 'JwtProof2020' ,
317- verificationMethod : 'sdfsdfasdfasdfasdfasdfassdfasdf' ,
384+ verificationMethod : 'sdfsdfasdfasdfasdfasdfassdfasdf'
318385 } ,
319- type : [ 'VerifiableCredential' ] ,
386+ type : [ 'VerifiableCredential' ]
320387 } ,
321- format : 'jwt_vc_json' ,
388+ format : 'jwt_vc_json'
389+ } )
390+ } )
391+
392+ it ( 'should fail issuing credential if the signing algorithm is missing' , async ( ) => {
393+ let createdAt = + new Date ( )
394+ await vcIssuer . cNonces . set ( 'test-nonce' , {
395+ cNonce : 'test-nonce' ,
396+ preAuthorizedCode : 'test-pre-authorized-code' ,
397+ createdAt : createdAt
322398 } )
399+
400+ jwtVerifyCallback . mockResolvedValue ( {
401+ did : 'did:example:1234' ,
402+ kid : 'did:example:1234#auth' ,
403+ alg : undefined ,
404+ didDocument : {
405+ '@context' : 'https://www.w3.org/ns/did/v1' ,
406+ id : 'did:example:1234' ,
407+ } ,
408+ jwt : {
409+ header : {
410+ typ : 'openid4vci-proof+jwt' ,
411+ alg : undefined ,
412+ kid : 'test-kid' ,
413+ } ,
414+ payload : {
415+ aud : IDENTIPROOF_ISSUER_URL ,
416+ iat : + new Date ( ) ,
417+ nonce : 'test-nonce' ,
418+ } ,
419+ }
420+ }
421+ )
422+
423+ expect ( vcIssuer . issueCredential ( {
424+ credentialRequest : {
425+ types : [ 'VerifiableCredential' ] ,
426+ format : 'jwt_vc_json' ,
427+ proof : {
428+ proof_type : 'jwt' ,
429+ jwt : 'ye.ye.ye' ,
430+ } ,
431+ } ,
432+ } ) ) . rejects . toThrow ( Error ( ALG_ERROR ) )
323433 } )
324434} )
0 commit comments