Skip to content

Commit c4ff7b2

Browse files
authored
[IoT] Add support for custom authentication in IoT connection (#1457)
1 parent 679ee33 commit c4ff7b2

File tree

3 files changed

+350
-83
lines changed

3 files changed

+350
-83
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Change Log - AWS SDK for Android
22

3+
## [Release 2.16.8](https://github.com/aws/aws-sdk-android/releases/tag/release_v2.16.8)
4+
5+
### New Features
6+
7+
- **AWS IoT**
8+
- Added support for Custom Authorizers. AWS IoT allows you to define custom authorizers that allow you to manage your own authentication and authorization strategy using a custom authentication service and a Lambda function. Custom authorizers allow AWS IoT to authenticate your devices and authorize operations using bearer token authentication and authorization strategies. See [AWS IoT Custom Authentication](https://docs.aws.amazon.com/iot/latest/developerguide/custom-authentication.html) for more details.
9+
310
## [Release 2.16.7](https://github.com/aws/aws-sdk-android/releases/tag/release_v2.16.7)
411

512
### Misc. Updates

aws-android-sdk-iot/src/androidTest/java/com/amazonaws/mobileconnectors/iot/MqttManagerIntegrationTest.java

Lines changed: 172 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.amazonaws.mobileconnectors.iot;
1717

1818
import android.content.Context;
19+
import android.support.annotation.Nullable;
1920
import android.support.test.InstrumentationRegistry;
2021
import android.util.Log;
2122

@@ -39,6 +40,8 @@
3940
import com.amazonaws.services.iot.model.ResourceAlreadyExistsException;
4041
import com.amazonaws.services.iot.model.UpdateCertificateRequest;
4142

43+
import org.json.JSONException;
44+
import org.json.JSONObject;
4245
import org.junit.After;
4346
import org.junit.Before;
4447
import org.junit.Test;
@@ -83,7 +86,8 @@ public void setUp() throws Exception {
8386
Context appContext = InstrumentationRegistry.getTargetContext();
8487
KEYSTORE_PATH = appContext.getFilesDir().toString() + "/";
8588
System.out.println(KEYSTORE_PATH);
86-
credentialsProvider = new CognitoCachingCredentialsProvider(appContext, getPackageConfigure().getString("identity_pool_id"), Regions.US_EAST_1);
89+
credentialsProvider = new CognitoCachingCredentialsProvider(appContext,
90+
getPackageConfigure().getString(ConfigKey.IDENTITY_POOL_ID.toString()), Regions.US_EAST_1);
8791

8892
iotClient = new AWSIotClient(credentialsProvider);
8993

@@ -722,21 +726,48 @@ public void onFailure(Throwable exception) {
722726
}
723727

724728
@Test
725-
public void mqttWebSocket() throws Exception {
729+
public void testWebSocketWithIamAuth() throws Exception {
730+
websocketConnectionTest(AWSIotMqttManager.AuthenticationMode.IAM);
731+
}
732+
733+
@Test
734+
public void testWebSocketWithCustomAuth() throws Exception {
735+
websocketConnectionTest(AWSIotMqttManager.AuthenticationMode.CUSTOM_AUTH);
736+
}
726737

738+
private void websocketConnectionTest(AWSIotMqttManager.AuthenticationMode authMode)
739+
throws InterruptedException, JSONException {
727740
final ArrayList<AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus> statuses = new ArrayList<AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus>();
728741
final ArrayList<String> messages = new ArrayList<String>();
729742

730-
AWSIotMqttManager mqttManager = new AWSIotMqttManager("int-test-w-ws", Region.getRegion(Regions.US_EAST_1), endpointPrefix);
743+
AWSIotMqttManager mqttManager = new AWSIotMqttManager("int-test-w-ws",
744+
Region.getRegion(Regions.US_EAST_1), endpointPrefix);
731745

732746
mqttManager.setAutoReconnect(false);
733-
// connect using WebSockets and IAM credentials
734-
mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
735-
@Override
736-
public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
737-
statuses.add(status);
738-
}
739-
});
747+
748+
if (AWSIotMqttManager.AuthenticationMode.CUSTOM_AUTH.equals(authMode)) {
749+
ConnectionParams connectionParams = ConnectionParams.fromPackageConfig(getPackageConfigure());
750+
// connect using WebSockets and custom authentication token
751+
mqttManager.connect(
752+
connectionParams.getTokenKeyName(),
753+
connectionParams.getToken(),
754+
connectionParams.getTokenSignature(),
755+
connectionParams.getCustomerAuthorizerName(),
756+
new AWSIotMqttClientStatusCallback() {
757+
@Override
758+
public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
759+
statuses.add(status);
760+
}
761+
});
762+
} else if (AWSIotMqttManager.AuthenticationMode.IAM.equals(authMode)) {
763+
// connect using WebSockets and IAM credentials
764+
mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
765+
@Override
766+
public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
767+
statuses.add(status);
768+
}
769+
});
770+
}
740771

741772
Thread.sleep(3000);
742773

@@ -778,21 +809,47 @@ public void onMessageArrived(String topic, byte[] data) {
778809
}
779810

