feat(taobao): add native mini program with shared backend
Preserve H5 flows while adding Taobao identity, native capture, reports and isolated local development. Fix canceled province selection and expired-session logout. Record passing local regressions, two pre-existing H5 assertion failures and pending platform validation.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
const crypto = require('node:crypto');
|
||||
|
||||
// Cloud gateway query parameters + the exact, unread JSON body. Algorithm:
|
||||
// https://developer.alibaba.com/docs/doc.htm?articleId=121746&docType=1&treeId=832
|
||||
function signature(params, body, secret) {
|
||||
const method = params.sign_method || '';
|
||||
if (!['', 'md5', 'hmac', 'hmac_md5', 'hmac_sha256'].includes(method)) return null;
|
||||
const content = Object.keys(params).filter(key => key !== 'sign' && params[key] !== '').sort()
|
||||
.map(key => key + params[key]).join('') + body;
|
||||
if (method === 'hmac' || method === 'hmac_md5' || method === 'hmac_sha256') {
|
||||
return crypto.createHmac(method === 'hmac_sha256' ? 'sha256' : 'md5', secret).update(content).digest('hex').toUpperCase();
|
||||
}
|
||||
return crypto.createHash('md5').update((method === 'md5' ? secret : '') + content + secret).digest('hex').toUpperCase();
|
||||
}
|
||||
function verify(url, body, env = process.env) {
|
||||
if (!env.TAOBAO_APP_KEY || !env.TAOBAO_APP_SECRET) return null;
|
||||
const params = Object.create(null);
|
||||
for (const [key, value] of new URL(url, 'http://localhost').searchParams) {
|
||||
if (key in params) return null; // Never ambiguously verify duplicate parameters.
|
||||
params[key] = value;
|
||||
}
|
||||
if (params.app_key !== env.TAOBAO_APP_KEY || !/^[A-Za-z0-9_-]{4,256}$/.test(params.open_id || '') ||
|
||||
!/^[A-Fa-f0-9]{32,64}$/.test(params.sign || '')) return null;
|
||||
const expected = signature(params, body, env.TAOBAO_APP_SECRET);
|
||||
if (!expected || expected.length !== params.sign.length ||
|
||||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(params.sign.toUpperCase()))) return null;
|
||||
// Login challenge is single-use and signed inside the body, preventing replay.
|
||||
return { providerKey: crypto.createHash('sha256').update(params.app_key + ':' + params.open_id).digest('hex') };
|
||||
}
|
||||
module.exports = { signature, verify };
|
||||
Reference in New Issue
Block a user