0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00
logto/packages/connectors/connector-aliyun-dm/src/utils.ts

66 lines
1.9 KiB
TypeScript
Raw Normal View History

import { type Optional } from '@silverhand/essentials';
import { got } from 'got';
import { createHmac, randomUUID } from 'node:crypto';
import type { PublicParameters } from './types.js';
// Aliyun has special escape rules.
// https://help.aliyun.com/document_detail/29442.html
const escaper = (string_: string) =>
encodeURIComponent(string_)
.replace(/!/g, '%21')
.replace(/"/g, '%22')
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/\*/g, '%2A')
.replace(/\+/g, '%2B');
// Format date string to 'YYYY-MM-DDThh:mm:ssZ' format.
const formatDateString = (date: Date) => {
const rawString = date.toISOString();
return rawString.replace(/\.\d{3}Z$/, 'Z'); // Trim milliseconds.
};
export const getSignature = (
parameters: Record<string, string>,
secret: string,
method: string
) => {
const canonicalizedQuery = Object.entries(parameters)
.map(([key, value]) => {
return `${escaper(key)}=${escaper(value)}`;
})
.slice()
.sort()
.join('&');
const stringToSign = `${method.toUpperCase()}&${escaper('/')}&${escaper(canonicalizedQuery)}`;
return createHmac('sha1', `${secret}&`).update(stringToSign).digest('base64');
};
export const request = async (
url: string,
parameters: PublicParameters & Record<string, string>,
accessKeySecret: string
) => {
const finalParameters = Object.entries<Optional<string>>({
...parameters,
SignatureNonce: randomUUID(),
Timestamp: formatDateString(new Date()),
}).reduce<Record<string, string>>(
(result, [key, value]) => (value === undefined ? result : { ...result, [key]: value }),
{}
);
const signature = getSignature(finalParameters, accessKeySecret, 'POST');
return got.post({
url,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
form: { ...finalParameters, Signature: signature },
});
};