780811
@Test
781-
public void mqttWebSocketReconnect() throws Exception {
812+
public void testWebsocketReconnectWithIam() throws Exception {
813+
websocketReconnectionTest(AWSIotMqttManager.AuthenticationMode.IAM);
814+
}
815+
816+
@Test
817+
public void testWebsocketReconnectWithCustomAuth() throws Exception {
818+
websocketReconnectionTest(AWSIotMqttManager.AuthenticationMode.CUSTOM_AUTH);
819+
}
782820

821+
private void websocketReconnectionTest(AWSIotMqttManager.AuthenticationMode authMode)
822+
throws InterruptedException, JSONException {
783823
final ArrayList<AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus> statuses = new ArrayList<AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus>();
784824
final ArrayList<String> messages = new ArrayList<String>();
785825

786-
AWSIotMqttManager mqttManager = new AWSIotMqttManager("int-test-ws-rc", Region.getRegion(Regions.US_EAST_1), endpointPrefix);
826+
AWSIotMqttManager mqttManager = new AWSIotMqttManager("int-test-ws-rc",
827+
Region.getRegion(Regions.US_EAST_1), endpointPrefix);
787828

788829
mqttManager.setAutoReconnect(true);
789-
// connect using WebSockets and IAM credentials
790-
mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
791-
@Override
792-
public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
793-
statuses.add(status);
794-
}
795-
});
830+
if (AWSIotMqttManager.AuthenticationMode.IAM.equals(authMode)) {
831+
// connect using WebSockets and IAM credentials
832+
mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
833+
@Override
834+
public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
835+
statuses.add(status);
836+
}
837+
});
838+
} else if (AWSIotMqttManager.AuthenticationMode.CUSTOM_AUTH.equals(authMode)) {
839+
ConnectionParams connectionParams = ConnectionParams.fromPackageConfig(getPackageConfigure());
840+
// connect using WebSockets and custom authentication token
841+
mqttManager.connect(
842+
connectionParams.getTokenKeyName(),
843+
connectionParams.getToken(),
844+
connectionParams.getTokenSignature(),
845+
connectionParams.getCustomerAuthorizerName(),
846+
new AWSIotMqttClientStatusCallback() {
847+
@Override
848+
public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
849+
statuses.add(status);
850+
}
851+
});
852+
}
796853

797854
Thread.sleep(3000);
798855

@@ -1163,7 +1220,9 @@ private void deletePolicyAndCertificate() {
11631220
Log.d(TAG, "Detaching the policy from the certificate.");
11641221
DetachPolicyRequest detachPolicyRequest = new DetachPolicyRequest();
11651222
detachPolicyRequest.setPolicyName(IOT_POLICY_NAME);
1166-
detachPolicyRequest.setTarget(this.certResult.getCertificateArn());
1223+
if (this.certResult != null) {
1224+
detachPolicyRequest.setTarget(this.certResult.getCertificateArn());
1225+
}
11671226
iotClient.detachPolicy(detachPolicyRequest);
11681227

11691228
// delete policy
@@ -1190,4 +1249,97 @@ private void deletePolicyAndCertificate() {
11901249
deleteCertificateRequest.setCertificateId(this.certResult.getCertificateId());
11911250
iotClient.deleteCertificate(deleteCertificateRequest);
11921251
}
1252+
1253+
static final class ConnectionParams {
1254+
private final String tokenKeyName;
1255+
private final String token;
1256+
private final String tokenSignature;
1257+
private final String customerAuthorizerName;
1258+
1259+
ConnectionParams(final String tokenKeyName,
1260+
final String token,
1261+
final String tokenSignature,
1262+
final String customerAuthorizerName) {
1263+
this.tokenKeyName = tokenKeyName;
1264+
this.token = token;
1265+
this.tokenSignature = tokenSignature;
1266+
this.customerAuthorizerName = customerAuthorizerName;
1267+
}
1268+
1269+
1270+
String getTokenKeyName() {
1271+
return tokenKeyName;
1272+
}
1273+
1274+
String getToken() {
1275+
return token;
1276+
}
1277+
1278+
String getTokenSignature() {
1279+
return tokenSignature;
1280+
}
1281+
1282+
public String getCustomerAuthorizerName() {
1283+
return customerAuthorizerName;
1284+
}
1285+
1286+
static ConnectionParams fromPackageConfig(JSONObject config) throws JSONException {
1287+
return new ConnectionParams(
1288+
config.getString(ConfigKey.TOKEN_KEY_NAME.toString()),
1289+
config.getString(ConfigKey.TOKEN.toString()),
1290+
config.getString(ConfigKey.TOKEN_SIGNATURE.toString()),
1291+
config.getString(ConfigKey.CUSTOM_AUTHORIZER_NAME.toString())
1292+
);
1293+
}
1294+
}
1295+
1296+
/**
1297+
* Iot instrumented tests need following configuration values.
1298+
*/
1299+
enum ConfigKey {
1300+
/**
1301+
* HTTP request header key for the custom authorization token.
1302+
*/
1303+
TOKEN_KEY_NAME("token_key_name"),
1304+
1305+
/**
1306+
* Custom authorization token used to connect via IoT custom authorizer.
1307+
*/
1308+
TOKEN("token"),
1309+
1310+
/**
1311+
* Custom authorization token signature.
1312+
*/
1313+
TOKEN_SIGNATURE("token_signature"),
1314+
1315+
/**
1316+
* Name of the AWS IoT custom authorizer to be used.
1317+
*/
1318+
CUSTOM_AUTHORIZER_NAME("custom_authorizer_name"),
1319+
1320+
/**
1321+
* Identity pool id used to authorize connection via IAM.
1322+
*/
1323+
IDENTITY_POOL_ID("identity_pool_id");
1324+
1325+
private final String configKey;
1326+
1327+
ConfigKey(String configKey) {
1328+
this.configKey = configKey;
1329+
}
1330+
1331+
@Override
1332+
public String toString() {
1333+
return configKey;
1334+
}
1335+
1336+
public static ConfigKey fromString(@Nullable String value) {
1337+
for (ConfigKey configKey : values()) {
1338+
if (configKey.toString().equals(value)) {
1339+
return configKey;
1340+
}
1341+
}
1342+
throw new IllegalArgumentException("No config key with value = " + value);
1343+
}
1344+
}
11931345
}

0 commit comments

Comments
 (0)