Skip to content

Commit 567ac28

Browse files
feat: add support for AWS_LAMBDA auth type (#1412)
1 parent c636b7b commit 567ac28

File tree

19 files changed

+466
-816
lines changed

19 files changed

+466
-816
lines changed

aws-api-appsync/src/main/java/com/amplifyframework/api/aws/AuthorizationType.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public enum AuthorizationType {
5656
*/
5757
AMAZON_COGNITO_USER_POOLS,
5858

59+
/**
60+
* Control access by implementing your own API authorization logic within an AWS Lambda function.
61+
*/
62+
AWS_LAMBDA,
63+
5964
/**
6065
* No authorization.
6166
*/
@@ -107,6 +112,8 @@ public static AuthorizationType from(AuthStrategy.Provider authRuleProvider) {
107112
return AWS_IAM;
108113
case API_KEY:
109114
return API_KEY;
115+
case FUNCTION:
116+
return AWS_LAMBDA;
110117
default:
111118
throw new IllegalArgumentException("No such authorization type: " + authRuleProvider.name());
112119
}

aws-api/src/main/java/com/amplifyframework/api/aws/AWSApiPlugin.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.amplifyframework.api.ApiPlugin;
2626
import com.amplifyframework.api.aws.auth.ApiRequestDecoratorFactory;
2727
import com.amplifyframework.api.aws.auth.AuthRuleRequestDecorator;
28+
import com.amplifyframework.api.aws.auth.RequestDecorator;
2829
import com.amplifyframework.api.aws.operation.AWSRestOperation;
2930
import com.amplifyframework.api.events.ApiEndpointStatusChangeEvent;
3031
import com.amplifyframework.api.events.ApiEndpointStatusChangeEvent.ApiEndpointStatus;
@@ -144,26 +145,35 @@ public void configure(
144145
configurator.applyConfiguration(okHttpClientBuilder);
145146
}
146147

148+
final ApiRequestDecoratorFactory requestDecoratorFactory = new ApiRequestDecoratorFactory(authProvider,
149+
apiConfiguration.getAuthorizationType(),
150+
apiConfiguration.getRegion(),
151+
apiConfiguration.getEndpointType(),
152+
apiConfiguration.getApiKey());
153+
147154
ClientDetails clientDetails = null;
148155
if (EndpointType.REST.equals(endpointType)) {
149-
final InterceptorFactory interceptorFactory =
150-
new AppSyncSigV4SignerInterceptorFactory(authProvider);
151156
if (apiConfiguration.getAuthorizationType() != AuthorizationType.NONE) {
152-
okHttpClientBuilder.addInterceptor(interceptorFactory.create(apiConfiguration));
157+
AuthorizationType authorizationType = apiConfiguration.getAuthorizationType();
158+
RequestDecorator decorator = requestDecoratorFactory.forAuthType(authorizationType);
159+
okHttpClientBuilder.addInterceptor(chain -> {
160+
try {
161+
return chain.proceed(decorator.decorate(chain.request()));
162+
} catch (ApiException.ApiAuthException apiAuthException) {
163+
throw new IOException("Failed to decorate request for authorization.", apiAuthException);
164+
}
165+
});
153166
}
154-
clientDetails = new ClientDetails(apiConfiguration, okHttpClientBuilder.build(), null, null);
167+
clientDetails = new ClientDetails(apiConfiguration,
168+
okHttpClientBuilder.build(),
169+
null,
170+
requestDecoratorFactory);
155171
restApis.add(apiName);
156172
} else if (EndpointType.GRAPHQL.equals(endpointType)) {
157173
final SubscriptionAuthorizer subscriptionAuthorizer =
158174
new SubscriptionAuthorizer(apiConfiguration, authProvider);
159175
final SubscriptionEndpoint subscriptionEndpoint =
160176
new SubscriptionEndpoint(apiConfiguration, gqlResponseFactory, subscriptionAuthorizer);
161-
final ApiRequestDecoratorFactory requestDecoratorFactory =
162-
new ApiRequestDecoratorFactory(authProvider,
163-
apiConfiguration.getAuthorizationType(),
164-
apiConfiguration.getRegion(),
165-
apiConfiguration.getApiKey());
166-
167177
clientDetails = new ClientDetails(apiConfiguration,
168178
okHttpClientBuilder.build(),
169179
subscriptionEndpoint,

aws-api/src/main/java/com/amplifyframework/api/aws/ApiAuthProviders.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.amplifyframework.api.aws.sigv4.ApiKeyAuthProvider;
2121
import com.amplifyframework.api.aws.sigv4.CognitoUserPoolsAuthProvider;
22+
import com.amplifyframework.api.aws.sigv4.FunctionAuthProvider;
2223
import com.amplifyframework.api.aws.sigv4.OidcAuthProvider;
2324

2425
import com.amazonaws.auth.AWSCredentialsProvider;
@@ -34,12 +35,14 @@ public final class ApiAuthProviders {
3435
private final AWSCredentialsProvider awsCredentialsProvider;
3536
private final CognitoUserPoolsAuthProvider cognitoUserPoolsAuthProvider;
3637
private final OidcAuthProvider oidcAuthProvider;
38+
private final FunctionAuthProvider functionAuthProvider;
3739

3840
private ApiAuthProviders(Builder builder) {
3941
this.apiKeyAuthProvider = builder.getApiKeyAuthProvider();
4042
this.awsCredentialsProvider = builder.getAWSCredentialsProvider();
4143
this.oidcAuthProvider = builder.getOidcAuthProvider();
4244
this.cognitoUserPoolsAuthProvider = builder.getCognitoUserPoolsAuthProvider();
45+
this.functionAuthProvider = builder.getFunctionAuthProvider();
4346
}
4447

4548
/**
@@ -74,6 +77,14 @@ public CognitoUserPoolsAuthProvider getCognitoUserPoolsAuthProvider() {
7477
return this.cognitoUserPoolsAuthProvider;
7578
}
7679

80+
/**
81+
* Gets the custom auth provider.
82+
* @return an implementation of {@link FunctionAuthProvider}
83+
*/
84+
public FunctionAuthProvider getFunctionAuthProvider() {
85+
return this.functionAuthProvider;
86+
}
87+
7788
/**
7889
* Statically gets the builder for conveniently
7990
* configuring an immutable instance of {@link ApiAuthProviders}.
@@ -101,6 +112,7 @@ public static final class Builder {
101112
private AWSCredentialsProvider awsCredentialsProvider;
102113
private CognitoUserPoolsAuthProvider cognitoUserPoolsAuthProvider;
103114
private OidcAuthProvider oidcAuthProvider;
115+
private FunctionAuthProvider functionAuthProvider;
104116

105117
/**
106118
* Assigns an API key Auth provider.
@@ -142,6 +154,16 @@ public ApiAuthProviders.Builder oidcAuthProvider(@NonNull OidcAuthProvider provi
142154
return ApiAuthProviders.Builder.this;
143155
}
144156

157+
/**
158+
* Assigns a serverless function (e.g. AWS Lambda) auth provider.
159+
* @param provider an instance of {@link FunctionAuthProvider}
160+
* @return this builder object for chaining
161+
*/
162+
public ApiAuthProviders.Builder functionAuthProvider(@NonNull FunctionAuthProvider provider) {
163+
ApiAuthProviders.Builder.this.functionAuthProvider = Objects.requireNonNull(provider);
164+
return ApiAuthProviders.Builder.this;
165+
}
166+
145167
/**
146168
* Creates an immutable instance of {@link ApiAuthProviders}
147169
* configured to this builder instance.
@@ -166,5 +188,9 @@ CognitoUserPoolsAuthProvider getCognitoUserPoolsAuthProvider() {
166188
OidcAuthProvider getOidcAuthProvider() {
167189
return ApiAuthProviders.Builder.this.oidcAuthProvider;
168190
}
191+
192+
FunctionAuthProvider getFunctionAuthProvider() {
193+
return ApiAuthProviders.Builder.this.functionAuthProvider;
194+
}
169195
}
170196
}

aws-api/src/main/java/com/amplifyframework/api/aws/AppSyncSigV4SignerInterceptorFactory.java

Lines changed: 0 additions & 130 deletions
This file was deleted.

aws-api/src/main/java/com/amplifyframework/api/aws/SubscriptionAuthorizer.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.amplifyframework.api.aws.sigv4.AppSyncV4Signer;
2525
import com.amplifyframework.api.aws.sigv4.CognitoUserPoolsAuthProvider;
2626
import com.amplifyframework.api.aws.sigv4.DefaultCognitoUserPoolsAuthProvider;
27+
import com.amplifyframework.api.aws.sigv4.FunctionAuthProvider;
2728
import com.amplifyframework.api.aws.sigv4.OidcAuthProvider;
2829
import com.amplifyframework.api.graphql.GraphQLRequest;
2930
import com.amplifyframework.core.Amplify;
@@ -105,6 +106,19 @@ private JSONObject createHeaders(GraphQLRequest<?> request,
105106
};
106107
}
107108
return forOidc(oidcProvider);
109+
case AWS_LAMBDA:
110+
FunctionAuthProvider functionAuthProvider = authProviders.getFunctionAuthProvider();
111+
if (functionAuthProvider == null) {
112+
functionAuthProvider = () -> {
113+
throw new ApiAuthException(
114+
"FunctionAuthProvider interface is not implemented.",
115+
"Please implement FunctionAuthProvider interface to return " +
116+
"appropriate token from the appropriate service."
117+
);
118+
};
119+
}
120+
return forAwsLambda(functionAuthProvider);
121+
108122
case NONE:
109123
default:
110124
return new JSONObject();
@@ -154,6 +168,20 @@ private JSONObject forOidc(OidcAuthProvider oidcProvider) throws ApiException {
154168
}
155169
}
156170

171+
private JSONObject forAwsLambda(FunctionAuthProvider functionAuthProvider) throws ApiException {
172+
try {
173+
return new JSONObject()
174+
.put("host", getHost())
175+
.put("Authorization", functionAuthProvider.getLatestAuthToken());
176+
} catch (JSONException jsonException) {
177+
// This error should never be thrown
178+
throw new ApiException(
179+
"Error constructing the authorization json for the AWS_LAMBDA auth type.",
180+
jsonException, AmplifyException.REPORT_BUG_TO_AWS_SUGGESTION
181+
);
182+
}
183+
}
184+
157185
private JSONObject forIam(
158186
AWSCredentialsProvider credentialsProvider,
159187
GraphQLRequest<?> request,

0 commit comments

Comments
 (0)