@@ -8,12 +8,16 @@ import { VcIssuer } from '../VcIssuer'
88import { AuthorizationServerMetadataBuilder , CredentialSupportedBuilderV1_15 , VcIssuerBuilder } from '../builder'
99import { MemoryStates } from '../state-manager'
1010import {
11- Alg , ALG_ERROR ,
11+ Alg ,
12+ ALG_ERROR ,
1213 AuthorizationDetailsV1_0_15 ,
1314 CredentialConfigurationSupportedV1_0_15 ,
1415 CredentialOfferSession ,
15- IssueStatus , STATE_MISSING_ERROR
16+ GrantTypes ,
17+ IssueStatus ,
18+ STATE_MISSING_ERROR
1619} from '@sphereon/oid4vci-common'
20+ import { createAccessTokenResponse } from '../tokens'
1721
1822const IDENTIPROOF_ISSUER_URL = 'https://issuer.research.identiproof.io'
1923
@@ -334,6 +338,105 @@ describe('VcIssuer', () => {
334338 expect ( typeof result . credentials [ 0 ] . credential ) . toBe ( 'object' )
335339 } )
336340
341+ it ( 'should generate credential_identifiers in token response and accept them in credential request' , async ( ) => {
342+ const createdAt = + new Date ( )
343+
344+ // Setup session with authorization_details
345+ const authorizationDetails : AuthorizationDetailsV1_0_15 [ ] = [ {
346+ type : 'openid_credential' ,
347+ credential_configuration_id : 'UniversityDegree_JWT' ,
348+ format : 'jwt_vc_json' as const ,
349+ types : [ 'VerifiableCredential' , 'UniversityDegree_JWT' ]
350+ } ]
351+
352+ await vcIssuer . credentialOfferSessions . set ( 'test-pre-authorized-code' , {
353+ createdAt : createdAt ,
354+ notification_id : '43243' ,
355+ preAuthorizedCode : 'test-pre-authorized-code' ,
356+ credentialOffer : {
357+ credential_offer : {
358+ credential_issuer : 'did:key:test' ,
359+ credential_configuration_ids : [ 'UniversityDegree_JWT' ]
360+ }
361+ } ,
362+ authorizationDetails : authorizationDetails ,
363+ lastUpdatedAt : createdAt ,
364+ status : IssueStatus . ACCESS_TOKEN_CREATED
365+ } )
366+
367+ const tokenResponse = await createAccessTokenResponse ( {
368+ grant_type : GrantTypes . PRE_AUTHORIZED_CODE ,
369+ 'pre-authorized_code' : 'test-pre-authorized-code'
370+ } , {
371+ credentialOfferSessions : vcIssuer . credentialOfferSessions ,
372+ cNonces : vcIssuer . cNonces ,
373+ tokenExpiresIn : 300 ,
374+ accessTokenSignerCallback : async ( ) => 'mock-access-token' ,
375+ accessTokenIssuer : 'test-issuer'
376+ } )
377+
378+ // Verify token response includes authorization_details with generated credential_identifiers
379+ expect ( tokenResponse . authorization_details ) . toBeDefined ( )
380+ expect ( tokenResponse . authorization_details ) . toHaveLength ( 1 )
381+ expect ( tokenResponse . authorization_details ! [ 0 ] ) . toHaveProperty ( 'credential_identifiers' )
382+ expect ( tokenResponse . authorization_details ! [ 0 ] . credential_identifiers ) . toHaveLength ( 1 )
383+
384+ const generatedIdentifier = tokenResponse . authorization_details ! [ 0 ] . credential_identifiers ! [ 0 ]
385+ expect ( generatedIdentifier ) . toMatch ( / U n i v e r s i t y D e g r e e _ J W T _ / )
386+
387+ // Step 2: Mock JWT verification for credential request
388+ jwtVerifyCallback . mockResolvedValue ( {
389+ did : 'did:example:1234' ,
390+ kid : 'did:example:1234#auth' ,
391+ alg : Alg . ES256K ,
392+ didDocument : {
393+ '@context' : 'https://www.w3.org/ns/did/v1' ,
394+ id : 'did:example:1234'
395+ } ,
396+ jwt : {
397+ header : {
398+ typ : 'openid4vci-proof+jwt' ,
399+ alg : Alg . ES256K ,
400+ kid : 'test-kid'
401+ } ,
402+ payload : {
403+ aud : IDENTIPROOF_ISSUER_URL ,
404+ iat : + new Date ( ) / 1000 ,
405+ nonce : 'test-nonce' ,
406+ // Include authorization_details from token response
407+ authorization_details : tokenResponse . authorization_details
408+ }
409+ }
410+ } )
411+
412+ await vcIssuer . cNonces . set ( 'test-nonce' , {
413+ cNonce : 'test-nonce' ,
414+ createdAt : createdAt
415+ } )
416+
417+ // Step 3: Use generated credential_identifier in credential request
418+ const credentialResult = await vcIssuer . issueCredential ( {
419+ credential : verifiableCredential ,
420+ credentialRequest : {
421+ credential_identifier : generatedIdentifier ,
422+ proof : {
423+ proof_type : 'jwt' ,
424+ jwt : 'ye.ye.ye'
425+ }
426+ } as any ,
427+ issuerCorrelation : {
428+ preAuthorizedCode : 'test-pre-authorized-code' ,
429+ authorizationDetails : tokenResponse . authorization_details
430+ } ,
431+ newCNonce : 'new-test-nonce'
432+ } )
433+
434+ // Verify credential was issued successfully
435+ expect ( credentialResult . credentials ) . toHaveLength ( 1 )
436+ expect ( credentialResult . credentials [ 0 ] . credential ) . toBeDefined ( )
437+ expect ( credentialResult . notification_id ) . toBe ( '43243' )
438+ } )
439+
337440 it ( 'should reject invalid credential_identifier' , async ( ) => {
338441 jwtVerifyCallback . mockResolvedValue ( {
339442 did : 'did:example:1234' ,
0 commit comments