Amplify Gen2 Flutter REST API
Description
I can't see an example of Amplify Gen2 Flutter REST API. There is one for React (https://docs.amplify.aws/react/build-a-backend/add-aws-services/rest-api/set-up-rest-api/), however whatever I try for Flutter, it doesn't work. Could you please provide some working example of:
- REST API Gateway CDK
- AND Dart code that invokes REST endpoint
- AND authorization using Cognito User pool for that endpoint
Categories
- [ ] Analytics
- [X] API (REST)
- [ ] API (GraphQL)
- [ ] Auth
- [ ] Authenticator
- [ ] DataStore
- [ ] Notifications (Push)
- [ ] Storage
Steps to Reproduce
There's documentation for React (https://docs.amplify.aws/react/build-a-backend/add-aws-services/rest-api/set-up-rest-api/), but I can't find one for flutter. I tried to adapt existing example from React to Flutter, however I can't get requests authorized.
// backend.ts
// all the same as https://docs.amplify.aws/react/build-a-backend/add-aws-services/rest-api/set-up-rest-api/
// except for outputs, which is here, adjusted to what Flutter's Gen2 configure() expects:
// add outputs to the configuration file
backend.addOutput({
custom: {
rest_api: {
[myRestApi.restApiName]: {
aws_region: Stack.of(myRestApi).region,
url: myRestApi.url,
authorization_type: "AMAZON_COGNITO_USER_POOLS",
},
},
}
});
// in _configureAmplify()
Map decodedAmplifyConfig = json.decode(amplifyConfig);
var newConfig = {};
newConfig.addAll(decodedAmplifyConfig);
newConfig.addAll(decodedAmplifyConfig["custom"]);
await Amplify.configure(json.encode(newConfig));
// call the API
try {
final restOperation = Amplify.API.post(
'cognito-auth-path',
body: HttpPayload.json({'name': 'Mow the lawn'}),
);
final response = await restOperation.response;
print('POST call succeeded');
print(response.decodeBody());
} on ApiException catch (e) {
print('POST call failed: $e');
}
The above gives 401
{"message":"Unauthorized"}
Screenshots
No response
Platforms
- [ ] iOS
- [X] Android
- [ ] Web
- [ ] macOS
- [ ] Windows
- [ ] Linux
Flutter Version
3.22.3
Amplify Flutter Version
2.3.0
Deployment Method
AWS CDK
Schema
No response
Hi @mateuszboryn thank you for opening this request. I'm going to mark this as a feature request for REST API support in Gen2.
In the meantime can you you try updating your amplify config Json like in this example
final json = jsonDecode(amplifyConfig);
// ignore: avoid_dynamic_calls
json['rest_api'] = {'multiAuthRest': json['custom']['multiAuthRest']};
final configString = jsonEncode(json);
await Amplify.configure(configString);
Let us know if this resolves your issue and please note that this is not an officially supported work around and could break in future versions, but we will let you know when we have an official implementation.
Thank you @tyllark for prompt response and the example provided.
- It helped me to get IAM auth REST API working.
- However, I'm missing AMAZON_COGNITO_USER_POOLS REST API (I have a strict requirement to avoid AWS access key and secret access key in the REST API).
The example you linked has name suggesting it is multiAuthRest. I searched the repo for multiAuthRest and the only resources created that I found are using IAM, and not COGNITO USER POOL.
With that example I got API Gateway to have routes with AWS_IAM authorizer. I'm expecting to have Cognito User Pool authorizer instead.
Below is the code that is much closer to what I expect with some comments on which settings fail.
// create a new REST API
const myRestApi = new RestApi(apiStack, "RestApi", {
restApiName: "multiAuthRest",
deploy: true,
deployOptions: {
stageName: "dev",
},
defaultCorsPreflightOptions: {
allowOrigins: Cors.ALL_ORIGINS, // Restrict this to domains you trust
allowMethods: Cors.ALL_METHODS, // Specify only the methods you need to allow
allowHeaders: Cors.DEFAULT_HEADERS, // Specify only the headers you need to allow
},
});
// create a new Lambda integration
const lambdaIntegration = new LambdaIntegration(
backend.addDeviceFunction.resources.lambda
);
// const cognitoAuth = new CognitoUserPoolsAuthorizer(apiStack, "CognitoAuth", { // uncomment only for authorizationType: AuthorizationType.COGNITO in resource below
// cognitoUserPools: [backend.auth.resources.userPool],
// });
// create a new resource path with IAM authorization
const itemsPath = myRestApi.root.addResource("items", {
defaultMethodOptions: {
authorizationType: AuthorizationType.IAM, // works, but relies on AWS Access key and secret access key.
// authorizationType: AuthorizationType.COGNITO, authorizer: cognitoAuth, // fails with 401 {"message":"Unauthorized"}
},
});
// add methods you would like to create to the resource path
itemsPath.addMethod("ANY", lambdaIntegration);
// add a proxy resource path to the API
itemsPath.addProxy({
anyMethod: true,
defaultIntegration: lambdaIntegration,
});
const apiRestPolicy = new Policy(apiStack, "RestApiPolicy", {
statements: [
new PolicyStatement({
actions: ["execute-api:Invoke"],
resources: [
`${myRestApi.arnForExecuteApi("*", "/items", "dev")}`,
`${myRestApi.arnForExecuteApi("*", "/items/*", "dev")}`,
],
}),
],
});
// attach the policy to the authenticated and unauthenticated IAM roles
backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(
apiRestPolicy
);
// add outputs to the configuration file
backend.addOutput({
custom: {
rest_api: {
[myRestApi.restApiName]: {
url: myRestApi.url.replace(/\/+$/, ""),
aws_region: Stack.of(myRestApi).region,
authorization_type: AuthorizationType.IAM, // works, but relies on AWS Access key and secret access key.
// authorization_type: AuthorizationType.COGNITO, this gives error (apparently, string is not recognized in dart code mappings):
// E/flutter ( 5190): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: ConfigurationError {
// E/flutter ( 5190): "message": "The provided configuration can not be decoded to AmplifyOutputs or AmplifyConfig. Check underlyingException.",
// E/flutter ( 5190): "recoverySuggestion": "If using Amplify Gen 2 ensure that the json string can be decoded to AmplifyOutputs type. If using Amplify Gen 1 ensure that the json string can be decoded to AmplifyConfig type.",
// E/flutter ( 5190): "underlyingException": "CheckedFromJsonException\nCould not create `AuthConfig`.\nThere is a problem with \"plugins\".\nNull is not a Map"
// E/flutter ( 5190): }
// E/flutter ( 5190): #0 AmplifyClass._parseJsonConfig (package:amplify_core/src/amplify_class.dart:151:9)
// E/flutter ( 5190): #1 AmplifyClass.configure (package:amplify_core/src/amplify_class.dart:117:24)
// E/flutter ( 5190): #2 _MyApp._configureAmplify (package:tta_user_app/main.dart:100:21)
// E/flutter ( 5190): <asynchronous suspension>
// authorization_type: "AMAZON_COGNITO_USER_POOLS", // fails with 401 {"message":"Unauthorized"}
},
}
}
});
I have to do this today to make it work, @Jordan-Nelson @NikaHsn could you confirm this is the current approach for output version 1.3?
``... final json = jsonDecode(amplifyConfig); final myRestApi= json['custom']['API']['myRestApi']; json['rest_api'] = { 'myRestApi': { 'aws_region': myRestApi['region'], 'url': myRestApi['endpoint'], } }; final configString = jsonEncode(json); await Amplify.configure(configString);
Hi @mateuszboryn, yes that is correct as of the current working approach! We are still tracking the feature request however cannot provide an update yet.
Thanks, I look forward for an updated official documentation.