Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for AWS_LAMBDA auth mode #358

Merged
merged 2 commits into from
Jul 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.amazonaws.mobileconnectors.appsync.fetcher.AppSyncResponseFetchers;
import com.amazonaws.mobileconnectors.appsync.retry.RetryInterceptor;
import com.amazonaws.mobileconnectors.appsync.sigv4.APIKeyAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.AWSLambdaAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncSigV4SignerInterceptor;
import com.amazonaws.mobileconnectors.appsync.sigv4.BasicAPIKeyAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.CognitoUserPoolsAuthProvider;
Expand Down Expand Up @@ -105,7 +106,8 @@ private enum AuthMode {
API_KEY("API_KEY"),
AWS_IAM("AWS_IAM"),
AMAZON_COGNITO_USER_POOLS("AMAZON_COGNITO_USER_POOLS"),
OPENID_CONNECT("OPENID_CONNECT");
OPENID_CONNECT("OPENID_CONNECT"),
AWS_LAMBDA("AWS_LAMBDA");

private final String name;

Expand Down Expand Up @@ -159,6 +161,8 @@ private AWSAppSyncClient(AWSAppSyncClient.Builder builder) {
appSyncSigV4SignerInterceptor = new AppSyncSigV4SignerInterceptor(builder.mCognitoUserPoolsAuthProvider, builder.mRegion.getName());
} else if (builder.mOidcAuthProvider != null) {
appSyncSigV4SignerInterceptor = new AppSyncSigV4SignerInterceptor(builder.mOidcAuthProvider);
} else if (builder.mAWSLambdaAuthProvider != null) {
appSyncSigV4SignerInterceptor = new AppSyncSigV4SignerInterceptor(builder.mAWSLambdaAuthProvider);
} else if (builder.mApiKey != null) {
appSyncSigV4SignerInterceptor = new AppSyncSigV4SignerInterceptor(builder.mApiKey, builder.mRegion.getName(), getClientSubscriptionUUID(builder.mApiKey.getAPIKey()));
} else {
Expand Down Expand Up @@ -309,6 +313,7 @@ public static class Builder {
APIKeyAuthProvider mApiKey;
CognitoUserPoolsAuthProvider mCognitoUserPoolsAuthProvider;
OidcAuthProvider mOidcAuthProvider;
AWSLambdaAuthProvider mAWSLambdaAuthProvider;
NormalizedCacheFactory mNormalizedCacheFactory;
CacheKeyResolver mResolver;
ConflictResolverInterface mConflictResolver;
Expand Down Expand Up @@ -365,6 +370,11 @@ public Builder oidcAuthProvider(OidcAuthProvider oidcAuthProvider) {
return this;
}

public Builder awsLamdbaAuthProvider(AWSLambdaAuthProvider awsLambdaAuthProvider) {
mAWSLambdaAuthProvider = awsLambdaAuthProvider;
return this;
}

public Builder serverUrl(String serverUrl) {
mServerUrl = serverUrl;
return this;
Expand Down Expand Up @@ -525,6 +535,7 @@ public AWSAppSyncClient build() {
authModeObjects.put(mCredentialsProvider, AuthMode.AWS_IAM);
authModeObjects.put(mCognitoUserPoolsAuthProvider, AuthMode.AMAZON_COGNITO_USER_POOLS);
authModeObjects.put(mOidcAuthProvider, AuthMode.OPENID_CONNECT);
authModeObjects.put(mAWSLambdaAuthProvider, AuthMode.AWS_LAMBDA);
authModeObjects.remove(null);

// Validate if only one Auth object is passed in to the builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.amazonaws.http.HttpMethodName;
import com.amazonaws.mobile.config.AWSConfiguration;
import com.amazonaws.mobileconnectors.appsync.sigv4.APIKeyAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.AWSLambdaAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncV4Signer;
import com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.CognitoUserPoolsAuthProvider;
Expand Down Expand Up @@ -45,6 +46,7 @@ class SubscriptionAuthorizer {
private final OidcAuthProvider mOidcAuthProvider;
private final AWSCredentialsProvider mCredentialsProvider;
private final CognitoUserPoolsAuthProvider mCognitoUserPoolsAuthProvider;
private final AWSLambdaAuthProvider mAWSLambdaAuthProvider;
private final String mServerUrl;
private final APIKeyAuthProvider mApiKeyProvider;

Expand All @@ -54,6 +56,7 @@ class SubscriptionAuthorizer {
this.mOidcAuthProvider = builder.mOidcAuthProvider;
this.mCredentialsProvider = builder.mCredentialsProvider;
this.mCognitoUserPoolsAuthProvider = builder.mCognitoUserPoolsAuthProvider;
this.mAWSLambdaAuthProvider = builder.mAWSLambdaAuthProvider;
this.mServerUrl = builder.mServerUrl;
this.mApiKeyProvider = builder.mApiKey;
}
Expand Down Expand Up @@ -85,6 +88,8 @@ JSONObject getAuthorizationDetails(boolean connectionFlag,
return getAuthorizationDetailsForUserpools();
case "OPENID_CONNECT" :
return getAuthorizationDetailsForOidc();
case "AWS_LAMBDA" :
return getAuthorizationDetailsForAwsLambda();
default :
throw new RuntimeException("Invalid AuthMode read from awsconfiguration.json.");
}
Expand Down Expand Up @@ -197,6 +202,16 @@ private JSONObject getAuthorizationDetailsForOidc() {
}
}

private JSONObject getAuthorizationDetailsForAwsLambda() {
try {
return new JSONObject()
.put("host", getHost(mServerUrl))
.put("Authorization", mAWSLambdaAuthProvider.getLatestAuthToken());
} catch (JSONException | MalformedURLException e) {
throw new RuntimeException("Error constructing authorization message json", e);
}
}

private static String getHost(String apiUrl) throws MalformedURLException {
return new URL(apiUrl).getHost();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.amazonaws.mobileconnectors.appsync.sigv4;

public interface AWSLambdaAuthProvider {
public String getLatestAuthToken();
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ public class AppSyncSigV4SignerInterceptor implements Interceptor {

private final CognitoUserPoolsAuthProvider cognitoUserPoolsAuthProvider;
private final OidcAuthProvider oidcAuthProvider;
private final AWSLambdaAuthProvider awsLambdaAuthProvider;
private final String awsRegion;
private final AuthMode authMode;

private enum AuthMode {
API_KEY,
IAM,
OIDC_AUTHORIZATION_TOKEN,
USERPOOLS_AUTHORIZATION_TOKEN
USERPOOLS_AUTHORIZATION_TOKEN,
AWS_LAMBDA_AUTHORIZATION_TOKEN
}

public AppSyncSigV4SignerInterceptor(APIKeyAuthProvider apiKey, final String awsRegion, final String subscriberUUID){
Expand All @@ -61,6 +63,7 @@ public AppSyncSigV4SignerInterceptor(APIKeyAuthProvider apiKey, final String aws
this.credentialsProvider = null;
this.cognitoUserPoolsAuthProvider = null;
this.oidcAuthProvider = null;
this.awsLambdaAuthProvider = null;
poojamat marked this conversation as resolved.
Show resolved Hide resolved
authMode = AuthMode.API_KEY;
this.subscriberUUID = subscriberUUID;
}
Expand All @@ -71,6 +74,7 @@ public AppSyncSigV4SignerInterceptor(AWSCredentialsProvider credentialsProvider,
this.apiKey = null;
this.cognitoUserPoolsAuthProvider = null;
this.oidcAuthProvider = null;
this.awsLambdaAuthProvider = null;
authMode = AuthMode.IAM;
subscriberUUID = null;
}
Expand All @@ -81,6 +85,7 @@ public AppSyncSigV4SignerInterceptor(CognitoUserPoolsAuthProvider cognitoUserPoo
this.apiKey = null;
this.cognitoUserPoolsAuthProvider = cognitoUserPoolsAuthProvider;
this.oidcAuthProvider = null;
this.awsLambdaAuthProvider = null;
authMode = AuthMode.USERPOOLS_AUTHORIZATION_TOKEN;
subscriberUUID = null;
}
Expand All @@ -91,10 +96,22 @@ public AppSyncSigV4SignerInterceptor(OidcAuthProvider oidcAuthProvider){
this.apiKey = null;
this.cognitoUserPoolsAuthProvider = null;
this.oidcAuthProvider = oidcAuthProvider;
this.awsLambdaAuthProvider = null;
authMode = AuthMode.OIDC_AUTHORIZATION_TOKEN;
subscriberUUID = null;
}

public AppSyncSigV4SignerInterceptor(AWSLambdaAuthProvider awsLambdaAuthProvider) {
this.credentialsProvider = null;
this.awsRegion = null;
this.apiKey = null;
this.cognitoUserPoolsAuthProvider = null;
this.oidcAuthProvider = null;
this.awsLambdaAuthProvider = awsLambdaAuthProvider;
authMode = AuthMode.AWS_LAMBDA_AUTHORIZATION_TOKEN;
subscriberUUID = null;
}

@Override
public Response intercept(Chain chain) throws IOException {
Log.d(TAG, "Signer Interceptor called");
Expand Down Expand Up @@ -155,6 +172,13 @@ public Response intercept(Chain chain) throws IOException {
IOException ioe = new IOException("Failed to retrieve OIDC token.", e);
throw ioe;
}
} else if (AuthMode.AWS_LAMBDA_AUTHORIZATION_TOKEN.equals(authMode)) {
try {
dr.addHeader(AUTHORIZATION, awsLambdaAuthProvider.getLatestAuthToken());
} catch (Exception e) {
IOException ioe = new IOException("Failed to retrieve AWS Lambda authorization token.", e);
throw ioe;
}
}

//Copy the signed/credentialed request back into an OKHTTP Request object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.amazonaws.auth.CognitoCredentialsProvider;
import com.amazonaws.mobile.config.AWSConfiguration;
import com.amazonaws.mobileconnectors.appsync.sigv4.APIKeyAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.AWSLambdaAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.BasicAPIKeyAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.OidcAuthProvider;
Expand All @@ -19,7 +20,6 @@
import com.apollographql.apollo.internal.RealAppSyncCall;
import com.apollographql.apollo.internal.RealAppSyncSubscriptionCall;
import com.apollographql.apollo.internal.subscription.SubscriptionManager;

import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;
Expand All @@ -29,12 +29,10 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;

import java.io.ByteArrayInputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;

import static junit.framework.TestCase.assertEquals;
Expand Down Expand Up @@ -233,6 +231,22 @@ public String getLatestAuthToken() {
assertNotNull(awsAppSyncClient);
}

@Test
public void testAWSLambdaAuthProvider() {
awsConfiguration.setConfiguration("OpenidConnect");
final AWSAppSyncClient awsAppSyncClient = AWSAppSyncClient.builder()
.context(shadowContext)
.awsConfiguration(awsConfiguration)
.awsLamdbaAuthProvider(new AWSLambdaAuthProvider() {
@Override
public String getLatestAuthToken() {
return "AWS_LAMBDA_AUTHORIZATION_TOKEN";
}
})
.build();
assertNotNull(awsAppSyncClient);
}

@Test
public void testRealAppSyncSubscriptionCallErrorHandling() throws InterruptedException {
RealAppSyncSubscriptionCall<Object> call = new RealAppSyncSubscriptionCall(mockSubscription, mockSubscriptionManager, mockApolloClient, mockLogger, mockSubscriptionMetadataRequest);
Expand Down