Code Monkey home page Code Monkey logo

amazon-cognito-identity-dart-2's Introduction

Pub

Official package is available - Amplify Flutter

Amazon Cognito Identity SDK for Dart

Unofficial Amazon Cognito Identity SDK written in Dart for Dart.

Based on amazon-cognito-identity-js. First version was created by Jonsaw amazon-cognito-identity-dart.

Need ideas to get started?

Usage

Use Case 1. Registering a user with the application. One needs to create a CognitoUserPool object by providing a UserPoolId and a ClientId and signing up by using a username, password, attribute list, and validation data.

import 'package:amazon_cognito_identity_dart_2/cognito.dart';

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
);
final userAttributes = [
  AttributeArg(name: 'first_name', value: 'Jimmy'),
  AttributeArg(name: 'last_name', value: 'Wong'),
];

var data;
try {
  data = await userPool.signUp(
    '[email protected]',
    'Password001',
     userAttributes: userAttributes,
   );
} catch (e) {
  print(e);
}

Use case 2. Confirming a registered, unauthenticated user using a confirmation code received via SMS/email.

import 'package:amazon_cognito_identity_dart_2/cognito.dart';

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
);

final cognitoUser = CognitoUser('[email protected]', userPool);

bool registrationConfirmed = false;
try {
  registrationConfirmed = await cognitoUser.confirmRegistration('123456');
} catch (e) {
  print(e);
}
print(registrationConfirmed);

Use case 3. Resending a confirmation code via SMS/email for confirming registration for unauthenticated users.

import 'package:amazon_cognito_identity_dart_2/cognito.dart';

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
);
final cognitoUser = CognitoUser('[email protected]', userPool);
final String status;
try {
  status = await cognitoUser.resendConfirmationCode();
} catch (e) {
  print(e);
}

Use case 4. Authenticating a user and establishing a user session with the Amazon Cognito Identity service.

import 'package:amazon_cognito_identity_dart_2/cognito.dart';

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
);
final cognitoUser = CognitoUser('[email protected]', userPool);
final authDetails = AuthenticationDetails(
  username: '[email protected]',
  password: 'Password001',
);
CognitoUserSession session;
try {
  session = await cognitoUser.authenticateUser(authDetails);
} on CognitoUserNewPasswordRequiredException catch (e) {
  // handle New Password challenge
} on CognitoUserMfaRequiredException catch (e) {
  // handle SMS_MFA challenge
} on CognitoUserSelectMfaTypeException catch (e) {
  // handle SELECT_MFA_TYPE challenge
} on CognitoUserMfaSetupException catch (e) {
  // handle MFA_SETUP challenge
} on CognitoUserTotpRequiredException catch (e) {
  // handle SOFTWARE_TOKEN_MFA challenge
} on CognitoUserCustomChallengeException catch (e) {
  // handle CUSTOM_CHALLENGE challenge
} on CognitoUserConfirmationNecessaryException catch (e) {
  // handle User Confirmation Necessary
} on CognitoClientException catch (e) {
  // handle Wrong Username and Password and Cognito Client
}catch (e) {
  print(e);
}
print(session.getAccessToken().getJwtToken());

Use case 5. Retrieve user attributes for authenticated users.

List<CognitoUserAttribute> attributes;
try {
  attributes = await cognitoUser.getUserAttributes();
} catch (e) {
  print(e);
}
attributes.forEach((attribute) {
  print('attribute ${attribute.getName()} has value ${attribute.getValue()}');
});

Use case 6. Verify user attribute for an authenticated user.

var data;
try {
  data = await cognitoUser.getAttributeVerificationCode('email');
} catch {
  print(e);
}
print(data);

// obtain verification code...

bool attributeVerified = false;
try {
  attributeVerified = await cognitoUser.verifyAttribute('email', '123456');
} catch (e) {
  print(e);
}
print(attributeVerified);

Use case 7. Delete user attributes for authenticated users.

try {
  final List<String> attributeList = ['nickname'];
  cognitoUser.deleteAttributes(attributeList);
} catch (e) {
  print(e);
}

Use case 8. Update user attributes for authenticated users.

final List<CognitoUserAttribute> attributes = [];
attributes.add(CognitoUserAttribute(name: 'nickname', value: 'joe'));

try {
  await cognitoUser.updateAttributes(attributes);
} catch (e) {
  print(e);
}

Use case 9. Enabling SMS-MFA for a user on a pool that has an optional MFA setting for authenticated users. The phone number needs to be verified

bool mfaEnabled = false;
try {
  mfaEnabled = await cognitoUser.enableMfa();
  var mfaOptions = await cognitoManager.cognitoUser.getMFAOptions();
  print('mfaOptions: $mfaOptions');
} catch (e) {
  print(e);
}
print('mfaEnabled: $mfaEnabled');

Use case 10. Disabling MFA for a user on a pool that has an optional MFA setting for authenticated users.

bool mfaDisabled = false;
try {
  mfaDisabled = await cognitoUser.disableMfa();
} catch (e) {
  print(e);
}
print(mfaDisabled);

Use case 11. Changing the current password for authenticated users.

bool passwordChanged = false;
try {
  passwordChanged = await cognitoUser.changePassword(
    'oldPassword',
    'newPassword',
  );
} catch (e) {
  print(e);
}
print(passwordChanged);

Use case 12. Starting and completing a forgot password flow for an unauthenticated user.

import 'package:amazon_cognito_identity_dart_2/cognito.dart';

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
);
final cognitoUser = CognitoUser('[email protected]', userPool);

var data;
try {
  data = await cognitoUser.forgotPassword();
} catch (e) {
  print(e);
}
print('Code sent to $data');

// prompt user for verification input...

bool passwordConfirmed = false;
try {
  passwordConfirmed = await cognitoUser.confirmPassword(
      '123456', 'newPassword');
} catch (e) {
  print(e);
}
print(passwordConfirmed);

Use case 13. Deleting authenticated users.

bool userDeleted = false
try {
  userDeleted = await cognitoUser.deleteUser();
} catch (e) {
  print(e);
}
print(userDeleted);

Use case 14. Signing out from the application.

await cognitoUser.signOut();

Use case 15. Global signout for authenticated users (invalidates all issued tokens).

await cognitoUser.globalSignOut();

Use case 16. Manually set key value pairs that can be passed to Cognito Lambda Triggers.

Map<String, String> validationData = {
  'myCustomKey1': 'myCustomValue1',
  'myCustomKey2': 'myCustomValue2',
};

Use case 17. Manually set Authorization header (e.g. JWT token)

signedRequest = SigV4Request(
  awsSigV4Client,
  method: Method.post,
  authorizationHeader: session.idToken.jwtToken, // <---- custom authorizationHeader
  path: '/path',
  headers: Map<String, String>.from({
    CONTENT_TYPE: APPLICATION_GRAPHQL,
    ACCEPT: APPLICATION_JSON,
  }),
  body: Map<String, String>.from(
    {
      QUERY: query,
    },
  ),
);

Use case 18. Resetting an user password after first user authentication (when an account has status FORCE_CHANGE_PASSWORD). When an administrator creates the user pool account then an user has to change its password after first sign-in. Moreover the Cognito User Pool service can send a list of required attributes that user has to set when settings a new password.

