import { boolean, object, optional, parse, string } from 'remix/data-schema' const verifyInput = object( { signatureCertChainUrl: optional(string()), signature: optional(string()), body: optional(string()), dryRun: optional(boolean()), allowUnsigned: optional(boolean()), }, { unknownKeys: 'error' }, ) const ALEXA_SIGNATURE_DOCS = [ 'https://developer.amazon.com/en-US/docs/alexa/custom-skills/host-a-custom-skill-as-a-web-service.html#verifying-that-the-request-was-sent-by-alexa', 'https://developer.amazon.com/en-US/docs/alexa/custom-skills/request-and-response-json-reference.html', ] as const export type VerifyAlexaRequestResult = { verified: false implemented: false skipped: true unsafeForProduction: true devBypass: boolean reason: string docs: string[] checked: { hasSignatureCertChainUrl: boolean hasSignature: boolean hasBody: boolean } } /** * Report Alexa request-signature status without performing crypto. * Use before treating a skill request as authentic. This head-start stub * always skips SignatureCertChainUrl verification. * * @param input - Optional SignatureCertChainUrl header, Signature header, raw body, and a loud dev flag * @returns A skipped, unsafe-for-production result. `verified` is always false. * * @example * import verifyAlexaRequest from 'kody:@kody/alexa/verify' * * const status = await verifyAlexaRequest({ dryRun: true }) */ export default async function verifyAlexaRequest( raw: unknown = {}, ): Promise { const input = parse(verifyInput, raw) const devBypass = input.dryRun === true || input.allowUnsigned === true const reason = devBypass ? 'Signature verification is not implemented. dryRun or allowUnsigned is set. That flag is unsafe for production.' : 'Signature verification is not implemented. This stub does not validate SignatureCertChainUrl or Signature. Unsafe for production.' return { verified: false, implemented: false, skipped: true, unsafeForProduction: true, devBypass, reason, docs: [...ALEXA_SIGNATURE_DOCS], checked: { hasSignatureCertChainUrl: Boolean(input.signatureCertChainUrl?.trim()), hasSignature: Boolean(input.signature?.trim()), hasBody: Boolean(input.body), }, } }