Skip to content

Commit 3b4a735

Browse files
committed
chore: store authorization response in state
1 parent 7a221bb commit 3b4a735

4 files changed

Lines changed: 104 additions & 105 deletions

File tree

packages/client/lib/OpenID4VCIClient.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export interface OpenID4VCIClientState {
4646
endpointMetadata?: EndpointMetadataResult;
4747
accessTokenResponse?: AccessTokenResponse;
4848
authorizationRequestOpts?: AuthorizationRequestOpts;
49+
authorizationCodeResponse?: AuthorizationResponse;
4950
pkce: PKCEOpts;
5051
authorizationURL?: string;
5152
}
@@ -65,6 +66,7 @@ export class OpenID4VCIClient {
6566
endpointMetadata,
6667
accessTokenResponse,
6768
authorizationRequestOpts,
69+
authorizationCodeResponse,
6870
authorizationURL,
6971
}: {
7072
credentialOffer?: CredentialOfferRequestWithBaseUrl;
@@ -78,6 +80,7 @@ export class OpenID4VCIClient {
7880
endpointMetadata?: EndpointMetadataResult;
7981
accessTokenResponse?: AccessTokenResponse;
8082
authorizationRequestOpts?: AuthorizationRequestOpts;
83+
authorizationCodeResponse?: AuthorizationResponse;
8184
authorizationURL?: string;
8285
}) {
8386
const issuer = credentialIssuer ?? (credentialOffer ? getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) : undefined);
@@ -93,6 +96,7 @@ export class OpenID4VCIClient {
9396
clientId: clientId ?? (credentialOffer && getClientIdFromCredentialOfferPayload(credentialOffer.credential_offer)) ?? kid?.split('#')[0],
9497
pkce: { disabled: false, codeChallengeMethod: CodeChallengeMethod.S256, ...pkce },
9598
authorizationRequestOpts,
99+
authorizationCodeResponse,
96100
jwk,
97101
endpointMetadata,
98102
accessTokenResponse,
@@ -254,7 +258,12 @@ export class OpenID4VCIClient {
254258
}): Promise<AccessTokenResponse> {
255259
const { pin, clientId } = opts ?? {};
256260
let { redirectUri } = opts ?? {};
257-
const code = opts?.code ?? (opts?.authorizationResponse ? toAuthorizationResponsePayload(opts.authorizationResponse).code : undefined);
261+
if (opts?.authorizationResponse) {
262+
this._state.authorizationCodeResponse = { ...toAuthorizationResponsePayload(opts.authorizationResponse) };
263+
} else if (opts?.code) {
264+
this._state.authorizationCodeResponse = { code: opts.code };
265+
}
266+
const code = this._state.authorizationCodeResponse?.code;
258267

259268
if (opts?.codeVerifier) {
260269
this._state.pkce.codeVerifier = opts.codeVerifier;

packages/common/lib/types/OpenID4VCIErrors.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import { Alg } from './CredentialIssuance.types'
1+
import { Alg } from './CredentialIssuance.types';
22

33
export const BAD_PARAMS = 'Wrong parameters provided';
44
export const URL_NOT_VALID = 'Request url is not valid';
55
export const JWS_NOT_VALID = 'JWS is not valid';
66
export const PROOF_CANT_BE_CONSTRUCTED = "Proof can't be constructed.";
77
export const NO_JWT_PROVIDED = 'No JWT provided';
88
export const TYP_ERROR = 'Typ must be "openid4vci-proof+jwt"';
9-
export const ALG_ERROR = `Algorithm is a required field, you are free to use the signing algorithm of your choice or one of the following: ${Object.keys(Alg).join(', ')}`;
9+
export const ALG_ERROR = `Algorithm is a required field, you are free to use the signing algorithm of your choice or one of the following: ${Object.keys(
10+
Alg,
11+
).join(', ')}`;
1012
export const KID_JWK_X5C_ERROR = 'Only one must be present: kid, jwk or x5c';
1113
export const KID_DID_NO_DID_ERROR = 'A DID value needs to be returned when kid is present';
1214
export const DID_NO_DIDDOC_ERROR = 'A DID Document needs to be resolved when a DID is encountered';

packages/issuer/lib/VcIssuer.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,14 @@ import {
3232
toUniformCredentialOfferRequest,
3333
TYP_ERROR,
3434
UniformCredentialRequest,
35-
URIState
35+
URIState,
3636
} from '@sphereon/oid4vci-common'
3737
import { CompactSdJwtVc, CredentialMapper, W3CVerifiableCredential } from '@sphereon/ssi-types'
3838
import { v4 } from 'uuid'
3939

4040
import { assertValidPinNumber, createCredentialOfferObject, createCredentialOfferURIFromObject } from './functions'
4141
import { LookupStateManager } from './state-manager'
42-
import {
43-
CredentialDataSupplier,
44-
CredentialDataSupplierArgs,
45-
CredentialIssuanceInput,
46-
CredentialSignerCallback
47-
} from './types'
42+
import { CredentialDataSupplier, CredentialDataSupplierArgs, CredentialIssuanceInput, CredentialSignerCallback } from './types'
4843

4944
const SECOND = 1000
5045

packages/issuer/lib/__tests__/VcIssuer.spec.ts

Lines changed: 88 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
CredentialSupported,
77
IssuerCredentialSubjectDisplay,
88
IssueStatus,
9-
STATE_MISSING_ERROR
9+
STATE_MISSING_ERROR,
1010
} from '@sphereon/oid4vci-common'
1111
import { IProofPurpose, IProofType } from '@sphereon/ssi-types'
1212
import { DIDDocument } from 'did-resolver'
@@ -18,24 +18,18 @@ import { MemoryStates } from '../state-manager'
1818
const IDENTIPROOF_ISSUER_URL = 'https://issuer.research.identiproof.io'
1919

2020
const verifiableCredential = {
21-
'@context': [
22-
'https://www.w3.org/2018/credentials/v1',
23-
'https://w3id.org/security/suites/jws-2020/v1'
24-
],
21+
'@context': ['https://www.w3.org/2018/credentials/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
2522
id: 'http://university.example/credentials/1872',
26-
type: [
27-
'VerifiableCredential',
28-
'ExampleAlumniCredential'
29-
],
23+
type: ['VerifiableCredential', 'ExampleAlumniCredential'],
3024
issuer: 'https://university.example/issuers/565049',
3125
issuanceDate: new Date().toISOString(),
3226
credentialSubject: {
3327
id: 'did:example:ebfeb1f712ebc6f1c276e12ec21',
3428
alumniOf: {
3529
id: 'did:example:c276e12ec21ebfeb1f712ebc6f1',
36-
name: 'Example University'
37-
}
38-
}
30+
name: 'Example University',
31+
},
32+
},
3933
}
4034

