fix: align SMS expiry with PNVS valid time

This commit is contained in:
Codex
2026-07-04 00:47:36 +08:00
parent 0efdb0660c
commit 1c2ce38cea
3 changed files with 16 additions and 3 deletions

View File

@@ -29,6 +29,7 @@ export interface SmsSendResult {
provider: SmsProviderName;
status: 'sent' | 'mocked';
verification?: 'local' | 'provider';
ttlSeconds?: number;
providerMessageId?: string;
raw?: Record<string, unknown>;
}
@@ -242,6 +243,11 @@ function optionalIntegerString(config: TenantAuthProviderConfig, keys: string[],
return fallback;
}
function positiveInteger(value: string, fallback: number) {
const parsed = Number(value);
return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : fallback;
}
class AliyunPnvsSmsProvider implements SmsProvider {
readonly name = 'aliyun-pnvs' as const;
@@ -250,6 +256,7 @@ class AliyunPnvsSmsProvider implements SmsProvider {
async send(input: SmsSendInput): Promise<SmsSendResult> {
const signName = requirePublicString(this.providerConfig, ['signName'], 'SMS_PUBLIC_CONFIG_REQUIRED');
const templateCode = requirePublicString(this.providerConfig, ['templateCode'], 'SMS_PUBLIC_CONFIG_REQUIRED');
const validTime = optionalIntegerString(this.providerConfig, ['validTime'], String(input.ttlSeconds)) || String(input.ttlSeconds);
const params: Record<string, string> = {
CountryCode: optionalPublicString(this.providerConfig, ['countryCode']) || '86',
PhoneNumber: input.phone,
@@ -259,7 +266,7 @@ class AliyunPnvsSmsProvider implements SmsProvider {
OutId: input.outId,
CodeType: optionalIntegerString(this.providerConfig, ['codeType'], '1') || '1',
CodeLength: optionalIntegerString(this.providerConfig, ['codeLength'], '6') || '6',
ValidTime: optionalIntegerString(this.providerConfig, ['validTime'], String(input.ttlSeconds)) || String(input.ttlSeconds),
ValidTime: validTime,
DuplicatePolicy: optionalIntegerString(this.providerConfig, ['duplicatePolicy'], '1') || '1',
Interval: optionalIntegerString(this.providerConfig, ['interval'], String(input.cooldownSeconds)) || String(input.cooldownSeconds),
};
@@ -286,6 +293,7 @@ class AliyunPnvsSmsProvider implements SmsProvider {
provider: this.name,
status: 'sent',
verification: 'provider',
ttlSeconds: positiveInteger(validTime, input.ttlSeconds),
providerMessageId: typeof model.BizId === 'string' ? model.BizId : undefined,
raw: {
requestId: raw.RequestId,

View File

@@ -307,8 +307,11 @@ export async function sendSmsCodeRoute(ctx: RequestContext) {
cooldownSeconds: config.authSmsCooldownSeconds,
metadata,
});
const ttlSeconds = Number.isFinite(providerResult.ttlSeconds) && Number(providerResult.ttlSeconds) > 0
? Math.trunc(Number(providerResult.ttlSeconds))
: config.authCodeTtlSeconds;
const codeHash = hashSmsCode(tenantId, phone, purpose, code);
const expiresAt = new Date(Date.now() + config.authCodeTtlSeconds * 1000).toISOString();
const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
const item = await transaction(async client => {
const insertResult = await client.query(
@@ -354,7 +357,7 @@ export async function sendSmsCodeRoute(ctx: RequestContext) {
return {
item,
expireIn: config.authCodeTtlSeconds,
expireIn: ttlSeconds,
cooldown: config.authSmsCooldownSeconds,
debugCode: provider.name === 'mock' && !config.isProduction ? code : undefined,
};

View File

@@ -18,7 +18,9 @@ assert.match(providerSource, /SendSmsVerifyCode/, 'PNVS provider should call Sen
assert.match(providerSource, /CheckSmsVerifyCode/, 'PNVS provider should call CheckSmsVerifyCode');
assert.match(providerSource, /VerifyResult.*PASS/s, 'PNVS verify should require provider PASS result');
assert.match(providerSource, /##code##/, 'PNVS provider should preserve Aliyun-generated code placeholder');
assert.match(providerSource, /ttlSeconds:\s*positiveInteger\(validTime,\s*input\.ttlSeconds\)/, 'PNVS provider should expose provider ValidTime as send result ttlSeconds');
assert.match(routeSource, /aliyun-pnvs/, 'auth routes should recognize aliyun-pnvs aliases');
assert.match(routeSource, /const ttlSeconds =[\s\S]*providerResult\.ttlSeconds[\s\S]*expireIn: ttlSeconds/, 'auth routes should persist and return provider-specific SMS TTL');
assert.match(routeSource, /replace\(\s*\/\[_\\s\]\/g,\s*'-'\s*\)/, 'auth routes should normalize underscore PNVS provider aliases before runtime selection');
assert.match(routeSource, /verification.*provider/s, 'auth routes should store provider verification mode');
assert.match(routeSource, /provider\.verify/, 'auth routes should delegate PNVS verification to provider');