import 'package:amazon_cognito_identity_dart_2/cognito.dart';

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx'
);
final cognitoUser = CognitoUser('[email protected]', userPool);
final authDetails = AuthenticationDetails(
  username: '[email protected]',
  password: 'Password001!',
);
CognitoUserSession session;
try {
  session = await cognitoUser.authenticateUser(authDetails);
} on CognitoUserNewPasswordRequiredException catch (e) {
  try {
    if(e.requiredAttributes.isEmpty) {
      // No attribute hast to be set
      session = await cognitoUser.sendNewPasswordRequiredAnswer("NewPassword002!");
    } else {
      // All attributes from the e.requiredAttributes has to be set.
      print(e.requiredAttributes);
      // For example obtain and set the name attribute.
      var attributes = { "name": "Adam Kaminski"};
      session = await cognitoUser.sendNewPasswordRequiredAnswer("NewPassword002!", attributes);
    }
  } on CognitoUserMfaRequiredException catch (e) {
    // handle SMS_MFA challenge
  } on CognitoUserSelectMfaTypeException catch (e) {
    // handle SELECT_MFA_TYPE challenge
  } on CognitoUserMfaSetupException catch (e) {
    // handle MFA_SETUP challenge
  } on CognitoUserTotpRequiredException catch (e) {
    // handle SOFTWARE_TOKEN_MFA challenge
  } on CognitoUserCustomChallengeException catch (e) {
    // handle CUSTOM_CHALLENGE challenge
  } catch (e) {
    print(e);
  }
} on CognitoUserMfaRequiredException catch (e) {
  // handle SMS_MFA challenge
} on CognitoUserSelectMfaTypeException catch (e) {
  // handle SELECT_MFA_TYPE challenge
} on CognitoUserMfaSetupException catch (e) {
  // handle MFA_SETUP challenge
} on CognitoUserTotpRequiredException catch (e) {
  // handle SOFTWARE_TOKEN_MFA challenge
} on CognitoUserCustomChallengeException catch (e) {
  // handle CUSTOM_CHALLENGE challenge
} on CognitoUserConfirmationNecessaryException catch (e) {
  // handle User Confirmation Necessary
} catch (e) {
  print(e);
}
print(session.getAccessToken().getJwtToken());

Use case 19. Using this library with Cognito's federated sign in on mobile devices. Use flutter_webview (https://pub.dev/packages/webview_flutter) to navigate to Cognito's authorize URL. Use flutter_webview's navigationDelegate to catch the redirect to myapp://?code=<AUTH_CODE>. Make a POST request to Cognito's token URL to get your tokens. Create the session and user with the tokens.

  final Completer<WebViewController> _webViewController = Completer<WebViewController>();
  Widget getWebView() {
    var url = "https://${COGNITO_POOL_URL}" +
      ".amazoncognito.com/oauth2/authorize?identity_provider=Google&redirect_uri=" +
      "myapp://&response_type=CODE&client_id=${COGNITO_CLIENT_ID}" +
      "&scope=email%20openid%20profile%20aws.cognito.signin.user.admin";
    return
      WebView(
        initialUrl: url,
        userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) ' +
            'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36',
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webViewController) {
          _webViewController.complete(webViewController);
        },
        navigationDelegate: (NavigationRequest request) {
          if (request.url.startsWith("myapp://?code=")) {
            String code = request.url.substring("myapp://?code=".length);
            signUserInWithAuthCode(code);
            return NavigationDecision.prevent;
          }

          return NavigationDecision.navigate;
        },
        gestureNavigationEnabled: true,
      );
  }

  final userPool = CognitoUserPool(
    'ap-southeast-1_xxxxxxxxx',
    'xxxxxxxxxxxxxxxxxxxxxxxxxx'
  );
  static Future signUserInWithAuthCode(String authCode) async {
    String url = "https://${COGNITO_POOL_URL}" +
        ".amazoncognito.com/oauth2/token?grant_type=authorization_code&client_id=" +
        "${COGNITO_CLIENT_ID}&code=" + authCode + "&redirect_uri=myapp://";
    final response = await http.post(url, body: {}, headers: {'Content-Type': 'application/x-www-form-urlencoded'});
    if (response.statusCode != 200) {
      throw Exception("Received bad status code from Cognito for auth code:" +
          response.statusCode.toString() + "; body: " + response.body);
    }

    final tokenData = json.decode(response.body);

    final idToken = CognitoIdToken(tokenData['id_token']);
    final accessToken = CognitoAccessToken(tokenData['access_token']);
    final refreshToken = CognitoRefreshToken(tokenData['refresh_token']);
    final session = CognitoUserSession(idToken, accessToken, refreshToken: refreshToken);
    final user = CognitoUser(null, userPool, signInUserSession: session);

    // NOTE: in order to get the email from the list of user attributes, make sure you select email in the list of
    // attributes in Cognito and map it to the email field in the identity provider.
    final attributes = await user.getUserAttributes();
    for (CognitoUserAttribute attribute in attributes) {
      if (attribute.getName() == "email") {
        user.username = attribute.getValue();
        break;
      }
    }

    return user;
  }
}

Addtional Features

Get AWS Credentials

Get a authenticated user's AWS Credentials. Use with other signing processes like Signature Version 4.

import 'package:amazon_cognito_identity_dart_2/cognito.dart';

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
);
final cognitoUser = CognitoUser('[email protected]', userPool);
final authDetails = AuthenticationDetails(
    username: '[email protected]',
    password: 'Password001',
);
final session = await cognitoUser.authenticateUser(authDetails);

final credentials = CognitoCredentials(
    'ap-southeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', userPool);
await credentials.getAwsCredentials(session.getIdToken().getJwtToken());
print(credentials.accessKeyId);
print(credentials.secretAccessKey);
print(credentials.sessionToken);

Signing Requests

For S3 Uploads

S3 Upload to user "folder" using HTTP Presigned Post.

import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:async/async.dart';
import 'package:http/http.dart' as http;
import 'package:amazon_cognito_identity_dart_2/cognito.dart';
import 'package:amazon_cognito_identity_dart_2/sig_v4.dart';

class Policy {
  String expiration;
  String region;
  String bucket;
  String key;
  String credential;
  String datetime;
  String sessionToken;
  int maxFileSize;

  Policy(
    this.key,
    this.bucket,
    this.datetime,
    this.expiration,
    this.credential,
    this.maxFileSize,
    this.sessionToken,
    {
    this.region = 'us-east-1',
   });

  factory Policy.fromS3PresignedPost(
    String key,
    String bucket,
    int expiryMinutes,
    String accessKeyId,
    int maxFileSize,
    String sessionToken, {
    String region,
   }) {
    final datetime = SigV4.generateDatetime();
    final expiration = (DateTime.now())
        .add(Duration(minutes: expiryMinutes))
        .toUtc()
        .toString()
        .split(' ')
        .join('T');
    final cred =
        '$accessKeyId/${SigV4.buildCredentialScope(datetime, region, 's3')}';
    final p = Policy(
        key, bucket, datetime, expiration, cred, maxFileSize, sessionToken,
        region: region);
    return p;
  }

  String encode() {
    final bytes = utf8.encode(toString());
    return base64.encode(bytes);
  }

  @override
  String toString() {
    // Safe to remove the "acl" line if your bucket has no ACL permissions
    return '''
    { "expiration": "${this.expiration}",
      "conditions": [
        {"bucket": "${this.bucket}"},
        ["starts-with", "\$key", "${this.key}"],
        {"acl": "public-read"},
        ["content-length-range", 1, ${this.maxFileSize}],
        {"x-amz-credential": "${this.credential}"},
        {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
        {"x-amz-date": "${this.datetime}" },
        {"x-amz-security-token": "${this.sessionToken}" }
      ]
    }
    ''';
  }
}