4135
describe('VcIssuer', () => {
@@ -275,27 +269,26 @@ describe('VcIssuer', () => {
275269
// Of course this doesn't work. The state is part of the proof to begin with
276270
it('should fail issuing credential if an invalid state is used', async () => {
277271
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',
272+
did: 'did:example:1234',
273+
kid: 'did:example:1234#auth',
274+
alg: Alg.ES256K,
275+
didDocument: {
276+
'@context': 'https://www.w3.org/ns/did/v1',
277+
id: 'did:example:1234',
278+
},
279+
jwt: {
280+
header: {
281+
typ: 'openid4vci-proof+jwt',
282+
alg: Alg.ES256K,
283+
kid: 'test-kid',
284284
},
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-
)
285+
payload: {
286+
aud: IDENTIPROOF_ISSUER_URL,
287+
iat: +new Date(),
288+
nonce: 'test-nonce',
289+
},
290+
},
291+
})
299292

300293
await expect(
301294
vcIssuer.issueCredential({
@@ -314,45 +307,44 @@ describe('VcIssuer', () => {
314307

315308
it.each([...Object.values<string>(Alg), 'CUSTOM'])('should issue %s signed credential if a valid state is passed in', async (alg: string) => {
316309
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',
310+
did: 'did:example:1234',
311+
kid: 'did:example:1234#auth',
312+
alg: alg,
313+
didDocument: {
314+
'@context': 'https://www.w3.org/ns/did/v1',
315+
id: 'did:example:1234',
316+
},
317+
jwt: {
318+
header: {
319+
typ: 'openid4vci-proof+jwt',
320+
alg: alg,
321+
kid: 'test-kid',
323322
},
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-
)
323+
payload: {
324+
aud: IDENTIPROOF_ISSUER_URL,
325+
iat: +new Date(),
326+
nonce: 'test-nonce',
327+
},
328+
},
329+
})
338330

339-
let createdAt = +new Date()
331+
const createdAt = +new Date()
340332
await vcIssuer.cNonces.set('test-nonce', {
341333
cNonce: 'test-nonce',
342334
preAuthorizedCode: 'test-pre-authorized-code',
343-
createdAt: createdAt
335+
createdAt: createdAt,
344336
})
345337
await vcIssuer.credentialOfferSessions.set('test-pre-authorized-code', {
346338
createdAt: createdAt,
347339
preAuthorizedCode: 'test-pre-authorized-code',
348340
credentialOffer: {
349341
credential_offer: {
350342
credential_issuer: 'did:key:test',
351-
credentials: []
352-
}
343+
credentials: [],
344+
},
353345
},
354346
lastUpdatedAt: createdAt,
355-
status: IssueStatus.ACCESS_TOKEN_CREATED
347+
status: IssueStatus.ACCESS_TOKEN_CREATED,
356348
})
357349

358350
expect(
@@ -363,11 +355,11 @@ describe('VcIssuer', () => {
363355
format: 'jwt_vc_json',
364356
proof: {
365357
proof_type: 'jwt',
366-
jwt: 'ye.ye.ye'
367-
}
358+
jwt: 'ye.ye.ye',
359+
},
368360
},
369-
newCNonce: 'new-test-nonce'
370-
})
361+
newCNonce: 'new-test-nonce',
362+
}),
371363
).resolves.toEqual({
372364
c_nonce: 'new-test-nonce',
373365
c_nonce_expires_in: 300000,
@@ -381,54 +373,55 @@ describe('VcIssuer', () => {
381373
jwt: 'ye.ye.ye',
382374
proofPurpose: 'assertionMethod',
383375
type: 'JwtProof2020',
384-
verificationMethod: 'sdfsdfasdfasdfasdfasdfassdfasdf'
376+
verificationMethod: 'sdfsdfasdfasdfasdfasdfassdfasdf',
385377
},
386-
type: ['VerifiableCredential']
378+
type: ['VerifiableCredential'],
387379
},
388-
format: 'jwt_vc_json'
380+
format: 'jwt_vc_json',
389381
})
390382
})
391383

