← Public packages
@kody/alexa
Alexa custom skill webhook into Kody. Checks the skill id, maps common requests, and leaves utterance forwarding unwired.
src/verify.ts
70 lines · 2.2 KB · TypeScriptimport { 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<VerifyAlexaRequestResult> {
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),
},
}
}