void main() async {
  const _awsUserPoolId = 'ap-southeast-xxxxxxxxxxx';
  const _awsClientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';

  const _identityPoolId = 'ap-southeast-1:xxxxxxxxx-xxxx-xxxx-xxxxxxxxxxx';
  final _userPool = CognitoUserPool(_awsUserPoolId, _awsClientId);

  final _cognitoUser = CognitoUser('+60100000000', _userPool);
  final authDetails =
      AuthenticationDetails(username: '+60100000000', password: 'p&ssW0RD');

  CognitoUserSession _session;
  try {
    _session = await _cognitoUser.authenticateUser(authDetails);
  } catch (e) {
    print(e);
  }

  final _credentials = CognitoCredentials(_identityPoolId, _userPool);
  await _credentials.getAwsCredentials(_session.getIdToken().getJwtToken());

  const _region = 'ap-southeast-1';
  const _s3Endpoint = 'https://my-s3-bucket.s3-ap-southeast-1.amazonaws.com';

  final file = File(path.join('/path/to/my/folder', 'square-cinnamon.jpg'));

  final stream = http.ByteStream(DelegatingStream.typed(file.openRead()));
  final length = await file.length();

  final uri = Uri.parse(_s3Endpoint);
  final req = http.MultipartRequest("POST", uri);
  final multipartFile = http.MultipartFile('file', stream, length,
      filename: path.basename(file.path));

  final String fileName = 'square-cinnamon.jpg';
  final String usrIdentityId = _credentials.userIdentityId;
  final String bucketKey = 'test/$usrIdentityId/$fileName';

  final policy = Policy.fromS3PresignedPost(
      bucketKey,
      'my-s3-bucket',
      15,
      _credentials.accessKeyId,
      length,
      _credentials.sessionToken,
      region: _region);
  final key = SigV4.calculateSigningKey(
      _credentials.secretAccessKey, policy.datetime, _region, 's3');
  final signature = SigV4.calculateSignature(key, policy.encode());

  req.files.add(multipartFile);
  req.fields['key'] = policy.key;
  req.fields['acl'] = 'public-read'; // Safe to remove this if your bucket has no ACL permissions
  req.fields['X-Amz-Credential'] = policy.credential;
  req.fields['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
  req.fields['X-Amz-Date'] = policy.datetime;
  req.fields['Policy'] = policy.encode();
  req.fields['X-Amz-Signature'] = signature;
  req.fields['x-amz-security-token'] = _credentials.sessionToken;

  try {
    final res = await req.send();
    await for (var value in res.stream.transform(utf8.decoder)) {
      print(value);
    }
  } catch (e) {
    print(e.toString());
  }
}

The role attached to the identity pool (e.g. the ..._poolAuth role) should contain a policy like this:

{
    "Effect": "Allow",
    "Action": [
        "s3:PutObject",
        "s3:GetObject"
    ],
    "Resource": "arn:aws:s3:::BUCKET_NAME/test/${cognito-identity.amazonaws.com:sub}/*"
}

For S3 GET Object

Signing S3 GET Object using Authorization header.

import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:amazon_cognito_identity_dart_2/cognito.dart';
import 'package:amazon_cognito_identity_dart_2/sig_v4.dart';
import 'package:http/http.dart' as http;

void main() {
  const _awsUserPoolId = 'ap-southeast-1_xxxxxxxx';
  const _awsClientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';

  const _identityPoolId =
      'ap-southeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
  final _userPool = CognitoUserPool(_awsUserPoolId, _awsClientId);

  final _cognitoUser = CognitoUser('+60100000000', _userPool);
  final authDetails =
      AuthenticationDetails(username: '+60100000000', password: 'p@ssW0rd');

  CognitoUserSession _session;
  try {
    _session = await _cognitoUser.authenticateUser(authDetails);
  } catch (e) {
    print(e);
    return;
  }

  final _credentials = CognitoCredentials(_identityPoolId, _userPool);
  await _credentials.getAwsCredentials(_session.getIdToken().getJwtToken());

  final host = 's3.ap-southeast-1.amazonaws.com';
  final region = 'ap-southeast-1';
  final service = 's3';
  final key =
      'my-s3-bucket/ap-southeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/square-cinnamon.jpg';
  final payload = SigV4.hashCanonicalRequest('');
  final datetime = SigV4.generateDatetime();
  final canonicalRequest = '''GET
${'/$key'.split('/').map((s) => Uri.encodeComponent(s)).join('/')}

host:$host
x-amz-content-sha256:$payload
x-amz-date:$datetime
x-amz-security-token:${_credentials.sessionToken}

host;x-amz-content-sha256;x-amz-date;x-amz-security-token
$payload''';
  final credentialScope =
      SigV4.buildCredentialScope(datetime, region, service);
  final stringToSign = SigV4.buildStringToSign(datetime, credentialScope,
      SigV4.hashCanonicalRequest(canonicalRequest));
  final signingKey = SigV4.calculateSigningKey(
      _credentials.secretAccessKey, datetime, region, service);
  final signature = SigV4.calculateSignature(signingKey, stringToSign);

  final authorization = [
    'AWS4-HMAC-SHA256 Credential=${_credentials.accessKeyId}/$credentialScope',
    'SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token',
    'Signature=$signature',
  ].join(',');

  final uri = Uri.https(host, key);
  http.Response response;
  try {
    response = await http.get(uri, headers: {
      'Authorization': authorization,
      'x-amz-content-sha256': payload,
      'x-amz-date': datetime,
      'x-amz-security-token': _credentials.sessionToken,
    });
  } catch (e) {
    print(e);
    return;
  }

  final file = File(path.join(
      '/path/to/my/folder',
      'square-cinnamon-downloaded.jpg'));

  try {
    await file.writeAsBytes(response.bodyBytes);
  } catch (e) {
    print(e.toString());
    return;
  }

  print('complete!');
}

For AppSync's GraphQL

Authenticating Amazon Cognito User Pool using JWT Tokens.

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:amazon_cognito_identity_dart_2/cognito.dart';

void main() async {
  const _awsUserPoolId = 'ap-southeast-1_xxxxxxxxx';
  const _awsClientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';

  const _identityPoolId = 'ap-southeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
  final _userPool = CognitoUserPool(_awsUserPoolId, _awsClientId);

  final _cognitoUser = CognitoUser('+60100000000', _userPool);
  final authDetails =
      AuthenticationDetails(username: '+60100000000', password: 'p@ssW0rd');

  CognitoUserSession _session;
  try {
    _session = await _cognitoUser.authenticateUser(authDetails);
  } catch (e) {
    print(e);
    return;
  }

  final _credentials = CognitoCredentials(_identityPoolId, _userPool);
  await _credentials.getAwsCredentials(_session.getIdToken().getJwtToken());

  final _endpoint =
      'https://xxxxxxxxxxxxxxxxxxxxxxxxxx.appsync-api.ap-southeast-1.amazonaws.com';

  final body = {
    'operationName': 'CreateItem',
    'query': '''mutation CreateItem {
        createItem(name: "Some Name") {
          name
        }
      }''',
  };
  http.Response response;
  try {
    response = await http.post(
      '$_endpoint/graphql',
      headers: {
        'Authorization': _session.getAccessToken().getJwtToken(),
        'Content-Type': 'application/json',
      },
      body: json.encode(body),
    );
  } catch (e) {
    print(e);
  }
  print(response.body);
}

Signing GraphQL requests for authenticated users with IAM Authorization type for access to AppSync data.

import 'package:http/http.dart' as http;
import 'package:amazon_cognito_identity_dart_2/cognito.dart';
import 'package:amazon_cognito_identity_dart_2/sig_v4.dart';

void main() async {
  final credentials = CognitoCredentials(
      'ap-southeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', userPool);
  await credentials.getAwsCredentials(session.getIdToken().getJwtToken());

  const endpoint =
      'https://xxxxxxxxxxxxxxxxxxxxxxxxxx.appsync-api.ap-southeast-1.amazonaws.com';

  final awsSigV4Client = 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 = SigV4Request(awsSigV4Client,
      method: 'POST', path: '/graphql',
      headers: Map<String, String>.from(
          {'Content-Type': 'application/graphql; charset=utf-8'}),
      body: 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);
  }
  print(response.body);
}

