furaiev / amazon-cognito-identity-dart-2

Unofficial Amazon Cognito Identity Provider Dart SDK, to easily add user sign-up and sign-in to your mobile and web apps with AWS.
MIT License
187 stars 114 forks source link

SignedRequest with body - failing from AWS #46

Closed skotadia closed 4 years ago

skotadia commented 4 years ago

Thanks for the package. I am trying to use it. I am almost there but stuck at specific issue around SignedRequest to API Gateway (Authenticated by IAM Role)

This is what I am doing:

final GoogleSignIn googleSignIn = GoogleSignIn(scopes: ['email']);
                                final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
                                GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
                                CognitoCredentials _credential = new CognitoCredentials(identityPoolId, userPool);
                                await _credential.getAwsCredentials(googleSignInAuthentication.idToken, 'accounts.google.com');

final awsSigV4Client = new AwsSigV4Client(
                                  _credential.accessKeyId, _credential.secretAccessKey, endpoint,
                                  sessionToken: _credential.sessionToken,
                                  region: region);
                                final signedRequest = new SigV4Request(awsSigV4Client,
                                  method: 'POST',
                                  path: '/my-path/',
                                  body:  new Map<String, String>.from({'field_1': 'field_value'})
                                );  
http.Response response = await http.post(
                                  signedRequest.url,
                                  headers: signedRequest.headers,
                                  body: signedRequest.body,
                                );

print(response.body);
print(response.statusCode);
print(response.headers);       

I am getting following error:

message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'

If I remove payload (body) or pass blank body, it passes the AWS checks (working). As soon as I put body, it fails. I tried different combinations of the map, still the same error.

I am sure I must be making very trivial mistake somewhere but I couldn't figure it out.

Pls let me know if any one of you encounter similar issue or see something obvious. Happy to provide more details. Thanks.

skotadia commented 4 years ago

Looking it further, I found the cause. The canonical string generated by this package is:

POST
/dev/my-path/

accept:application/json
content-type:application/json
host:xxxxxxxxx.execute-api.us-east-1.amazonaws.com
x-amz-date:20200408T115657Z
accept;content-type;host;x-amz-date

while canonical string expected or generated on AWS is:

POST
/dev/my-path/

accept:application/json
content-type:application/json; charset=utf-8
host:xxxxxxxxx.execute-api.us-east-1.amazonaws.com
x-amz-date:20200408T115657Z

accept;content-type;host;x-amz-date

If you notice the content-type header has charset=utf-8 extra.

I have locally changed the code to add that and it seems to be working.

Now the question is whether this issue is specific to my AWS side settings or across everything.

If this is a real issue, I am happy to create PR. Please let me know. Thanks

furaiev commented 4 years ago

hi @skotadia It supposed that user adjusts headers manually, something like:

final awsSigV4Client = new AwsSigV4Client(
      credentials.accessKeyId, credentials.secretAccessKey, endpoint,
      serviceName: 'appsync',
      sessionToken: credentials.sessionToken,
      region: 'ap-southeast-1');

  final String query = '''query GetEvent {
    getEvent(id: "3dcd52c3-1fd6-4e4d-8da6-946ef4a0c94d") {
      id
      name
      comments(limit: 10) {
        items {
          content
          createdAt
        }
      }
    }
  }''';

  final signedRequest = new SigV4Request(awsSigV4Client,
      method: 'POST', path: '/graphql',
      headers: new Map<String, String>.from(
          {'Content-Type': 'application/graphql; charset=utf-8'}), // <<<<<<<<<<<<<<<<<<<
      body: new Map<String, String>.from({
          'operationName': 'GetEvent',
          'query': query}));

  http.Response response;
  try {
    response = await http.post(
        signedRequest.url,
        headers: signedRequest.headers, body: signedRequest.body);
  } catch (e) {
    print(e);
  }
skotadia commented 4 years ago

That makes sense. I verified as well. Thanks

sachin052 commented 4 years ago

The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'PUT\n/dev/clients/update/114\n\naccept:application/json\ncontent-type:text/plain; charset=utf-8\nhost:tzqehx0xp8.execute-api.ap-southeast-1.amazonaws