From ea25ada1e499f5be5efa9346a250eadb0ba33bd2 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Tue, 3 Mar 2026 19:37:05 +0100 Subject: [PATCH 1/2] feat(lambda-events): support X509 custom authorizer in IoT events Make IoTCoreMqttContext.password optional to support X509/mutual TLS custom authorizers where AWS IoT does not send username or password fields. Add x509_certificate_pem and principal_id fields to IoTCoreTlsContext for X509 certificate-based authentication. Add test fixture and test for the X509 authorizer payload. --- lambda-events/Cargo.toml | 2 +- lambda-events/src/event/iot/mod.rs | 18 ++++++++++++++++ .../example-iot-custom-auth-request-x509.json | 21 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 lambda-events/src/fixtures/example-iot-custom-auth-request-x509.json diff --git a/lambda-events/Cargo.toml b/lambda-events/Cargo.toml index dd04460f..41885e52 100644 --- a/lambda-events/Cargo.toml +++ b/lambda-events/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws_lambda_events" -version = "1.0.3" +version = "1.1.0" rust-version = "1.84.0" description = "AWS Lambda event definitions" authors = [ diff --git a/lambda-events/src/event/iot/mod.rs b/lambda-events/src/event/iot/mod.rs index 81123b6e..3cc094a4 100644 --- a/lambda-events/src/event/iot/mod.rs +++ b/lambda-events/src/event/iot/mod.rs @@ -54,6 +54,10 @@ pub struct IoTCoreProtocolData { pub struct IoTCoreTlsContext { #[serde(default)] pub server_name: Option, + #[serde(default)] + pub x509_certificate_pem: Option, + #[serde(default)] + pub principal_id: Option, /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. /// Enabled with Cargo feature `catch-all-fields`. /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. @@ -91,6 +95,10 @@ pub struct IoTCoreHttpContext { pub struct IoTCoreMqttContext { #[serde(default)] pub client_id: Option, + /// X.509 custom authorizer requests don't include a password field. + /// Default to empty Vec when absent. + /// Serializing result will be `password: ""` + #[serde(default)] pub password: Base64Data, #[serde(default)] pub username: Option, @@ -158,6 +166,16 @@ mod test { assert_eq!(parsed, reparsed); } + #[test] + #[cfg(feature = "iot")] + fn example_iot_custom_auth_request_x509() { + let data = include_bytes!("../../fixtures/example-iot-custom-auth-request-x509.json"); + let parsed: IoTCoreCustomAuthorizerRequest = serde_json::from_slice(data).unwrap(); + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: IoTCoreCustomAuthorizerRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + } + #[test] #[cfg(feature = "iot")] fn example_iot_custom_auth_response() { diff --git a/lambda-events/src/fixtures/example-iot-custom-auth-request-x509.json b/lambda-events/src/fixtures/example-iot-custom-auth-request-x509.json new file mode 100644 index 00000000..cd31955f --- /dev/null +++ b/lambda-events/src/fixtures/example-iot-custom-auth-request-x509.json @@ -0,0 +1,21 @@ +{ + "token": "aToken", + "signatureVerified": true, + "protocols": [ + "tls", + "mqtt" + ], + "protocolData": { + "tls": { + "serverName": "serverName", + "x509CertificatePem": "x509CertificatePem", + "principalId": "principalId" + }, + "mqtt": { + "clientId": "myClientId" + } + }, + "connectionMetadata": { + "id": "UUID" + } +} \ No newline at end of file From 3d39897bf443130126d7c815a4d76c259538311d Mon Sep 17 00:00:00 2001 From: Jess Izen <44884346+jlizen@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:39:12 -0800 Subject: [PATCH 2/2] Fix comment formatting for client_id field --- lambda-events/src/event/iot/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda-events/src/event/iot/mod.rs b/lambda-events/src/event/iot/mod.rs index 3cc094a4..5fe14f56 100644 --- a/lambda-events/src/event/iot/mod.rs +++ b/lambda-events/src/event/iot/mod.rs @@ -96,7 +96,7 @@ pub struct IoTCoreMqttContext { #[serde(default)] pub client_id: Option, /// X.509 custom authorizer requests don't include a password field. - /// Default to empty Vec when absent. + /// Default to empty `Vec` when absent. /// Serializing result will be `password: ""` #[serde(default)] pub password: Base64Data,