For API Gateway & Lambda

Signing requests for authenticated users for access to secured routes to API Gateway and Lambda.

import 'package:http/http.dart' as http;
import 'package:amazon_cognito_identity_dart_2/cognito.dart';
import 'package:amazon_cognito_identity_dart_2/sig_v4.dart';

void main() async {
  final credentials = CognitoCredentials(
      'ap-southeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', userPool);
  await credentials.getAwsCredentials(session.getIdToken().getJwtToken());

  const endpoint =
      'https://xxxx.execute-api.ap-southeast-1.amazonaws.com/dev';
  final awsSigV4Client = AwsSigV4Client(
      credentials.accessKeyId, credentials.secretAccessKey, endpoint,
      sessionToken: credentials.sessionToken,
      region: 'ap-southeast-1');

  final signedRequest = SigV4Request(awsSigV4Client,
      method: 'POST',
      path: '/projects',
      headers: Map<String, String>.from(
          {'header-1': 'one', 'header-2': 'two'}),
      queryParams: Map<String, String>.from({'tracking': 'x123'}),
      body: Map<String, dynamic>.from({'color': 'blue'}));

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

Use Custom Storage

Persist user session using custom storage.

Shared Preferences Plugin storage example found here.

import 'dart:convert';
import 'package:amazon_cognito_identity_dart_2/cognito.dart';

Map<String, String> _storage = {};

class CustomStorage extends CognitoStorage {
  String prefix;
  CustomStorage(this.prefix);

  @override
  Future setItem(String key, value) async {
    _storage[prefix+key] = json.encode(value);
    return _storage[prefix+key];
  }

  @override
  Future getItem(String key) async {
    if (_storage[prefix+key] != null) {
      return json.decode(_storage[prefix+key]);
    }
    return null;
  }

  @override
  Future removeItem(String key) async {
    return _storage.remove(prefix+key);
  }

  @override
  Future<void> clear() async {
    _storage = {};
  }
}

final customStore = CustomStorage('custom:');

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
  storage: customStore,
);
final cognitoUser = CognitoUser(
  '[email protected]', userPool, storage: customStore);
final authDetails = AuthenticationDetails(
  username: '[email protected]', password: 'Password001');
await cognitoUser.authenticateUser(authDetails);

// some time later...
final user = await userPool.getCurrentUser();
final session = await user.getSession();
print(session.isValid());

Get AWS credentials with facebook

CognitoCredentials _credential = CognitoCredentials('ap-southeast-1_xxxxxxxxx', userPool);
await _credential.getAwsCredentials(accessToken, 'graph.facebook.com')
print(_credential.sessionToken);

Use client secret

The original amazon-cognito-identity-js which this library is based off, doesn't support client secret. This feature has been added since version 0.1.9, to use it, simply set your client secret when creating CognitoUserPool:

final userPool = CognitoUserPool(
  'ap-southeast-1_xxxxxxxxx',
  'xxxxxxxxxxxxxxxxxxxxxxxxxx',
  clientSecret: 'xxxxxxxxxxxxxxxxxxxxxxx'
);

Flutter web sign-in performance

To improve performance of modPow() used in authenticateUser(), use an external JavaScript call to do the modPow calculation using the native BigInt type.

Include the following JavaScript method in your top level index.html page for your flutter web app.

Also, add package:js as a dependency in your app's pubspec.yaml

js: ^0.6.3

<script>
  function jsBigIntModPow(bStr, eStr, mStr) {
  let b = BigInt(bStr);
  let e = BigInt(eStr);
  let m = BigInt(mStr);
  let bigIntOne = BigInt(1);
  let bigIntZero = BigInt(0);
  if (e < bigIntOne) {
  return bigIntOne;
}
  if (b < bigIntZero || b > m) {
  b = b % m;
}
  var r = bigIntOne;
  while (e > bigIntZero) {
  if ((e & bigIntOne) > bigIntZero) {
  r = (r * b) % m;
}
  e = e >> bigIntOne;
  b = (b * b) % m;
}
  return r;
}
</script>

amazon-cognito-identity-dart-2's People

Contributors

andreimisiukevich avatar asual avatar bsneider avatar chirag729 avatar dagovalsusa avatar dependabot[bot] avatar droidkfx avatar furaiev avatar gummiees avatar jcblancomartinez avatar jonsaw avatar juneyu-ctm avatar kbokarius avatar konectech avatar ludysu avatar maciejkozuch avatar moonshinephoenix avatar ncb000gt avatar okojoalg avatar pal avatar pana-g avatar pidwid avatar raywhiteside avatar s-okita avatar sergio-mira avatar shinriyo avatar spaicygaming avatar stopdropandrew avatar tslamaitemis avatar yeejingt95 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

amazon-cognito-identity-dart-2's Issues

CognitoUser.updateAttributes does not return a future

Hi,

first of all: Thank you for this package, it has been working flawlessly for us 👍

However, we have a minor question/issue: Is there a reason for CognitoUser.updateAttributes and CognitoUser.deleteAttributes not returning a Future of some kind (maybe void)?

We're trying to await the user attribute update before updating the UI, however Dart will show a warning info: 'await' applied to 'void', which is not a 'Future'. (await_only_futures) ..., although it looks like it works anyway.

Cheers

Wrong behaviour with Get Requests

@furaiev

SignedRequests have still a Content-Type Header even if its a GET method.

sig_v4.dart:

As first the Header is set to application/json as its default.

    if (headers['Content-Type'] == null) {
      headers['Content-Type'] = awsSigV4Client.defaultContentType;
    }

This If would remove the Header, if the Body is ''

    if (body == '') {
      headers.remove('Content-Type');
    }

As as next the _generateAuthorization is triggered, which builds the Canonical Request and the Authorization Header. So if body is '', the Header is removed and the request is without correct headers, because as last, there is an additional IF which would add the Header if null.

    if (headers['Content-Type'] == null) {
      print("8");
      headers['Content-Type'] = awsSigV4Client.defaultContentType;
    }

Important is, that the Content-Type will never be removed. This is not a real problem if you work with the http package , because it simply sends the content-type with the request and therefore the SigV4 request is valid.

But there is a problem with dio, because it removes the content-type header from GET requests, because there is no content.

I found a working Solution for now:

// Add the Content Type only if method is not GET
    if (headers['Content-Type'] == null **&& this.method != 'GET'**) {
      headers['Content-Type'] = awsSigV4Client.defaultContentType;
    }

Also removed following Code at the end of Constructor.

     if (headers['Content-Type'] == null) {
       print("setting");
      headers['Content-Type'] = awsSigV4Client.defaultContentType;
    }

Token is not from a supported provider of this identity pool