392384
it('should fail issuing credential if the signing algorithm is missing', async () => {
393-
let createdAt = +new Date()
385+
const createdAt = +new Date()
394386
await vcIssuer.cNonces.set('test-nonce', {
395387
cNonce: 'test-nonce',
396388
preAuthorizedCode: 'test-pre-authorized-code',
397-
createdAt: createdAt
389+
createdAt: createdAt,
398390
})
399391

400392
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',
393+
did: 'did:example:1234',
394+
kid: 'did:example:1234#auth',
395+
alg: undefined,
396+
didDocument: {
397+
'@context': 'https://www.w3.org/ns/did/v1',
398+
id: 'did:example:1234',
399+
},
400+
jwt: {
401+
header: {
402+
typ: 'openid4vci-proof+jwt',
403+
alg: undefined,
404+
kid: 'test-kid',
407405
},
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',
406+
payload: {
407+
aud: IDENTIPROOF_ISSUER_URL,
408+
iat: +new Date(),
409+
nonce: 'test-nonce',
430410
},
431411
},
432-
})).rejects.toThrow(Error(ALG_ERROR))
412+
})
413+
414+
expect(
415+
vcIssuer.issueCredential({
416+
credentialRequest: {
417+
types: ['VerifiableCredential'],
418+
format: 'jwt_vc_json',
419+
proof: {
420+
proof_type: 'jwt',
421+
jwt: 'ye.ye.ye',
422+
},
423+
},
424+
}),
425+
).rejects.toThrow(Error(ALG_ERROR))
433426
})
434427
})

0 commit comments

Comments
 (0)