mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
fix(connector): signature method for both Aliyun DM and SMS (#3920)
This commit is contained in:
parent
f35d1cbb86
commit
64af2dc88e
3 changed files with 46 additions and 27 deletions
6
.changeset/odd-pumpkins-poke.md
Normal file
6
.changeset/odd-pumpkins-poke.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
"@logto/connector-aliyun-dm": patch
|
||||||
|
"@logto/connector-aliyun-sms": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix Aliyun Direct Mail and Aliyun Short Message Service connectors' signature functions.
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { type Optional } from '@silverhand/essentials';
|
||||||
import { got } from 'got';
|
import { got } from 'got';
|
||||||
import { createHmac } from 'node:crypto';
|
import { createHmac, randomUUID } from 'node:crypto';
|
||||||
|
|
||||||
import type { PublicParameters } from './types.js';
|
import type { PublicParameters } from './types.js';
|
||||||
|
|
||||||
|
@ -7,26 +8,29 @@ import type { PublicParameters } from './types.js';
|
||||||
// https://help.aliyun.com/document_detail/29442.html
|
// https://help.aliyun.com/document_detail/29442.html
|
||||||
const escaper = (string_: string) =>
|
const escaper = (string_: string) =>
|
||||||
encodeURIComponent(string_)
|
encodeURIComponent(string_)
|
||||||
.replace(/\*/g, '%2A')
|
|
||||||
.replace(/'/g, '%27')
|
|
||||||
.replace(/!/g, '%21')
|
.replace(/!/g, '%21')
|
||||||
.replace(/"/g, '%22')
|
.replace(/"/g, '%22')
|
||||||
|
.replace(/'/g, '%27')
|
||||||
.replace(/\(/g, '%28')
|
.replace(/\(/g, '%28')
|
||||||
.replace(/\)/g, '%29')
|
.replace(/\)/g, '%29')
|
||||||
|
.replace(/\*/g, '%2A')
|
||||||
.replace(/\+/g, '%2B');
|
.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 = (
|
export const getSignature = (
|
||||||
parameters: Record<string, string>,
|
parameters: Record<string, string>,
|
||||||
secret: string,
|
secret: string,
|
||||||
method: string
|
method: string
|
||||||
) => {
|
) => {
|
||||||
const canonicalizedQuery = Object.keys(parameters)
|
const canonicalizedQuery = Object.entries(parameters)
|
||||||
.map((key) => {
|
.map(([key, value]) => {
|
||||||
const value = parameters[key];
|
return `${escaper(key)}=${escaper(value)}`;
|
||||||
|
|
||||||
return value === undefined ? '' : `${escaper(key)}=${escaper(value)}`;
|
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
|
||||||
.slice()
|
.slice()
|
||||||
.sort()
|
.sort()
|
||||||
.join('&');
|
.join('&');
|
||||||
|
@ -41,11 +45,14 @@ export const request = async (
|
||||||
parameters: PublicParameters & Record<string, string>,
|
parameters: PublicParameters & Record<string, string>,
|
||||||
accessKeySecret: string
|
accessKeySecret: string
|
||||||
) => {
|
) => {
|
||||||
const finalParameters: Record<string, string> = {
|
const finalParameters = Object.entries<Optional<string>>({
|
||||||
...parameters,
|
...parameters,
|
||||||
SignatureNonce: String(Math.random()),
|
SignatureNonce: randomUUID(),
|
||||||
Timestamp: new Date().toISOString(),
|
Timestamp: formatDateString(new Date()),
|
||||||
};
|
}).reduce<Record<string, string>>(
|
||||||
|
(result, [key, value]) => (value === undefined ? result : { ...result, [key]: value }),
|
||||||
|
{}
|
||||||
|
);
|
||||||
const signature = getSignature(finalParameters, accessKeySecret, 'POST');
|
const signature = getSignature(finalParameters, accessKeySecret, 'POST');
|
||||||
|
|
||||||
return got.post({
|
return got.post({
|
||||||
|
|
|
@ -1,32 +1,35 @@
|
||||||
|
import { type Optional } from '@silverhand/essentials';
|
||||||
import { got } from 'got';
|
import { got } from 'got';
|
||||||
import { createHmac } from 'node:crypto';
|
import { createHmac, randomUUID } from 'node:crypto';
|
||||||
|
|
||||||
import type { PublicParameters } from './types.js';
|
import type { PublicParameters } from './types.js';
|
||||||
|
|
||||||
// Aliyun has special escape rules.
|
// Aliyun has special escape rules.
|
||||||
// https://help.aliyun.com/document_detail/29442.html
|
// https://help.aliyun.com/document_detail/29442.html
|
||||||
const escaper = (string_: string) =>
|
const escaper = (string_: string) =>
|
||||||
encodeURIComponent(string_)
|
encodeURIComponent(string_)
|
||||||
.replace(/\*/g, '%2A')
|
|
||||||
.replace(/'/g, '%27')
|
|
||||||
.replace(/!/g, '%21')
|
.replace(/!/g, '%21')
|
||||||
.replace(/"/g, '%22')
|
.replace(/"/g, '%22')
|
||||||
|
.replace(/'/g, '%27')
|
||||||
.replace(/\(/g, '%28')
|
.replace(/\(/g, '%28')
|
||||||
.replace(/\)/g, '%29')
|
.replace(/\)/g, '%29')
|
||||||
|
.replace(/\*/g, '%2A')
|
||||||
.replace(/\+/g, '%2B');
|
.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 = (
|
export const getSignature = (
|
||||||
parameters: Record<string, string>,
|
parameters: Record<string, string>,
|
||||||
secret: string,
|
secret: string,
|
||||||
method: string
|
method: string
|
||||||
) => {
|
) => {
|
||||||
const canonicalizedQuery = Object.keys(parameters)
|
const canonicalizedQuery = Object.entries(parameters)
|
||||||
.map((key) => {
|
.map(([key, value]) => {
|
||||||
const value = parameters[key];
|
return `${escaper(key)}=${escaper(value)}`;
|
||||||
|
|
||||||
return value === undefined ? '' : `${escaper(key)}=${escaper(value)}`;
|
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
|
||||||
.slice()
|
.slice()
|
||||||
.sort()
|
.sort()
|
||||||
.join('&');
|
.join('&');
|
||||||
|
@ -41,11 +44,14 @@ export const request = async (
|
||||||
parameters: PublicParameters & Record<string, string>,
|
parameters: PublicParameters & Record<string, string>,
|
||||||
accessKeySecret: string
|
accessKeySecret: string
|
||||||
) => {
|
) => {
|
||||||
const finalParameters: Record<string, string> = {
|
const finalParameters = Object.entries<Optional<string>>({
|
||||||
...parameters,
|
...parameters,
|
||||||
SignatureNonce: String(Math.random()),
|
SignatureNonce: randomUUID(),
|
||||||
Timestamp: new Date().toISOString(),
|
Timestamp: formatDateString(new Date()),
|
||||||
};
|
}).reduce<Record<string, string>>(
|
||||||
|
(result, [key, value]) => (value === undefined ? result : { ...result, [key]: value }),
|
||||||
|
{}
|
||||||
|
);
|
||||||
const signature = getSignature(finalParameters, accessKeySecret, 'POST');
|
const signature = getSignature(finalParameters, accessKeySecret, 'POST');
|
||||||
|
|
||||||
return got.post({
|
return got.post({
|
||||||
|
|
Loading…
Reference in a new issue