Trying to add facebook login with AWS Cognito. After loggin using https://pub.dev/packages/flutter_facebook_login I am trying to pass the details to AWS. But its throwing error of
Exception has occurred. CognitoClientException (CognitoClientException{statusCode: 400, code: NotAuthorizedException, name: NotAuthorizedException, message: Token is not from a supported provider of this identity pool.})
What am I doing wrong?

  _loginWithFB() async{
    final result = await facebookLogin.logIn(['email']);

    switch (result.status) {
      case FacebookLoginStatus.loggedIn:
        final token = result.accessToken.token;
        final graphResponse = await http.get('https://graph.facebook.com/v2.12/me?fields=name,picture,email&access_token=${token}');
        final profile = JSON.jsonDecode(graphResponse.body);
        print(profile);
        final _userPool = new CognitoUserPool('ap-south-1_XXXXXXXXX', 'xxxxxxxxxxxxxxxxxxxxx');
        final _credential = new CognitoCredentials('ap-south-1:xxxxxxxxx-xxxx-xxxx-xxxxxxxxxxx', _userPool);
        await _credential.getAwsCredentials(token, 'graph.facebook.com');
        print(_credential.sessionToken);
        setState(() {
          userProfile = profile;
          _isLoggedIn = true;
        });
        break;

      case FacebookLoginStatus.cancelledByUser:
        setState(() => _isLoggedIn = false );
        break;
      case FacebookLoginStatus.error:
        setState(() => _isLoggedIn = false );
        break;
    }

  }

Thanks in advance.

authenticateUser results in Unable to verify secret hash or client

class Cognito {
  static const _userPoolId = 'xxx';
  static const _clientId = 'xxx';
  static const _clientSecret = 'xxx';

  final CognitoUserPool userPool = new CognitoUserPool(_userPoolId, _clientId, clientSecret: _clientSecret);

  static final Cognito _instance = Cognito._internal();

  factory Cognito() {
    return _instance;
  }

  Cognito._internal();

  Future signUpAndGetToken(String email, String password) async {
    await userPool.signUp(email, password);
    return await signInAndGetToken(email, password);
  }

  Future signInAndGetToken(String email, String password) async {
    final cognitoUser = new CognitoUser(email, new Cognito().userPool, clientSecret: _clientSecret);
    final authDetails = new AuthenticationDetails(
      username: email,
      password: password,
    );
    CognitoUserSession session = await cognitoUser.authenticateUser(authDetails);
    return session.getAccessToken().getJwtToken();
  }

I'm able to sign up a new user without an error, but the CognitoUserSession session = await cognitoUser.authenticateUser(authDetails); results in name: NotAuthorizedException, message: Unable to verify secret hash for client

I'm passing the same client secret in both the call to signUp and authenticateUser, so why would signUp be working but not authenticateUser?

Federated identity provider support?

I looked through the documentation and it's not clear whether this library has support for signing in via Google, Facebook, etc on mobile devices. I assume that there must be a way to pop up a browser, display the third party sign on page, and then redirect back to the app, but is that something that this library can facilitate or support?

s3 upload not working

Hi,

I am unable to upload images using the library.

I m getting the below response

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>3881BA8E9D4EAD4B</RequestId><HostId>EIi9mLd/oUOTw1+pOzGnC9fbrdbTiSLGJAwVJUjJLByglccc2yfPti2LKkkISq71eWx+QvqOjuM=</HostId></Error>

The code for upload is as follows :

static Future<String> uploadFile({File file}) async {
    CognitoUserPool userPool = CognitoUserPool(
      kUserPoolID,
      kClientID,
    );

    CognitoCredentials credentials = CognitoCredentials(
      kIdentityPoolID,
      userPool,
    );

    String response;
    try {
      final length = await file.length();
      final stream = file.openRead();
      final String fileName = path.basename(file.path);

      final uri = Uri.parse(
        ks3Endpoint,
      );

      final req = http.MultipartRequest(
        kPOST,
        uri,
      );

      String mimeType = mime(fileName);
      List<String> mimeTypes = mimeType.split('/');

      final multipartFile = http.MultipartFile(
        'file',
        stream,
        length,
        filename: fileName,
        contentType: MediaType(
          mimeTypes[0],
          mimeTypes[1],
        ),
      );

      final token = await AppSyncConfig.getIdToken();
      await credentials.getAwsCredentials(
        token,
      );

      final String usrIdentityId = credentials.userIdentityId;

      final String bucketKey = '$kBucketKey/$usrIdentityId/$fileName';

      final policy = Policy.fromS3PreSignedPost(
        bucketKey,
        kBucketID,
        15,
        credentials.accessKeyId,
        length,
        credentials.sessionToken,
        region: kRegion,
      );

      final key = SigV4.calculateSigningKey(
        credentials.secretAccessKey,
        policy.datetime,
        kRegion,
        's3',
      );

      final signature = SigV4.calculateSignature(
        key,
        policy.encode(),
      );

      req.fields["key"] = policy.key;
      req.fields["acl"] = "public-read";
      req.fields["X-Amz-Credential"] = policy.credential;
      req.fields["X-Amz-Algorithm"] = "AWS4-HMAC-SHA256";
      req.fields["X-Amz-Date"] = policy.datetime;
      req.fields["Policy"] = policy.encode();
      req.fields["X-Amz-Signature"] = signature;
      req.fields["x-amz-security-token"] = credentials.sessionToken;
      req.files.add(multipartFile);

      final res = await req.send();
      if (res.statusCode == 204) {
        response = kSuccess;
      } else {
        response = res.statusCode.toString();
      }
    } catch (e) {
      response = e.toString();
    }

    print(response);
    return response;
  }

Policy class

import 'dart:convert';

import 'package:amazon_cognito_identity_dart_2/sig_v4.dart';

class Policy {
  String expiration;
  String region;
  String bucket;
  String key;
  String credential;
  String datetime;
  String sessionToken;
  int maxFileSize;

  Policy(this.key, this.bucket, this.datetime, this.expiration, this.credential,
      this.maxFileSize, this.sessionToken,
      {this.region = 'us-west-2'});

  factory Policy.fromS3PreSignedPost(
      String key,
      String bucket,
      int expiryMinutes,
      String accessKeyId,
      int maxFileSize,
      String sessionToken,
      {String region}) {
    final datetime = SigV4.generateDatetime();
    final expiration = (DateTime.now())
        .add(Duration(minutes: expiryMinutes))
        .toUtc()
        .toString()
        .split(' ')
        .join('T');
    final cred =
        '$accessKeyId/${SigV4.buildCredentialScope(datetime, region, 's3')}';
    final p = Policy(
        key, bucket, datetime, expiration, cred, maxFileSize, sessionToken,
        region: region);
    return p;
  }

  String encode() {
    final bytes = utf8.encode(toString());
    return base64.encode(bytes);
  }

