Skip to content

Commit 76d1b7b

Browse files
authored
Fetch-timeout (#269)
Signed-off-by: Mirko Mollik <[email protected]> Signed-off-by: Mirko Mollik <[email protected]>
1 parent 5e64551 commit 76d1b7b

File tree

2 files changed

+50
-32
lines changed

2 files changed

+50
-32
lines changed

packages/sd-jwt-vc/src/sd-jwt-vc-instance.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,10 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
167167
* @returns
168168
*/
169169
private async fetch<T>(url: string, integrity?: string): Promise<T> {
170-
const controller = new AbortController();
171-
const timeoutId = setTimeout(
172-
() => controller.abort(),
173-
this.userConfig.timeout ?? 10000,
174-
);
175-
176170
try {
177171
const response = await fetch(url, {
178-
signal: controller.signal,
172+
signal: AbortSignal.timeout(this.userConfig.timeout ?? 10000),
179173
});
180-
181174
if (!response.ok) {
182175
const errorText = await response.text();
183176
throw new Error(
@@ -187,12 +180,10 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
187180
await this.validateIntegrity(response.clone(), url, integrity);
188181
return response.json() as Promise<T>;
189182
} catch (error) {
190-
if (error instanceof DOMException && error.name === 'AbortError') {
183+
if ((error as Error).name === 'TimeoutError') {
191184
throw new Error(`Request to ${url} timed out`);
192185
}
193186
throw error;
194-
} finally {
195-
clearTimeout(timeoutId);
196187
}
197188
}
198189

packages/sd-jwt-vc/src/test/vct.spec.ts

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
22
import type { DisclosureFrame, Signer, Verifier } from '@sd-jwt/types';
3-
import { describe, test, beforeAll, afterAll } from 'vitest';
3+
import { describe, test, beforeAll, afterAll, expect } from 'vitest';
44
import { SDJwtVcInstance } from '..';
55
import type { SdJwtVcPayload } from '../sd-jwt-vc-payload';
66
import Crypto from 'node:crypto';
@@ -41,7 +41,7 @@ const restHandlers = [
4141
};
4242
return HttpResponse.json(res);
4343
}),
44-
http.get('http://exmaple.com/example', () => {
44+
http.get('http://example.com/example', () => {
4545
const res: TypeMetadataFormat = {
4646
vct: 'http://example.com/example',
4747
name: 'ExampleCredentialType',
@@ -53,6 +53,13 @@ const restHandlers = [
5353
};
5454
return HttpResponse.json(res);
5555
}),
56+
http.get('http://example.com/timeout', () => {
57+
return new Promise((resolve) => {
58+
setTimeout(() => {
59+
resolve(HttpResponse.json({}));
60+
}, 10000);
61+
});
62+
}),
5663
];
5764

5865
//this value could be generated on demand to make it easier when changing the values
@@ -62,7 +69,7 @@ const vctIntegrity =
6269
const server = setupServer(...restHandlers);
6370

6471
const iss = 'ExampleIssuer';
65-
const vct = 'http://exmaple.com/example';
72+
const vct = 'http://example.com/example';
6673
const iat = new Date().getTime() / 1000;
6774

6875
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
@@ -84,31 +91,33 @@ const createSignerVerifier = () => {
8491
};
8592

8693
describe('App', () => {
94+
const { signer, verifier } = createSignerVerifier();
95+
96+
const sdjwt = new SDJwtVcInstance({
97+
signer,
98+
signAlg: 'EdDSA',
99+
verifier,
100+
hasher: digest,
101+
hashAlg: 'sha-256',
102+
saltGenerator: generateSalt,
103+
loadTypeMetadataFormat: true,
104+
timeout: 1000,
105+
});
106+
107+
const claims = {
108+
firstname: 'John',
109+
};
110+
const disclosureFrame = {
111+
_sd: ['firstname'],
112+
};
113+
87114
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
88115

89116
afterAll(() => server.close());
90117

91118
afterEach(() => server.resetHandlers());
92119

93120
test('VCT Validation', async () => {
94-
const { signer, verifier } = createSignerVerifier();
95-
const sdjwt = new SDJwtVcInstance({
96-
signer,
97-
signAlg: 'EdDSA',
98-
verifier,
99-
hasher: digest,
100-
hashAlg: 'sha-256',
101-
saltGenerator: generateSalt,
102-
loadTypeMetadataFormat: true,
103-
});
104-
105-
const claims = {
106-
firstname: 'John',
107-
};
108-
const disclosureFrame = {
109-
_sd: ['firstname'],
110-
};
111-
112121
const expectedPayload: SdJwtVcPayload = {
113122
iat,
114123
iss,
@@ -124,5 +133,23 @@ describe('App', () => {
124133
await sdjwt.verify(encodedSdjwt);
125134
});
126135

136+
test('VCT Validation with timeout', async () => {
137+
const vct = 'http://example.com/timeout';
138+
const expectedPayload: SdJwtVcPayload = {
139+
iat,
140+
iss,
141+
vct,
142+
...claims,
143+
};
144+
const encodedSdjwt = await sdjwt.issue(
145+
expectedPayload,
146+
disclosureFrame as unknown as DisclosureFrame<SdJwtVcPayload>,
147+
);
148+
149+
expect(sdjwt.verify(encodedSdjwt)).rejects.toThrowError(
150+
`Request to ${vct} timed out`,
151+
);
152+
});
153+
127154
//TODO: we need tests with an embedded schema, extended and maybe also to test the errors when schema information is not available or the integrity is not valid
128155
});

0 commit comments

Comments
 (0)