3

I have been trying to create a user login using the aws-sdk-cpp. I essentially would like a user to register using my app as a user (which will add them to the cognito user pool - I have this working), and then log-in. This login will then provide them access to a specific bucket in the account. I have created a policy which should allow cognito users to access the bucket using the below.

http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_s3_cognito-bucket.html

I have created a user pool and a federated identity in the AWS console, and enabled cognito as a identity provider in the user pool, so I think that side is all correct.

I have tried using the SDK to put this authentication together, using the integration tests from identity-management as a starting point.

Aws::SDKOptions options;
Aws::InitAPI(options);
{
    const Aws::String userPool_id = "eu-west-1_xxxxxxxxx";
    const Aws::String client_id = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
    const Aws::String region_id = "eu-west-1";
    const Aws::String identityPool_id = "eu-west-1:xxxxxxxxxxxxxxxxx";
    const Aws::String account_id = "xxxxxxxxxxxx";

    Aws::Client::ClientConfiguration clientConfig;
    clientConfig.region = region_id;

    std::shared_ptr<CustomPersistentCognitoIdentityProvider> persistent_provider = std::make_shared<CustomPersistentCognitoIdentityProvider>();
    persistent_provider->SetAccountId(account_id);
    persistent_provider->SetIdentityPoolId(identityPool_id);

    //Aws::Map<Aws::String, LoginAccessTokens> logins;
    //LoginAccessTokens loginAccessTokens;
    //loginAccessTokens.accessToken = LOGIN_ID;
    //logins[LOGIN_KEY] = loginAccessTokens;
    //persistent_provider->SetLogins("cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxxxxxx", client_id);

    auto cognito_client = std::make_shared<Aws::CognitoIdentity::CognitoIdentityClient>(clientConfig);

    Aws::CognitoIdentity::Model::GetIdRequest id_request;
    id_request.SetAccountId(account_id);
    id_request.SetIdentityPoolId(identityPool_id);
    id_request.AddLogins("cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxxxxx", client_id);
    id_request.AddLogins("USERNAME", "tester@xxxxxxxxx");
    id_request.AddLogins("PASSWORD", "xxxxxxxxxxxxx");
    cognito_client->GetId(id_request);

    Aws::Auth::CognitoCachingAuthenticatedCredentialsProvider authenticated_provider(persistent_provider, cognito_client);
    Aws::Auth::AWSCredentials credentials = authenticated_provider.GetAWSCredentials();

    std::cout << "AccessKeyID : " << credentials.GetAWSAccessKeyId() << std::endl;
    std::cout << "SecretKey : " << credentials.GetAWSSecretKey() << std::endl;

    Aws::S3::S3Client s3_client(credentials, clientConfig);

    S3ListObject(s3_client, "cloudtesting");
    // do stuff with the s3 bucket 
}

Aws::ShutdownAPI(options);

The code above returns empty strings for the access keys.

Adding some debug at the GetId call return:

Request error: NotAuthorizedException Invalid login token. Not a valid OpenId Connect identity token.

I've obviously missed something here, or in the setup. Any suggestions/help/code examples would be greatly appreciated!

manishpin
  • 33
  • 4
  • 1
    Your first issue is that you are calling Cognito Federated Identity with the username/password instead of Cognito Identity Provider. Cognito Identity Provider is for authenticating your user and getting an OIDC token, Cognito Federated Identity is for exchanging the token you have obtained for AWS credentials. Before I go too deep into the answer are you doing this from an untrusted environment (i.e. mobile device or end user's desktop) or from your server backend? – perpil Dec 14 '17 at 23:31
  • Thanks for replying. This is all going to client side code, so yes, it will be an untrusted environment. I mocked up some server side code using `Aws::CognitoIdentityProvider::Model::AdminInitiateAuthRequest`. But this obviously is unsuitable from client side code. Some clarifications on how I authenticate my username/password would be appreciated and then exchange for AWS credentials would be greatly appreciated! – manishpin Dec 15 '17 at 10:15

1 Answers1

3

In order to authenticate with the Cognito User Pool, you have to use the CognitoIdentityProviderClient (see aws/cognito-idp/CognitoIdentityProviderClient.h). It uses the Secure Remote Password protocol (SRP) for authentication, which you unfortunately have to implement yourself. You first make a call to InitiateAuth, which then reply with some info to which you have to respond with RespondToAuthChallenge.

This is implemented in the Amazon Cognito Identity SDK for JavaScript which you can use as a reference.

  • Thanks, your explanation really helped my misunderstanding. I have now implemented SRP using openssl and the login now seems to be behaving well. – manishpin Jan 09 '18 at 15:51
  • Just to add, this [blog post](http://blog.mmlac.com/aws-cognito-srp-login-c-sharp-dot-net/) for the SRP implementation helped immensely. Many thanks to the original author @mmlac – manishpin Jan 09 '18 at 15:55
  • 4
    For anyone returning to this question, I have uploaded my SRP code to gist https://gist.github.com/manishpin/f7d8e9af84068bd9b0220bb2c7d14a4d – manishpin Feb 23 '18 at 10:45