  @override
  String toString() {
    // Safe to remove the "acl" line if your bucket has no ACL permissions
    return '''
    { "expiration": "${this.expiration}",
      "conditions": [
        {"bucket": "${this.bucket}"},
        ["starts-with", "\$key", "${this.key}"],
        {"acl": "public-read"},
        ["content-length-range", 1, ${this.maxFileSize}],
        {"x-amz-credential": "${this.credential}"},
        {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
        {"x-amz-date": "${this.datetime}" },
        {"x-amz-security-token": "${this.sessionToken}" }
      ]
    }
    ''';
  }
}

User is not confirmed error when sending SMS_MFA

When sending a code for SMS MFA an error is thrown of CognitoUserException: User Confirmation Necessary although the user is already of status confirmed

ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: CognitoUserException: User Confirmation Necessary
E/flutter (13388): #0      CognitoUser.sendMFACode (package:amazon_cognito_identity_dart_2/src/cognito_user.dart:871:7)

I am sending the SMS MFA code that the user receives after they attempt to login. This is automatically sent cause MFA is required on the pool.

Correct exception is not thrown from SignIn

The following snippet from the package README does not work as expected

CognitoUserSession session;
try {
  session = await cognitoUser.authenticateUser(authDetails);
...
} on CognitoUserConfirmationNecessaryException catch (e) {
  // handle User Confirmation Necessary
} on CognitoClientException catch (e) {
  // handle Wrong Username and Password and Cognito Client
}
...

When using this code CognitoUserConfirmationNecessaryException is not thrown. Rather:

CognitoClientException{statusCode: 400, code: UserNotConfirmedException, name: UserNotConfirmedException, message: User is not confirmed.}

Expected Behavior: Correct exception should be thrown so the client can handle the challenge appropriately.

Example with API GW not working | Missing Header

Hi,

tried the example with API GW and Post Method but it was not working. I had to add an additional header with Content-Type: application/json; charset=utf-8

final signedRequest = new SigV4Request(awsSigV4Client,
    method: 'POST',
    path: 'xx',
    headers: new Map<String, String>.from(
        {"Content-Type": "application/json; charset=utf-8"}),
    queryParams: new Map<String, String>.from({}),
    body: new Map<String, dynamic>.from({'color': 'blue'}));

Cheers

confirmRegistration is not sending clientSecret

when I try to use confirmRegistration from CognitoUser I get the following error:

{
    "__type": "NotAuthorizedException",
    "message": "Unable to verify secret hash for client ***********"
}

then I debug the lib and figured it is not sending the client secret in this call, it can be fixed doing like the signUp method

if (_clientSecret != null) {
    params['SecretHash'] = CognitoUser.calculateClientSecretHash(username, _clientId, _clientSecret);
}

Respond to auth challenge SELECT_MFA_TYPE

Hi,
Is there any way that i can respond to auth challenge SELECT_MFA_TYPE?.. I was trying to reuse sendCustomChallengeAnswer method, but the challengeName is hardcoded as 'CUSTOM_CHALLENGE'.
Can you please provide a method that accepts both the challenge name and answer?.. This way, in future we can support any type of auth challenges.

Thank you

Google Sign In "Working" but can't see user in User Pool

Hi,

I have cloned the example app and I have added awsconfiguration.json file with my cognito info.

I then tried using sign up (i.e. email + password) and this worked, and my user was shown in my user pool.

I then added in google_sign_in package:

google_sign_in: ^4.2.0

And did the necessary config.

I ended up using the code below:

signInGoogle() async { final dynamic result = await signInWithGoogle(); Cognito .federatedSignIn('accounts.google.com', result.idToken) .then((res) { print(res); }).catchError((dynamic error) { print(error); }); }

I get a "flutter: UserState.SIGNED_IN" back and the state shows that I'm signed in.

I then go under my groups, e.g.

Groups => eu-west-xxx_Google

But I don't see the user.

I don't have any errors in the app, and so was wondering if you could point me in the right direction to get this working.

Thanks, Robert

GET can work, but POST failed with "POST: 403, {"message":"The request signature we calculated does not match the signature you provided..."

When I am using GET the request succeeds.
But when I use POST with a body I get this error response, POST without a body can work
The request signature we calculated does not match the signature you provided. ...
I saw someone mentioned exactly the same issue in amazon-cognito-idenity-dart: jonsaw/amazon-cognito-identity-dart#35
But I tried following code without any luck, any help is appreciated!
final headers = Map<String, String>();
headers["Content-Type"] = "application/json; charset=utf-8";
final signedRequest = SigV4Request(client, method: 'POST', path: path, body: jsonBody, headers: headers);
final response = await http.post(signedRequest.url, headers: signedRequest.headers, body: jsonBody);

How to get s3 upload progress

Hi @furaiev,
Thanks for this great library & updates on it!
I'm wondering if there is a way to get the upload progress through s3 send(). Right now it only returns an empty response after image upload completion.

Use SigV4Request for signed request for Lex service

I use this package to signed my http request for amazon lex service like this :

AwsSigV4Client client=AwsSigV4Client(
      kAccessKey,
      kSecretKey,
      'https://runtime.lex.us-east-1.amazonaws.com',
      region: 'us-east-1',
      serviceName: 'lex',
    );

final signedRequest = new SigV4Request(
      client,
      method: 'POST',
      path: '/bot/MyBotName/alias/BETA/user/MyUser/text',
      body: new Map<String, dynamic>.from({"inputText": "hi"}),
    );

var response = await http.post(
      signedRequest.url,
      headers: signedRequest.headers,
      body: signedRequest.body,
    );
    print(response.body.toString());

but i got this message from server :
{"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."}

also i cant see :
X-Amz-Content-Sha256
in headers .
am i miss something ?

The request signature we calculated does not match the signature you provided

Hi,

i have a Problem with the Encoding and i have no clue where its coming from.

Package: 2 0.1.12+3

What iam Doing:
Get request against API Gateway.

code:

    final signedRequest = new SigV4Request(
      awsSigV4Client,
      method: 'GET',
      path: "/path/$querystring",
      headers: new Map<String, String>.from({}),
      queryParams: new Map<String, String>.from({}),
    );
    Response response;
    response = await http.get(
      signedRequest.url,
      options: Options(
        headers: signedRequest.headers,
      ),
    );

When i pass no special Characters it works, but as soon i pass characters like &ÄÜÖ i receive an error:
"The request signature we calculated does not match the signature you provided
The Canonical String for this request should have been
'GET
/path/%25C3%25A4 (<<< is an double encoded 'ä')
"
it seems like it is double encoded and i have no clue where to start whats going on here.

Any thoughts?

Best Regards,

Inviting forkers of original repo

Hey, have you tried inviting the coders that forked the original version of this project? They seem to have made a lot of contributions in their own forks.

For S3 GET Object possible with accesskeyid and secret?

Your example shows using cognito. is it difficult to change this to use accesskeyid and secret? I managed to get S3 upload working (code below) but for GET's (and DELETE's) which don't have request body's but instead rely on header values, i have had no luck.

