-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
sign-and-post.ts
114 lines (103 loc) · 2.76 KB
/
sign-and-post.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
*
* This is a usage example of generate digest, sign and POST.
*
*/
import {
genDigestHeaderBothRFC3230AndRFC9530,
signAsDraftToRequest,
RequestLike,
RFC9421SignSource,
signAsRFC9421ToRequestOrResponse,
} from '@/index'; // REPLACE with '@misskey-dev/node-http-message-signatures'
import { jest } from '@jest/globals';
import { rsa4096 } from 'test/keys'; // for test
let fetch = jest.fn<typeof globalThis.fetch>(); // for test
/**
* PREPARE keyId - privateKeyPem Map
*/
const privateKeyMap = new Map([
['https://sender.example.com/users/0001#ed25519-key', rsa4096.privateKey],
]);
/**
* For RFC9421
* See https://datatracker.ietf.org/doc/html/rfc9421#create-sig-input
*
* @example
* [
* '@method',
* [
* '@query-param',
* { name: 'foo' },
* ],
* ]
*/
const identifiers = ['@method', '@authority', '@path', '@query', 'content-digest', 'accept'];
// For Draft
const includeHeaders = ['(request-target)', 'date', 'host', 'digest'];
export async function send(url: string | URL, body: string, keyId: string, rfc9421 = false) {
// Get private key
const privateKeyPem = privateKeyMap.get(keyId);
if (!privateKeyPem) {
throw new Error('No private key found');
}
// Prepare request
const u = new URL(url);
const request = {
headers: {
Date: (new Date()).toUTCString(),
Host: u.host,
Accept: '*/*',
},
method: 'POST',
url: u.href,
body,
} satisfies RequestLike;
// Generate SHA-256 digests and add them to the request header
await genDigestHeaderBothRFC3230AndRFC9530(request, body, 'SHA-256');
if (rfc9421) {
// Define sources
const sources = {
sig1: {
key: {
// Private key can be CryptoKey (`privateKey`) or PEM string (`privateKeyPem`)
privateKeyPem: privateKeyPem,
keyId: keyId,
},
defaults: {
ec: 'DSA',
hash: 'SHA-256',
},
identifiers,
created: Date.now(),
},
} satisfies Record<string, RFC9421SignSource>;
await signAsRFC9421ToRequestOrResponse(request, sources);
} else {
await signAsDraftToRequest(request, { keyId, privateKeyPem }, includeHeaders);
}
fetch(u, {
method: request.method,
headers: request.headers,
body,
});
}
describe('sign and post usage', () => {
beforeEach(() => {
fetch = jest.fn<typeof globalThis.fetch>();
});
test('rfc9421', async () => {
const url = 'https://receiver.example.com/foo';
const body = 'Hello, World!';
const keyId = 'https://sender.example.com/users/0001#ed25519-key';
await send(url, body, keyId, true);
expect(fetch).toHaveBeenCalledTimes(1);
});
test('draft', async () => {
const url = 'https://receiver.example.com/foo';
const body = 'Hello, World!';
const keyId = 'https://sender.example.com/users/0001#ed25519-key';
await send(url, body, keyId, false);
expect(fetch).toHaveBeenCalledTimes(1);
});
});