11import {
2+ acquireDeferredCredential ,
23 CredentialResponse ,
34 getCredentialRequestForVersion ,
45 getUniformFormat ,
6+ isDeferredCredentialResponse ,
57 OID4VCICredentialFormat ,
68 OpenId4VCIVersion ,
79 OpenIDResponse ,
@@ -19,25 +21,56 @@ import { isValidURL, post } from './functions';
1921const debug = Debug ( 'sphereon:oid4vci:credential' ) ;
2022
2123export interface CredentialRequestOpts {
24+ deferredCredentialAwait ?: boolean ;
25+ deferredCredentialIntervalInMS ?: number ;
2226 credentialEndpoint : string ;
27+ deferredCredentialEndpoint ?: string ;
2328 credentialTypes : string [ ] ;
2429 format ?: CredentialFormat | OID4VCICredentialFormat ;
2530 proof : ProofOfPossession ;
2631 token : string ;
2732 version : OpenId4VCIVersion ;
2833}
2934
35+ export async function buildProof < DIDDoc > (
36+ proofInput : ProofOfPossessionBuilder < DIDDoc > | ProofOfPossession ,
37+ opts : {
38+ version : OpenId4VCIVersion ;
39+ cNonce ?: string ;
40+ } ,
41+ ) {
42+ if ( 'proof_type' in proofInput ) {
43+ if ( opts . cNonce ) {
44+ throw Error ( `Cnonce param is only supported when using a Proof of Posession builder` ) ;
45+ }
46+ return await ProofOfPossessionBuilder . fromProof ( proofInput as ProofOfPossession , opts . version ) . build ( ) ;
47+ }
48+ if ( opts . cNonce ) {
49+ proofInput . withAccessTokenNonce ( opts . cNonce ) ;
50+ }
51+ return await proofInput . build ( ) ;
52+ }
53+
3054export class CredentialRequestClient {
3155 private readonly _credentialRequestOpts : Partial < CredentialRequestOpts > ;
56+ private _isDeferred = false ;
3257
3358 get credentialRequestOpts ( ) : CredentialRequestOpts {
3459 return this . _credentialRequestOpts as CredentialRequestOpts ;
3560 }
3661
62+ public isDeferred ( ) : boolean {
63+ return this . _isDeferred ;
64+ }
65+
3766 public getCredentialEndpoint ( ) : string {
3867 return this . credentialRequestOpts . credentialEndpoint ;
3968 }
4069
70+ public getDeferredCredentialEndpoint ( ) : string | undefined {
71+ return this . credentialRequestOpts . deferredCredentialEndpoint ;
72+ }
73+
4174 public constructor ( builder : CredentialRequestClientBuilder ) {
4275 this . _credentialRequestOpts = { ...builder } ;
4376 }
@@ -63,11 +96,40 @@ export class CredentialRequestClient {
6396 debug ( `Acquiring credential(s) from: ${ credentialEndpoint } ` ) ;
6497 debug ( `request\n: ${ JSON . stringify ( request , null , 2 ) } ` ) ;
6598 const requestToken : string = this . credentialRequestOpts . token ;
66- const response : OpenIDResponse < CredentialResponse > = await post ( credentialEndpoint , JSON . stringify ( request ) , { bearerToken : requestToken } ) ;
99+ let response : OpenIDResponse < CredentialResponse > = await post ( credentialEndpoint , JSON . stringify ( request ) , { bearerToken : requestToken } ) ;
100+ this . _isDeferred = isDeferredCredentialResponse ( response ) ;
101+ if ( this . isDeferred ( ) && this . credentialRequestOpts . deferredCredentialAwait && response . successBody ) {
102+ response = await this . acquireDeferredCredential ( response . successBody , { bearerToken : this . credentialRequestOpts . token } ) ;
103+ }
104+
67105 debug ( `Credential endpoint ${ credentialEndpoint } response:\r\n${ JSON . stringify ( response , null , 2 ) } ` ) ;
68106 return response ;
69107 }
70108
109+ public async acquireDeferredCredential (
110+ response : Pick < CredentialResponse , 'transaction_id' | 'acceptance_token' | 'c_nonce' > ,
111+ opts ?: {
112+ bearerToken ?: string ;
113+ } ,
114+ ) : Promise < OpenIDResponse < CredentialResponse > > {
115+ const transactionId = response . transaction_id ;
116+ const bearerToken = response . acceptance_token ?? opts ?. bearerToken ;
117+ const deferredCredentialEndpoint = this . getDeferredCredentialEndpoint ( ) ;
118+ if ( ! deferredCredentialEndpoint ) {
119+ throw Error ( `No deferred credential endpoint supplied.` ) ;
120+ } else if ( ! bearerToken ) {
121+ throw Error ( `No bearer token present and refresh for defered endpoint not supported yet` ) ;
122+ // todo updated bearer token with new c_nonce
123+ }
124+ return await acquireDeferredCredential ( {
125+ bearerToken,
126+ transactionId,
127+ deferredCredentialEndpoint,
128+ deferredCredentialAwait : this . credentialRequestOpts . deferredCredentialAwait ,
129+ deferredCredentialIntervalInMS : this . credentialRequestOpts . deferredCredentialIntervalInMS ,
130+ } ) ;
131+ }
132+
71133 public async createCredentialRequest < DIDDoc > ( opts : {
72134 proofInput : ProofOfPossessionBuilder < DIDDoc > | ProofOfPossession ;
73135 credentialTypes ?: string | string [ ] ;
@@ -93,11 +155,7 @@ export class CredentialRequestClient {
93155 else if ( ! this . isV11OrHigher ( ) && types . length !== 1 ) {
94156 throw Error ( 'Only a single credential type is supported for V8/V9' ) ;
95157 }
96-
97- const proof =
98- 'proof_type' in proofInput
99- ? await ProofOfPossessionBuilder . fromProof ( proofInput as ProofOfPossession , opts . version ) . build ( )
100- : await proofInput . build ( ) ;
158+ const proof = await buildProof ( proofInput , opts ) ;
101159
102160 // TODO: we should move format specific logic
103161 if ( format === 'jwt_vc_json' || format === 'jwt_vc' ) {
0 commit comments