Future<Map> send(
      {@required String imagePathOfPhone,
      @required String imagePathInS3Bucket,
      @required String bucketName,
      @required String accessKeyId,
      @required String secretKeyId,
      @required String region,
      @required String s3Endpoint}) async {
    final file = File(imagePathOfPhone);
    final stream = http.ByteStream(DelegatingStream.typed(file.openRead()));
    final length = await file.length();

    final uri = Uri.parse(s3Endpoint);
    final req = http.MultipartRequest("POST", uri);
    final multipartFile = http.MultipartFile('file', stream, length,
        filename: path.basename(file.path));

    final policy = Policy.fromS3PreSignedPost(
        imagePathInS3Bucket, bucketName, accessKeyId, 15, length,
        region: region);
    final key =
        SigV4.calculateSigningKey(secretKeyId, policy.datetime, region, 's3');
    final signature = SigV4.calculateSignature(key, policy.encode());

    req.files.add(multipartFile);
    req.fields['key'] = policy.key;
    req.fields['acl'] = 'public-read';
    req.fields['X-Amz-Credential'] = policy.credential;
    req.fields['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
    req.fields['X-Amz-Date'] = policy.datetime;
    req.fields['Policy'] = policy.encode();
    req.fields['X-Amz-Signature'] = signature;

    try {
      final res = await req.send();
 [...]

Any chance you can point me in the right direction?

x-amz-security-token:${_credentials.sessionToken}
_credentials.secretAccessKey
are the two fields i can't figure out doing the non-cognito way.

Any help is much appreciated.

SignedRequest with body - failing from AWS

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.

Exception in production APK | not in DEBUG APK | Unhandled Exception: type '_OneByteString' is not a subtype of type

I'm getting this exception on production apk in Pixel3A Android10

2020-03-30 10:03:58.923 2263-2289/? E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '_OneByteString' is not a subtype of type 'int' of 'index'
    #0      CognitoIdentityId.getIdentityId (package:amazon_cognito_identity_dart_2/src/cognito_identity_id.dart:51)
    <asynchronous suspension>
    #1      CognitoCredentials._getAwsCredentials (package:amazon_cognito_identity_dart_2/src/cognito_credentials.dart:52)
    <asynchronous suspension>
    #2      CognitoCredentials.getAwsCredentials (package:amazon_cognito_identity_dart_2/src/cognito_credentials.dart:38)
    <asynchronous suspension>

Flutter Channel Dev 1.15 issue

I follow the example app, and implement it to my app.
Once publish it, got an issue after create a release version.
After the user session is over (1 hour after), user no longer able to access the app.
the _getValues was throwing error :
"error type '_onebytestring' is not a subtype of type 'int' of 'index'"

I though there's something wrong with my code.
But when I try release the example, the same thing happen.
Secure Counter page & Login page always show "Loading..."

When check it, the actual error came from _cognitoUser.getSession() method.

I honestly don't know what happen here (I'm new to Flutter)
It seem, this issue not happen on debug mode, only when release.

Originally posted by @erw13n in #24 (comment)

Tell us about this package

I notice this takes another package and adds a '2' to the end of its' name and is a fork of that package. I assume that you needed the original package and that it was no longer supported by the original developer, so you started building on top of it?

I ask because I need this package but it looks strange and I want something that will be supported (not asking for new features).

requestMFACode

Hi @furaiev, we enabled MFA while sign-in and we use sendMFACode method to achieve it.
Can we have an another method to request for MFA code when we intend to do some (secured) transactions/actions?

Thanks in advance.

How can I get credential information for authenticated cognito identity pool?

Dear Developer,

I have an authenticated token and identity id from the server response.
By using that token and identity id, we can get credential information by using
DeveloperAuthenticatedIdentityProvider(reference below) in both Android and iOS.

May I know how can I get credential information by using token and identity id from amazon-cognito-identity-dart-2?

I am looking forward to your supporting soon.

Thanks in advance!

Reference:
https://docs.aws.amazon.com/cognito/latest/developerguide/developer-authenticated-identities.html

[web] [Internet explorer] authenticate error: Unsupported operation: No source of cryptographically secure random numbers available

I used the following code to authenticate user:

final authDetails = AuthenticationDetails(
       username: email,
       authParameters: List(),
       validationData: {},
       password: password);
   try {
     _session = await _cognitoUser.authenticateUser(authDetails);
   } on CognitoUserCustomChallengeException catch (e) {
     // handle custom challenage
     print('custom challenage $e');
   }  on Error catch (e) {
     print('Error $e');
   }

And then I get the error on IE 11.

Unsupported operation: No source of cryptographically secure random numbers available

It works fine on FireFox, Chrome, Edge.
Do you have any suggestion to fix this issue? Could it relate to Crypto package?

The request signature we calculated does not match the signature you provided

Hi,

i try to query a API GW with your plugin, but when i insert an umlaut like 'ä,ö,ü' its not working correctly and ive got the 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."

Without Umalute its working quite well.

Request i sent

Canonical Request sent

GET
/path/%C3%A4

accept:application/json
content-type:application/json
host:yyyyyyy.execute-api.eu-central-1.amazonaws.com
x-amz-date:20191229T163450Z

Exception:

{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.

The Canonical String for this request should have been

'GET
/path/%25C3%25A4

accept:application/json
content-type:application/json
host:yyyyyyy.execute-api.eu-central-1.amazonaws.com
x-amz-date:20191229T163450Z
}

Example app: why check login in FutureBuilder instead of initState?

I'm new to Flutter, so wondering about the example app's use of FutureBuilder, and calling _getValues in the build method. It seems like you'd only need to setup the UserService once, in an initState, instead of re-creating this and doing the auth check on every build method invocation? Further, when the increment button is pressed, you will wind up doing a second fetch of the counter, which is needless and would just double your Lambda invocations. i.e. it's incremented in the _incrementCounter and gets the updated counter there, but then sets the state, causing a rebuild which then fetches the counter again in _getValues. I added debug printing to my own code that follows the pattern of the sample app, and I see that happen at least.

I realize that technically a user's account could be deleted or logged out of all tokens via some other use, and thus one could argue that checking their auth on every render is the most secure, but it seems excessive and in a case like this where you also call a Lambda, like you're doing more than you need. Can you thus shed some light on this design and why it is favored over using initState to set all this up instead - since I'm new to this, I'm wondering if I'm missing something or if it's just a choice for the example, or? Thanks for this flutter plugin, it's working well for me so far!

getAWSCredentials with Google

Hi,

how can I authenticate user with Google using getAWSCredentials method? In the documentation there is en example for Facebook, saying that I need to pass 'graph.facebook.com' as a second parameter. I'm new to social auth and I do not know what is equivalent for that for Google or where actually search for it.

Just to add, I do not manage AWS Pools, I only do mobile app using whatever I am provided from AWS team.

Google Authentication : Couldn't Sign you in - The browser or app may not be secure.

Hi Team,

I referred the code from Use-case 19 for signing in using google account.

As a first step, I am launched to the Google Login screen(asking only email-Id). On entering the google account id and click on login, i am redirected to the error page saying "Couldn't sign you in. The browser or app may not be secure".

This is happening only in iOS and not in android.

Kindly help in sorting this out. Thanks.

Null credentials when authenticated

After authenticating and using the Cognito credentials for a little time (along with other AWS services), when I try to retrieve them after an app hot restart, I get the following error and I'm no more able to use them to access the other AWS services.

Failed assertion: line 27 pos 12: 'accessKey != null': is not true.

However, I noticed that the getAwsCredentials() method gets called twice: while the first time it returns null keys and tokens, the second time they are correct. If I hot reload after the hot restart that gets me null keys, I get valid keys and tokens at the first call.
I am pretty new to this package, so I might be making mistakes in my code, even though it is very basic. Here it is:

Future<CognitoCredentials> getCredentials() async {
    if (_cognitoUser == null || _session == null) return null;

    _credentials = CognitoCredentials(_identityPoolId, _userPool);
    await _credentials.getAwsCredentials(_session.getIdToken().getJwtToken());
    return _credentials;
}

Am I wrong with my code or is there an error in the package?

'User Confirmation Necessary' Exception for user already confirmed

When I tried your example with 2 existing users that I have in my pool (the 2 users are already with 'Enabled / CONFIRMED' status in Cognito), I got the CognitoUserConfirmationNecessaryException but I don't understand why this exception is thrown. And the same when I tried in my app. And the variable challengeName is null when I check it in the exception.

This is my code :

final cognitoUser = CognitoUser(_username, userPool, storage: userPool.storage);
final authDetails = AuthenticationDetails(
      username: _username,
      password: _password,
 );

CognitoUserSession session;

try {
     session = await cognitoUser.authenticateUser(authDetails);
} on CognitoUserConfirmationNecessaryException catch (e) {
     print(e.challengeName); -> null
     print(e.message); -> User Confirmation Necessary
     print(e.signInUserSession.isValid()); -> true
} catch (e) {
     print(e);
}

Change Content-Type from binary/octet-stream to image/jpeg

I'm making a POST request to Amazon S3 by sending an image together; however, when uploading the image, it assumes the default value as if I had not sent the content metadata, causing it to be downloaded when opening the image in the browser.

Code:

final _client = Dio( );
final int length = await image.length();
final String filename = '$path${DateTime.now().millisecondsSinceEpoch}.jpeg';
final String key = filename;
final Policy policy = Policy.fromS3PreSignedPost(
      key,
      bucketId,
      500,
      accessKey,
      length,
      sessionToken,
      region: region,
    );
final List<int> signKey = SigV4.calculateSigningKey(secretKey, policy.datetime, region, service);
final String signature = SigV4.calculateSignature(signKey, policy.encode( ));
final FormData formData = FormData.fromMap({
        'key': policy.key,
        'acl': 'public-read',
        'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
        'X-Amz-Credential': policy.credential,
        'X-Amz-Date': policy.datetime,
        'Policy': policy.encode(),
        'X-Amz-Signature': signature,
        'x-amz-security-token': sessionToken,
        'file': await MultipartFile.fromFile(
          image.path,
        filename: filename,
        contentType: MediaType('image', 'jpeg'),
      ),
});
await _client.post(post, data: formData);

getUserAttributes Type Error

both

final attributes = await _cognitoUser.getUserAttributes();
List attributes = await _cognitoUser.getUserAttributes();

getting this error:
_TypeError (type 'List' is not a subtype of type 'FutureOr<List>')

Kind of new to fluttter and all but strict type casting seems to be giving quite a bit of error recently.

Changing

Future<List> getUserAttributes() async {

to

Future getUserAttributes() async {

does work but I'm not really sure if it should be done this way.

getSession is called on nul

Hello,
When I try to get the currentUser() from Page to Page I got this error.

flutter: The error;
flutter: NoSuchMethodError: The method 'getSession' was called on null.

Receiver: null

Here my code

Future<UserService> _getValues() async {
    try {
      final user = await userPoolSettings.getCurrentUser();
      final session = await user.getSession();
      print(session);
    } catch (e) {
      print("The error; ");
      print(e);
    }
    return _userService;
  }

My userPool definition is like this, with great credentials

  static final userPoolSettings = new CognitoUserPool(
     xxxxxxxxx, xxxxxx);

Identity pool login with OpenId

Hi, I am trying to integrate aws with firebase as an identity provider. On flutter side I am using the firebase_auth package with the following code:

FirebaseUser user = await FirebaseAuth.instance.currentUser();
String fb_token;
user.getIdToken().then((result) {
  fb_token = result.token;
});

Now I am trying to connect to aws via an Identity pool. I am following the approach outlined in this SO post: https://stackoverflow.com/questions/47228334/firebase-as-identity-provider-with-cognito-aws/47252192
But so far I haven't had any luck finding a dart package that would support connecting to cognito with openId and identity pools. Do you have any suggestions on how to solve this problem or am I perhaps missing something?

Many thanks!

Non cognito related v4 signature

Hello,

I need some help with v4 signature.
I am generating pre signed url using generate_presigned_post call - https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html

This comes with a output like this:

{'url': 'https://mybucket.s3.amazonaws.com
'fields': {'acl': 'public-read',
'key': 'mykey', 'signature': 'mysignature', 'policy': 'mybase64 encoded policy'}

}

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.generate_presigned_post

Here is my dart code

    File vfile=File(filepath);
    var stream = new http.ByteStream(DelegatingStream.typed(vfile.openRead()));
    var length = await vfile.length();
    var uri = Uri.parse(signed_url['url']);
    var request = new http.MultipartRequest("POST", uri);

    String _secretKeyId = signed_url['fields']['signature'];
    String accessKeyId = signed_url['fields']['accessKeyId'];
    String sessionToken = signed_url['fields']['policy'];
    const _region = 'ap-south-1';
    String _s3Endpoint = signed_url['url'];
    String _policy=signed_url['fields']['policy'];

    dynamic policy_json=jsonDecode(utf8.decode(base64.decode(_policy)));

    final multipartFile = http.MultipartFile('file', stream, length,
      filename: path.basename(vfile.path));

    final presigned_policy = Policy.fromS3PresignedPost(
      'square-cinnamon.mp4',
      'my-s3-bucket',
      accessKeyId,
      15,
      length,
      region: _region);
    
    
    final key = SigV4.calculateSigningKey(
      _secretKeyId, presigned_policy.datetime, _region, 's3');
    final signature = SigV4.calculateSignature(key, presigned_policy.encode());
    final req = http.MultipartRequest("POST", uri);

    req.files.add(multipartFile);
    req.fields['key'] = presigned_policy.key;
    req.fields['acl'] = 'public-read';
    req.fields['X-Amz-Credential'] = presigned_policy.credential;
    req.fields['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
    req.fields['X-Amz-Date'] = presigned_policy.datetime;
    req.fields['Policy'] = presigned_policy.encode();
    req.fields['X-Amz-Signature'] = signature;
    req.fields['x-amz-security-token'] = "null"; // As we dont get this via generate_presigned_port call

    try {
      final res = await req.send();
      await for (var value in res.stream.transform(utf8.decoder)) {
        print(value);
      }
    } catch (e) {
      print("***** Exception in sending file *****" + e.toString());
    }

I get a error

<Error><Code>InvalidAccessKeyId</Code><Message>The AWS Access Key Id you provided does not exist in our records.</Message><AWSAccessKeyId>null</AWSAccessKeyId><RequestId>C7315716981A0E20</RequestId><HostId>R/Xkg4evb/oZR1EleAm+zHahUb5R9fFjL+wv5mcENwTG1EB95o0A5KojKq2RdhJyhHkLyxrrNFA=</HostId></Error>

Appreciate a response.

Thanks,
Shekar

passwordless login

Hello, everyone,

i am currently in the process of creating an app with a passwordless login. i found the following page:

https://techinscribed.com/passwordless-phone-number-authentication-using-aws-amplify-cognito/

I can also create an account with Lambda but unfortunately I can't login.

Here is my code and the error.

Future<CognitoUserSession> signIn(String phoneNumber) async { final cognitoUser = new CognitoUser(phoneNumber, _userPool); final authDetails = new AuthenticationDetails( username: phoneNumber ); CognitoUserSession session; try { session = await cognitoUser.authenticateUser(authDetails); } on CognitoUserNewPasswordRequiredException catch (e) { // handle New Password challenge print('handle New Password challenge $e'); } on CognitoUserMfaRequiredException catch (e) { // handle SMS_MFA challenge print('SMS_MFA challenge $e'); } on CognitoUserSelectMfaTypeException catch (e) { // handle SELECT_MFA_TYPE challenge print('SELECT_MFA_TYPE challenge $e'); } on CognitoUserMfaSetupException catch (e) { // handle MFA_SETUP challenge print('MFA_SETUP challenge $e'); } on CognitoUserTotpRequiredException catch (e) { // handle SOFTWARE_TOKEN_MFA challenge print('SOFTWARE_TOKEN_MFA challenge $e'); } on CognitoUserCustomChallengeException catch (e) { // handle CUSTOM_CHALLENGE challenge print('CUSTOM_CHALLENGE challenge $e'); } on CognitoUserConfirmationNecessaryException catch (e) { // handle User Confirmation Necessary print('User Confirmation Necessary $e'); } on CognitoClientException catch (e) { // handle Wrong Username and Password and Cognito Client print('Wrong Username and Password and Cognito Client $e'); } catch (e) { print('over error $e'); } //print(session.getAccessToken().getJwtToken()); return (session); }

Error

Wrong Username and Password and Cognito Client CognitoClientException{statusCode: 400, code: InvalidParameterException, name: InvalidParameterException, message: USER_SRP_AUTH is not enabled for the client.}

Is it possible that this type of login is not yet supported?

Check MFA status

Is there any way to see if the user has multiple factor authentication activated or not?

Keep logged after first login

Hi, i'm your using your library to develop my application that connects to cognito.
Phases of registration and login fuction correctly but i have problems in undertanding few things.
First of all, how keep my user loggend after his first login?
I red the example code but i don't understand what the init fuctions really does or better, I understand what i does but i don't understand how should I use it and the absence of the docs hits really hard.

Can you gently help me explaining the sequence of how should i can keep my user logged?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.