Goal: Make a signed request (SigV4) to AWS Lambda or API Gateway from a Flutter application (iOS, for the sake of this question).
For context, AWS introduced support for "native calls to AWS backends in [...] Flutter or Dart applications" back in May of 2022. There is an example of how to sign a request to Cognito to gather information about a User Pool, but I have not seen any application of this concept for Lambda or API Gateway calls, yet.
I'm wondering if anyone else has had success using AWS's official Dart packages to send signed requests or knows of another way to securely call AWS from a Flutter application.
EDIT:
I was able to accomplish the goal. Here's how I did it:
- The code (all-caps denotes placeholders for your own values):
import 'dart:convert';
import 'package:aws_common/aws_common.dart';
import 'package:aws_signature_v4/aws_signature_v4.dart';
const signer = AWSSigV4Signer(
credentialsProvider: AWSCredentialsProvider.dartEnvironment(),
);
const region = 'REGION';
Future<void> yourFunction() async {
final scope = AWSCredentialScope(
region: region,
service: AWSService.apiGatewayV2,
dateTime: AWSDateTime(DateTime.now()),
);
final request = AWSHttpRequest(
method: AWSHttpMethod.post,
uri: Uri.https('HOST.execute-api.REGION.amazonaws.com','/STAGE_NAME'),
headers: const {
AWSHeaders.contentType: 'application/x-amz-json-1.1',
},
body: json.encode({
'EVENT_KEY':'EVENT_VALUE',
}).codeUnits,
);
final signedRequest = await signer.sign(
request,
credentialScope: scope,
);
final resp = await signedRequest.send();
final respBody = await resp.decodeBody();
print('\n\n${signedRequest.headers}\n\n${respBody}\n\n${resp.statusCode}\n\n');
}
- Within a single AWS region (except where n/a):
- create IAM user with
execute-api:Invoke
permission and programmatic access; store keys securely for later use. - create a Lambda function (can be the default, for testing).
- create API in API Gateway:
- REST (not private)
- Regional endpoint
- Add method (for me, POST)
- IAM authorization type
- Integration type is Lambda
- select the target Lambda function, but
- do not use Lambda proxy
- deploy the newly created API to a new stage (give it a name)
Edit your dart file to include the new resources, including the stage name.
Test your API within API Gateway (I was getting 502 until I unchecked "Lambda proxy integration").
Run the following in your terminal after a successful API test; be sure to insert the keys for the IAM user you created.
flutter run --dart-define=AWS_ACCESS_KEY_ID=... --dart-define=AWS_SECRET_ACCESS_KEY=...
Summary:
In my case, I have a button that executes this function. If you keep the print statement in the above dart code, you should hopefully see {"statusCode": 200, "body": "\"Hello from Lambda!\""}
as the response body.
Hope this helps others. Cannot make any guarantees that my approach will work in another environment. I also may have forgotten to include something relevant in the steps above. Still open to questions and suggestions.
Thank you.