mirror of
https://github.com/versia-pub/api.git
synced 2025-12-06 08:28:19 +01:00
feat(federation): ✨ Update signature code to Working Draft 4 of Versia
This commit is contained in:
parent
0ca7bb3bda
commit
a7e4092a93
|
|
@ -7,7 +7,7 @@ describe("SignatureValidator", () => {
|
||||||
let publicKey: CryptoKey;
|
let publicKey: CryptoKey;
|
||||||
let body: string;
|
let body: string;
|
||||||
let signature: string;
|
let signature: string;
|
||||||
let date: string;
|
let nonce: string;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const keys = await crypto.subtle.generateKey("Ed25519", true, [
|
const keys = await crypto.subtle.generateKey("Ed25519", true, [
|
||||||
|
|
@ -25,8 +25,8 @@ describe("SignatureValidator", () => {
|
||||||
"https://bob.org/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51",
|
"https://bob.org/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51",
|
||||||
).sign("GET", new URL("https://example.com"), body);
|
).sign("GET", new URL("https://example.com"), body);
|
||||||
|
|
||||||
signature = headers.get("Signature") ?? "";
|
signature = headers.get("X-Signature") ?? "";
|
||||||
date = headers.get("Date") ?? "";
|
nonce = headers.get("X-Nonce") ?? "";
|
||||||
});
|
});
|
||||||
|
|
||||||
test("fromStringKey", async () => {
|
test("fromStringKey", async () => {
|
||||||
|
|
@ -46,8 +46,8 @@ describe("SignatureValidator", () => {
|
||||||
const request = new Request("https://example.com", {
|
const request = new Request("https://example.com", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Signature: signature,
|
"X-Signature": signature,
|
||||||
Date: date,
|
"X-Nonce": nonce,
|
||||||
},
|
},
|
||||||
body: body,
|
body: body,
|
||||||
});
|
});
|
||||||
|
|
@ -55,47 +55,40 @@ describe("SignatureValidator", () => {
|
||||||
expect(isValid).toBe(true);
|
expect(isValid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should throw with an invalid signature", async () => {
|
test("should return false with an invalid signature", async () => {
|
||||||
const request = new Request("https://example.com", {
|
const request = new Request("https://example.com", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Signature: "invalid",
|
"X-Signature": "invalid",
|
||||||
Date: date,
|
"X-Nonce": nonce,
|
||||||
},
|
},
|
||||||
body: body,
|
body: body,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(() => validator.validate(request)).toThrow(TypeError);
|
const isValid = await validator.validate(request);
|
||||||
|
|
||||||
|
expect(isValid).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should throw with missing headers", async () => {
|
test("should throw with missing nonce", async () => {
|
||||||
const request = new Request("https://example.com", {
|
const request = new Request("https://example.com", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Signature: signature,
|
"X-Signature": signature,
|
||||||
},
|
},
|
||||||
body: body,
|
body: body,
|
||||||
});
|
});
|
||||||
expect(() => validator.validate(request)).toThrow(TypeError);
|
expect(() => validator.validate(request)).toThrow(
|
||||||
});
|
"Headers are missing in request: X-Nonce",
|
||||||
|
);
|
||||||
test("should throw with missing date", async () => {
|
|
||||||
const request = new Request("https://example.com", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
Signature: signature,
|
|
||||||
},
|
|
||||||
body: body,
|
|
||||||
});
|
|
||||||
expect(() => validator.validate(request)).toThrow(TypeError);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should not verify a valid signature with a different body", async () => {
|
test("should not verify a valid signature with a different body", async () => {
|
||||||
const request = new Request("https://example.com", {
|
const request = new Request("https://example.com", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Signature: signature,
|
"X-Signature": signature,
|
||||||
Date: date,
|
"X-Nonce": nonce,
|
||||||
},
|
},
|
||||||
body: "different",
|
body: "different",
|
||||||
});
|
});
|
||||||
|
|
@ -104,19 +97,19 @@ describe("SignatureValidator", () => {
|
||||||
expect(isValid).toBe(false);
|
expect(isValid).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should not verify a signature with a wrong key", async () => {
|
test("should throw if signature is not base64", async () => {
|
||||||
const request = new Request("https://example.com", {
|
const request = new Request("https://example.com", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Signature:
|
"X-Signature": "thisIsNotbase64OhNo$^ù",
|
||||||
'keyId="badbbadwrong",algorithm="ed25519",headers="(request-target) host date digest",signature="ohno"',
|
"X-Nonce": nonce,
|
||||||
Date: date,
|
|
||||||
},
|
},
|
||||||
body: body,
|
body: body,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isValid = await validator.validate(request);
|
expect(() => validator.validate(request)).toThrow(
|
||||||
expect(isValid).toBe(false);
|
"Signature is not valid base64",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -158,27 +151,11 @@ describe("SignatureConstructor", () => {
|
||||||
test("should correctly sign ", async () => {
|
test("should correctly sign ", async () => {
|
||||||
const url = new URL("https://example.com");
|
const url = new URL("https://example.com");
|
||||||
headers = (await ctor.sign("GET", url, body)).headers;
|
headers = (await ctor.sign("GET", url, body)).headers;
|
||||||
expect(headers.get("Signature")).toBeDefined();
|
expect(headers.get("X-Signature")).toBeDefined();
|
||||||
expect(headers.get("Date")).toBeDefined();
|
expect(headers.get("X-Nonce")).toBeDefined();
|
||||||
|
|
||||||
// Check structure of Signature
|
expect(headers.get("X-Nonce")?.length).toBeGreaterThan(10);
|
||||||
const signature = headers.get("Signature") ?? "";
|
expect(headers.get("X-Signature")?.length).toBeGreaterThan(10);
|
||||||
const parts = signature.split(",");
|
|
||||||
expect(parts).toHaveLength(4);
|
|
||||||
|
|
||||||
expect(parts[0].split("=")[0]).toBe("keyId");
|
|
||||||
expect(parts[1].split("=")[0]).toBe("algorithm");
|
|
||||||
expect(parts[2].split("=")[0]).toBe("headers");
|
|
||||||
expect(parts[3].split("=")[0]).toBe("signature");
|
|
||||||
|
|
||||||
expect(parts[0].split("=")[1]).toBe(
|
|
||||||
'"https://bob.org/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51"',
|
|
||||||
);
|
|
||||||
expect(parts[1].split("=")[1]).toBe('"ed25519"');
|
|
||||||
expect(parts[2].split("=")[1]).toBe(
|
|
||||||
'"(request-target) host date digest"',
|
|
||||||
);
|
|
||||||
expect(parts[3].split("=")[1]).toBeString();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should correctly sign a Request", async () => {
|
test("should correctly sign a Request", async () => {
|
||||||
|
|
@ -190,8 +167,8 @@ describe("SignatureConstructor", () => {
|
||||||
const { request: newRequest } = await ctor.sign(request);
|
const { request: newRequest } = await ctor.sign(request);
|
||||||
|
|
||||||
headers = newRequest.headers;
|
headers = newRequest.headers;
|
||||||
expect(headers.get("Signature")).toBeDefined();
|
expect(headers.get("X-Signature")).toBeDefined();
|
||||||
expect(headers.get("Date")).toBeDefined();
|
expect(headers.get("X-Nonce")).toBeDefined();
|
||||||
|
|
||||||
expect(await newRequest.text()).toBe(body);
|
expect(await newRequest.text()).toBe(body);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ const base64ToArrayBuffer = (base64: string) =>
|
||||||
const arrayBufferToBase64 = (arrayBuffer: ArrayBuffer) =>
|
const arrayBufferToBase64 = (arrayBuffer: ArrayBuffer) =>
|
||||||
btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
|
btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
|
||||||
|
|
||||||
|
const uint8ArrayToBase64 = (uint8Array: Uint8Array) =>
|
||||||
|
btoa(String.fromCharCode(...uint8Array));
|
||||||
|
|
||||||
const checkEvironmentSupport = () => {
|
const checkEvironmentSupport = () => {
|
||||||
// Check if WebCrypto is supported
|
// Check if WebCrypto is supported
|
||||||
if (!globalThis.crypto?.subtle) {
|
if (!globalThis.crypto?.subtle) {
|
||||||
|
|
@ -75,23 +78,23 @@ export class SignatureValidator {
|
||||||
/**
|
/**
|
||||||
* Validates the signature of a request.
|
* Validates the signature of a request.
|
||||||
* @param signature The signature string.
|
* @param signature The signature string.
|
||||||
* @param date The date that the request was signed.
|
* @param nonce Signature nonce.
|
||||||
* @param method The HTTP verb.
|
* @param method The HTTP verb.
|
||||||
* @param url The URL object.
|
* @param url The URL object.
|
||||||
* @param body The request body.
|
* @param body The request body.
|
||||||
* @returns A Promise that resolves to a boolean indicating whether the signature is valid.
|
* @returns A Promise that resolves to a boolean indicating whether the signature is valid.
|
||||||
* @throws TypeError if any required parameters are missing or empty.
|
* @throws TypeError if any required parameters are missing or empty.
|
||||||
* @example
|
* @example
|
||||||
* const signature = "keyId=\"https://example.com\",algorithm=\"ed25519\",headers=\"(request-target) host date digest\",signature=\"base64Signature\"";
|
* const signature = "k4QNt5Grl40KK8orIdiaq118Z+P5pa6vIeArq55wsvfL7wNy4cE3f2fhsGcpZql+PStm+x2ZjZIhudrAC/32Cg==";
|
||||||
* const date = new Date("2021-01-01T00:00:00.000Z");
|
* const nonce = "bJzyhTNK2RXUCetKIpm0Fw==";
|
||||||
* const method = "GET";
|
* const method = "GET";
|
||||||
* const url = new URL("https://example.com/users/ff54ee40-2ce9-4d2e-86ac-3cd06a1e1480");
|
* const url = new URL("https://example.com/users/ff54ee40-2ce9-4d2e-86ac-3cd06a1e1480");
|
||||||
* const body = "{ ... }";
|
* const body = "{ ... }";
|
||||||
* const isValid = await validator.validate(signature, date, method, url, body);
|
* const isValid = await validator.validate(signature, nonce, method, url, body);
|
||||||
*/
|
*/
|
||||||
async validate(
|
async validate(
|
||||||
signature: string,
|
signature: string,
|
||||||
date: Date,
|
nonce: string,
|
||||||
method: HttpVerb,
|
method: HttpVerb,
|
||||||
url: URL,
|
url: URL,
|
||||||
body: string,
|
body: string,
|
||||||
|
|
@ -99,28 +102,25 @@ export class SignatureValidator {
|
||||||
|
|
||||||
async validate(
|
async validate(
|
||||||
requestOrSignature: Request | string,
|
requestOrSignature: Request | string,
|
||||||
date?: Date,
|
nonce?: string,
|
||||||
method?: HttpVerb,
|
method?: HttpVerb,
|
||||||
url?: URL,
|
url?: URL,
|
||||||
body?: string,
|
body?: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (requestOrSignature instanceof Request) {
|
if (requestOrSignature instanceof Request) {
|
||||||
const signature = requestOrSignature.headers.get("Signature");
|
const signature = requestOrSignature.headers.get("X-Signature");
|
||||||
const date = requestOrSignature.headers.get("Date");
|
const nonce = requestOrSignature.headers.get("X-Nonce");
|
||||||
const url = new URL(requestOrSignature.url);
|
const url = new URL(requestOrSignature.url);
|
||||||
const body = await requestOrSignature.text();
|
const body = await requestOrSignature.text();
|
||||||
const method = requestOrSignature.method as HttpVerb;
|
const method = requestOrSignature.method as HttpVerb;
|
||||||
|
|
||||||
const missingHeaders = [
|
const missingHeaders = [
|
||||||
!signature && "Signature",
|
!signature && "X-Signature",
|
||||||
!date && "Date",
|
!nonce && "X-Nonce",
|
||||||
!method && "Method",
|
|
||||||
!url && "URL",
|
|
||||||
!body && "Body",
|
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
// Check if all headers are present
|
// Check if all headers are present
|
||||||
if (!(signature && date && method && url && body)) {
|
if (!(signature && nonce && method && url && body)) {
|
||||||
// Say which headers are missing
|
// Say which headers are missing
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
`Headers are missing in request: ${missingHeaders.join(
|
`Headers are missing in request: ${missingHeaders.join(
|
||||||
|
|
@ -129,49 +129,30 @@ export class SignatureValidator {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signature.split("signature=").length < 2) {
|
return this.validate(signature, nonce, method, url, body);
|
||||||
throw new TypeError(
|
|
||||||
"Invalid Signature header (wrong format or missing signature)",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const extractedSignature = signature
|
|
||||||
.split("signature=")[1]
|
|
||||||
.replace(/"/g, "");
|
|
||||||
|
|
||||||
if (!extractedSignature) {
|
|
||||||
throw new TypeError(
|
|
||||||
"Invalid Signature header (wrong format or missing signature)",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.validate(
|
|
||||||
extractedSignature,
|
|
||||||
new Date(date),
|
|
||||||
method as HttpVerb,
|
|
||||||
url,
|
|
||||||
body,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(date && method && url && body)) {
|
if (!(nonce && method && url && body)) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
"Missing or empty required parameters: date, method, url or body",
|
"Missing or empty required parameters: nonce, method, url or body",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const signature = requestOrSignature;
|
const signature = requestOrSignature;
|
||||||
|
|
||||||
|
// Check if signature is base64
|
||||||
|
try {
|
||||||
|
atob(signature);
|
||||||
|
} catch {
|
||||||
|
throw new TypeError("Signature is not valid base64");
|
||||||
|
}
|
||||||
|
|
||||||
const digest = await crypto.subtle.digest(
|
const digest = await crypto.subtle.digest(
|
||||||
"SHA-256",
|
"SHA-256",
|
||||||
new TextEncoder().encode(body),
|
new TextEncoder().encode(body),
|
||||||
);
|
);
|
||||||
|
|
||||||
const expectedSignedString =
|
const expectedSignedString = `${method.toLowerCase()} ${encodeURIComponent(url.pathname)} ${nonce} ${arrayBufferToBase64(digest)}`;
|
||||||
`(request-target): ${method.toLowerCase()} ${url.pathname}\n` +
|
|
||||||
`host: ${url.host}\n` +
|
|
||||||
`date: ${date.toISOString()}\n` +
|
|
||||||
`digest: SHA-256=${arrayBufferToBase64(digest)}\n`;
|
|
||||||
|
|
||||||
// Check if signed string is valid
|
// Check if signed string is valid
|
||||||
const isValid = await crypto.subtle.verify(
|
const isValid = await crypto.subtle.verify(
|
||||||
|
|
@ -193,15 +174,15 @@ export class SignatureConstructor {
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of SignatureConstructor.
|
* Creates a new instance of SignatureConstructor.
|
||||||
* @param privateKey The private key used for signature generation.
|
* @param privateKey The private key used for signature generation.
|
||||||
* @param keyId The key ID used for the Signature header.
|
* @param authorUri URI of the User who is signing the request.
|
||||||
* @example
|
* @example
|
||||||
* const privateKey = // CryptoKey
|
* const privateKey = // CryptoKey
|
||||||
* const keyId = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51";
|
* const authorUri = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51";
|
||||||
* const constructor = new SignatureConstructor(privateKey, keyId);
|
* const constructor = new SignatureConstructor(privateKey, authorUri);
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private privateKey: CryptoKey,
|
private privateKey: CryptoKey,
|
||||||
private keyId: string,
|
private authorUri: URL | string,
|
||||||
) {
|
) {
|
||||||
checkEvironmentSupport();
|
checkEvironmentSupport();
|
||||||
}
|
}
|
||||||
|
|
@ -209,16 +190,16 @@ export class SignatureConstructor {
|
||||||
/**
|
/**
|
||||||
* Creates a SignatureConstructor instance from a base64-encoded private key.
|
* Creates a SignatureConstructor instance from a base64-encoded private key.
|
||||||
* @param base64PrivateKey The base64-encoded private key.
|
* @param base64PrivateKey The base64-encoded private key.
|
||||||
* @param keyId The key ID used for the Signature header.
|
* @param authorUri URI of the User who is signing the request.
|
||||||
* @returns A Promise that resolves to a SignatureConstructor instance.
|
* @returns A Promise that resolves to a SignatureConstructor instance.
|
||||||
* @example
|
* @example
|
||||||
* const privateKey = "base64PrivateKey";
|
* const privateKey = "base64PrivateKey";
|
||||||
* const keyId = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51";
|
* const authorUri = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51";
|
||||||
* const constructor = await SignatureConstructor.fromStringKey(privateKey, keyId);
|
* const constructor = await SignatureConstructor.fromStringKey(privateKey, authorUri);
|
||||||
*/
|
*/
|
||||||
static async fromStringKey(
|
static async fromStringKey(
|
||||||
base64PrivateKey: string,
|
base64PrivateKey: string,
|
||||||
keyId: string,
|
authorUri: URL | string,
|
||||||
): Promise<SignatureConstructor> {
|
): Promise<SignatureConstructor> {
|
||||||
return new SignatureConstructor(
|
return new SignatureConstructor(
|
||||||
await crypto.subtle.importKey(
|
await crypto.subtle.importKey(
|
||||||
|
|
@ -228,7 +209,7 @@ export class SignatureConstructor {
|
||||||
false,
|
false,
|
||||||
["sign"],
|
["sign"],
|
||||||
),
|
),
|
||||||
keyId,
|
authorUri,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +232,7 @@ export class SignatureConstructor {
|
||||||
* @param url The URL object.
|
* @param url The URL object.
|
||||||
* @param body The request body.
|
* @param body The request body.
|
||||||
* @param headers The request headers.
|
* @param headers The request headers.
|
||||||
* @param date The date that the request was signed (optional)
|
* @param nonce The signature nonce (optional).
|
||||||
* @returns A Promise that resolves to the signed headers, and the signed string.
|
* @returns A Promise that resolves to the signed headers, and the signed string.
|
||||||
* @throws TypeError if any required parameters are missing or empty.
|
* @throws TypeError if any required parameters are missing or empty.
|
||||||
* @example
|
* @example
|
||||||
|
|
@ -265,7 +246,7 @@ export class SignatureConstructor {
|
||||||
url: URL,
|
url: URL,
|
||||||
body?: string,
|
body?: string,
|
||||||
headers?: Headers,
|
headers?: Headers,
|
||||||
date?: Date,
|
nonce?: string,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
signedString: string;
|
signedString: string;
|
||||||
|
|
@ -276,7 +257,7 @@ export class SignatureConstructor {
|
||||||
url?: URL,
|
url?: URL,
|
||||||
body?: string,
|
body?: string,
|
||||||
headers: Headers = new Headers(),
|
headers: Headers = new Headers(),
|
||||||
date?: Date,
|
nonce?: string,
|
||||||
): Promise<
|
): Promise<
|
||||||
| {
|
| {
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
@ -295,13 +276,14 @@ export class SignatureConstructor {
|
||||||
new URL(requestOrMethod.url),
|
new URL(requestOrMethod.url),
|
||||||
await requestOrMethod.text(),
|
await requestOrMethod.text(),
|
||||||
requestOrMethod.headers,
|
requestOrMethod.headers,
|
||||||
requestOrMethod.headers.get("Date")
|
requestOrMethod.headers.get("X-Nonce") ?? undefined,
|
||||||
? new Date(requestOrMethod.headers.get("Date") ?? "")
|
|
||||||
: undefined,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
request.headers.set("Date", headers.get("Date") ?? "");
|
request.headers.set("X-Nonce", headers.get("X-Nonce") ?? "");
|
||||||
request.headers.set("Signature", headers.get("Signature") ?? "");
|
request.headers.set(
|
||||||
|
"X-Signature",
|
||||||
|
headers.get("X-Signature") ?? "",
|
||||||
|
);
|
||||||
|
|
||||||
return { request, signedString };
|
return { request, signedString };
|
||||||
}
|
}
|
||||||
|
|
@ -312,20 +294,18 @@ export class SignatureConstructor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalDate = date?.toISOString() ?? new Date().toISOString();
|
const finalNonce =
|
||||||
|
nonce ||
|
||||||
|
uint8ArrayToBase64(crypto.getRandomValues(new Uint8Array(16)));
|
||||||
|
|
||||||
const digest = await crypto.subtle.digest(
|
const digest = await crypto.subtle.digest(
|
||||||
"SHA-256",
|
"SHA-256",
|
||||||
new TextEncoder().encode(body ?? ""),
|
new TextEncoder().encode(body ?? ""),
|
||||||
);
|
);
|
||||||
|
|
||||||
const signedString =
|
const signedString = `${requestOrMethod.toLowerCase()} ${encodeURIComponent(
|
||||||
`(request-target): ${requestOrMethod.toLowerCase()} ${
|
url.pathname,
|
||||||
url.pathname
|
)} ${finalNonce} ${arrayBufferToBase64(digest)}`;
|
||||||
}\n` +
|
|
||||||
`host: ${url.host}\n` +
|
|
||||||
`date: ${finalDate}\n` +
|
|
||||||
`digest: SHA-256=${arrayBufferToBase64(digest)}\n`;
|
|
||||||
|
|
||||||
const signature = await crypto.subtle.sign(
|
const signature = await crypto.subtle.sign(
|
||||||
"Ed25519",
|
"Ed25519",
|
||||||
|
|
@ -335,11 +315,9 @@ export class SignatureConstructor {
|
||||||
|
|
||||||
const signatureBase64 = arrayBufferToBase64(signature);
|
const signatureBase64 = arrayBufferToBase64(signature);
|
||||||
|
|
||||||
headers.set("Date", finalDate);
|
headers.set("X-Nonce", finalNonce);
|
||||||
headers.set(
|
headers.set("X-Signature", signatureBase64);
|
||||||
"Signature",
|
headers.set("X-Signed-By", this.authorUri.toString());
|
||||||
`keyId="${this.keyId}",algorithm="ed25519",headers="(request-target) host date digest",signature="${signatureBase64}"`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
headers,
|
headers,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.8.3",
|
"@biomejs/biome": "^1.8.3",
|
||||||
"@types/bun": "^1.1.6",
|
"@types/bun": "^1.1.7",
|
||||||
|
"@types/node": "^22.5.0",
|
||||||
"bun-plugin-dts": "^0.2.3"
|
"bun-plugin-dts": "^0.2.3"
|
||||||
},
|
},
|
||||||
"trustedDependencies": ["@biomejs/biome"],
|
"trustedDependencies": ["@biomejs/biome"],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue