Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call API Gateway with Cognito Credentials through retrofit2 on Android?

I use retrofit2 in my android apps for any http/rest call. Now I need to call an api generated with Amazon AWS API Gateway.

The AWS documentation say I should generate the client code throw the API Gateway console and use the class ApiClientFactory to build the request:

ApiClientFactory factory = new ApiClientFactory();

// Use CognitoCachingCredentialsProvider to provide AWS credentials
// for the ApiClientFactory
AWSCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
        context,          // activity context
        "identityPoolId", // Cognito identity pool id
        Regions.US_EAST_1 // region of Cognito identity pool
};

factory.credentialsProvider(credentialsProvider);

// Create an instance of your SDK (this should come from the generated code).
final MyApiClient client = factory.build(MyApiClient.class);

// Invoke a method (e.g., 'parentPath1Get(param1,body)') exposed by your SDK. 
// Here the method's return type is OriginalModel.
OriginalModel output = client.parentPath1Get(param1,body);

// You also have access to your API's models.
OriginalModel myModel = new OriginalModel();
myModel.setStreetAddress(streetAddress);
myModel.setCity(city);
myModel.setState(state);
myModel.setStreetNumber(streetNumber);
myModel.setNested(nested);
myModel.setPoBox(poBox);

Instead I would like to define the API like I would with retrofit: with an interface I write, connect it to RxJava, OkHttp etc...

My question is: how can I sign the retrofit requests with Cognito Identity Provider?

like image 420
Daniele Segato Avatar asked Aug 03 '16 07:08

Daniele Segato


People also ask

How do I authorize API gateway with Cognito?

3.4 Create Authorizer in APINavigate to the API Gateway service to your API. In the left menu choose Authorizers and click the Create New Authorizer button. Give it the name MyAuthorizer, choose Cognito as Type and select the Cognito User Pool MyFirstUserPool.

How do I create authentication APIs with AWS Cognito?

Use the Amazon Cognito console, CLI/SDK, or API to create a user pool—or use one that's owned by another AWS account. Use the API Gateway console, CLI/SDK, or API to create an API Gateway authorizer with the chosen user pool. Use the API Gateway console, CLI/SDK, or API to enable the authorizer on selected API methods.

How do I get my Cognito credentials?

To do so, open the Amazon Cognito console , choose Manage identity pools, select your identity pool, choose Edit identity Pool, specify your authenticated and unauthenticated roles, and save the changes. To use the Amazon Web Services Documentation, Javascript must be enabled.


2 Answers

It took me several days to figure out how to make it work. Don't know why they don't point out the class instead of dozen of document pages. There are 4 steps in total, you must call in worker thread, I am using Rxjava but you can use AsyncTask instead:

    Observable.create((Observable.OnSubscribe<String>) subscriber -> {
//Step 1: Get credential, ask server team for Identity pool id and regions            
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
                this, // Context
                "Identity Pool ID", // Identity Pool ID
                Regions.US_EAST_1 // Region
            );

//Step 2: Get these 3 three keys, test with postman v4.9.3 to see if identity is correct  
            String identityId = credentialsProvider.getIdentityId();
            Log.show("identityId = " + identityId);

            String AccessKey = credentialsProvider.getCredentials().getAWSAccessKeyId();
            String SecretKey = credentialsProvider.getCredentials().getAWSSecretKey();
            String SessionKey = credentialsProvider.getCredentials().getSessionToken();

            Log.show("AccessKey = " + AccessKey);
            Log.show("SecretKey = " + SecretKey);
            Log.show("SessionKey = " + SessionKey);
//Step 3: Create an aws requets and sign by using AWS4Signer class
            AmazonWebServiceRequest amazonWebServiceRequest = new AmazonWebServiceRequest() {
            };

            ClientConfiguration clientConfiguration = new ClientConfiguration();

            String API_GATEWAY_SERVICE_NAME = "execute-api";

            Request request = new DefaultRequest(amazonWebServiceRequest,API_GATEWAY_SERVICE_NAME);
            request.setEndpoint(URI.create("YOUR_URI"));
            request.setHttpMethod(HttpMethodName.GET);

            AWS4Signer signer = new AWS4Signer();
            signer.setServiceName(API_GATEWAY_SERVICE_NAME);
            signer.setRegionName(Region.getRegion(Regions.US_EAST_1).getName());
            signer.sign(request, credentialsProvider.getCredentials());

            Log.show("Request header " + request.getHeaders().toString());
//Step 4: Create new request with authorization headers 

            OkHttpClient httpClient = new OkHttpClient();
            Map<String, String> headers = request.getHeaders();
            List<String> key = new ArrayList<String>();
            List<String> value = new ArrayList<String>();

            for (Map.Entry<String, String> entry : headers.entrySet())
            {
                key.add(entry.getKey());
                value.add(entry.getValue());
            }

            try {
                okhttp3.Request request2 = new okhttp3.Request.Builder()
                        .url("Your_url") // remember to add / to the end of the url, otherwise the signature will be different 
                        .addHeader(key.get(0), value.get(0))
                        .addHeader(key.get(1), value.get(1))
                        .addHeader(key.get(2), value.get(2))
                        .addHeader(key.get(3), value.get(3))

                        .addHeader("Content-Type", "application/x-www-form-urlencoded")
                        .build();
                Response response = null;

                response = httpClient.newCall(request2).execute();
                String body = response.body().string();
                Log.show("response " + body);
            } catch (Exception e) {
                Log.show("error " + e);
            }

            subscriber.onNext(identityId);

        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                Log.show("Throwable = " + e.getMessage());
            }

            @Override
            public void onNext(String s) {

            }
        });

The key here is the AWS4Signer class does 4 steps as documented here, you don't need to build one from scratch. In order to use AWS4Signer and AmazonWebServiceRequest, you need to import aws sdk in gradle:

compile 'com.amazonaws:aws-android-sdk-cognito:2.3.9'
like image 165
thanhbinh84 Avatar answered Oct 21 '22 09:10

thanhbinh84


Created an OkHttp interceptor based on @thanhbinh84 answer. Give it a try: https://github.com/Ghedeon/AwsInterceptor

like image 35
Ghedeon Avatar answered Oct 21 '22 08:10

Ghedeon