@cfworker/cosmos
Version:
Azure Cosmos DB client for Cloudflare Workers and service workers
45 lines (44 loc) • 1.88 kB
JavaScript
import { base64 } from 'rfc4648';
export class Signer {
masterkey;
encoder = new TextEncoder();
constructor(masterkey) {
const promiseLike = crypto.subtle.importKey('raw', base64.parse(masterkey), { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign']);
this.masterkey = Promise.resolve(promiseLike);
}
async sign(request, date = new Date()) {
const key = await this.masterkey;
const payload = this.getPayload(request, date);
const hashed = await crypto.subtle.sign('HMAC', key, payload);
const signature = base64.stringify(new Uint8Array(hashed));
request.headers.set('authorization', encodeURIComponent(`type=master&ver=1.0&sig=${signature}`));
request.headers.set('x-ms-date', date.toUTCString());
}
getPayload(request, date) {
const url = new URL(request.url);
const { resourceType, resourceId } = this.pathnameToResource(url.pathname);
const method = request.method.toLowerCase();
const text = `${method}\n${resourceType}\n${resourceId}\n${date
.toUTCString()
.toLowerCase()}\n\n`;
return this.encoder.encode(text);
}
pathnameToResource(pathname) {
const segments = pathname.split('/');
if (segments.length % 2 === 0) {
const resourceType = segments[segments.length - 1];
const resourceId = segments.slice(1, segments.length - 1).join('/');
return { resourceType, resourceId };
}
const resourceType = segments[segments.length - 2];
const resourceId = segments.slice(1, segments.length).join('/');
return { resourceType, resourceId };
}
}
const signers = {};
export function getSigner(masterKey) {
if (!signers[masterKey]) {
signers[masterKey] = new Signer(masterKey);
}
return signers[masterKey];
}