Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
802 views
in Technique[技术] by (71.8m points)

aws sdk - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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'

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...