Skip to content

Commit 0499ea5

Browse files
authored
Add support for custom auth in AWSMobileClient (#1178)
* Add support for custom auth in AWSMobileClient * Add custom auth test * Add license files * Refactoring * Update changelog * Review suggestions * Review Feedback * Review feedback * Update CHANGELOG.md * Update CHANGELOG.md
1 parent 999a9bd commit 0499ea5

14 files changed

+562
-228
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
### New Features
66

77
- **AWS Mobile Client**
8-
- **Breaking API Change** `SignUpResult` available in the user callback provided during sign up now contains UserSub(UID).
8+
- **Breaking API Change**
9+
- `SignUpResult` available in the user callback provided during sign up now contains UserSub(UID)
10+
- Deprecated APIs `getCredentialsProvider`, `setCredentialsProvider`, `initialize(Context)` and `initialize(Context, AWSStartupHandler)` have been removed
11+
- AWSMobileClient now supports Cognito Custom Authentication flow. See relevant [cognito docs](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#amazon-cognito-user-pools-custom-authentication-flow) and [amplify docs](https://aws-amplify.github.io/docs/android/authentication#customizing-authentication-flow) for details
12+
- `confirmSignIn(final Map<String, String> signInChallengeResponse)` now throws `IllegalStateException` if `confirmSignIn` is called after signIn has succeeded. This matches the behavior of the overloaded version of this method, viz. `confirmSignIn(final String signInChallengeResponse, final Callback<SignInResult> callback)`
913

1014
- **Amazon CognitoIdentityProvider**
1115
- **Breaking API Change** `SignUpHandler` now receives `SignUpResult` upon success instead of a ConfirmationState and `CognitoUserCodeDeliveryDetails`
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.amazonaws.mobile.client;
19+
20+
import android.content.Context;
21+
import android.support.test.InstrumentationRegistry;
22+
import android.util.Log;
23+
24+
25+
import com.amazonaws.auth.AWSCredentials;
26+
import com.amazonaws.mobile.client.results.SignInResult;
27+
import com.amazonaws.mobile.client.results.Token;
28+
import com.amazonaws.mobile.client.results.Tokens;
29+
import com.amazonaws.mobile.config.AWSConfiguration;
30+
import com.amazonaws.mobileconnectors.cognitoidentityprovider.util.CognitoServiceConstants;
31+
import org.junit.BeforeClass;
32+
import org.junit.Test;
33+
34+
import java.util.Date;
35+
import java.util.HashMap;
36+
import java.util.Map;
37+
import java.util.concurrent.CountDownLatch;
38+
39+
import static com.amazonaws.mobile.auth.core.internal.util.ThreadUtils.runOnUiThread;
40+
import static org.junit.Assert.assertNotNull;
41+
import static org.junit.Assert.assertTrue;
42+
43+
public class AWSMobileClientCustomAuthTest extends AWSMobileClientTestBase {
44+
45+
private static Context appContext;
46+
private static AWSMobileClient auth;
47+
48+
private static final CountDownLatch signUpLatch = new CountDownLatch(1);
49+
private static final String USERNAME = "customAuthTestUser";
50+
private static final String PASSWORD = "Test@123";
51+
private static final String TAG = AWSMobileClientCustomAuthTest.class.getSimpleName();
52+
53+
@BeforeClass
54+
public static void beforeClass() throws Exception {
55+
appContext = InstrumentationRegistry.getTargetContext();
56+
final CountDownLatch latch = new CountDownLatch(1);
57+
final AWSConfiguration awsConfiguration = new AWSConfiguration(appContext);
58+
awsConfiguration.setConfiguration("Auth1");
59+
assert awsConfiguration.optJsonObject("Auth").has("authenticationFlowType");
60+
AWSMobileClient.getInstance().initialize(appContext, awsConfiguration, new Callback<UserStateDetails>() {
61+
@Override
62+
public void onResult(UserStateDetails result) {
63+
latch.countDown();
64+
}
65+
66+
@Override
67+
public void onError(Exception e) {
68+
Log.e(TAG, "onError: ", e);
69+
latch.countDown();
70+
}
71+
});
72+
latch.await();
73+
auth = AWSMobileClient.getInstance();
74+
}
75+
76+
/**
77+
* This test needs following backend set up to complete successfully :
78+
*
79+
* User - This test assumes presence of a user named `customAuthTestUser` in the test user pool
80+
* App Client - There should be an app client with "Only allow Custom Authentication (CUSTOM_AUTH_FLOW_ONLY)" enabled.
81+
* Lambda Triggers - It needs following lambda triggers set in the test userpool:
82+
* Define Auth Lambda Trigger :
83+
* ```
84+
* exports.handler = function(event, context) {
85+
* if (event.request.session.length == 1 && event.request.session[0].challengeName == 'SRP_A') {
86+
* event.response.issueTokens = false;
87+
* event.response.failAuthentication = false;
88+
* event.response.challengeName = 'CUSTOM_CHALLENGE';
89+
* } else if (event.request.session.length == 2 && event.request.session[1].challengeName == 'CUSTOM_CHALLENGE' && event.request.session[1].challengeResult == true) {
90+
* event.response.issueTokens = true;
91+
* event.response.failAuthentication = false;
92+
* event.response.challengeName = 'CUSTOM_CHALLENGE';
93+
* } else {
94+
* event.response.issueTokens = false;
95+
* event.response.failAuthentication = true;
96+
* }
97+
* context.done(null, event);
98+
* }
99+
* ```
100+
*
101+
* Create Auth Lambda Trigger :
102+
* ```
103+
* function createAuthChallenge(event) {
104+
* if (event.request.challengeName === 'CUSTOM_CHALLENGE') {
105+
* event.response.publicChallengeParameters = {};
106+
* event.response.privateChallengeParameters = {};
107+
* event.response.privateChallengeParameters.answer = '1133';
108+
* }
109+
* }
110+
*
111+
* exports.handler = (event, context, callback) => {
112+
* console.log(JSON.stringify(event));
113+
* createAuthChallenge(event);
114+
*
115+
* console.log(JSON.stringify(event));
116+
* callback(null, event);
117+
* };
118+
* ```
119+
*
120+
* Verify Auth Lambda Trigger :
121+
* ```
122+
* function verifyAuthChallengeResponse(event) {
123+
* if (event.request.privateChallengeParameters.answer === event.request.challengeAnswer) {
124+
* event.response.answerCorrect = true;
125+
* } else {
126+
* event.response.answerCorrect = false;
127+
* }
128+
* }
129+
*
130+
* exports.handler = (event, context, callback) => {
131+
* console.log(JSON.stringify(event));
132+
* verifyAuthChallengeResponse(event);
133+
*
134+
* console.log(JSON.stringify(event));
135+
* callback(null, event);
136+
* };
137+
* ```
138+
* awsconfiguration.json - Should set authenticationFlowType to 'CUSTOM_AUTH' in Auth section as follows :
139+
*
140+
* ```
141+
* "Auth": {
142+
* "Default": {
143+
* "authenticationFlowType": "CUSTOM_AUTH"
144+
* }
145+
* }
146+
* ```
147+
*
148+
*
149+
* @throws Exception
150+
*/
151+
@Test
152+
public void testCustomAuth() throws Exception {
153+
// Check successful sign In
154+
assertTrue("SignIn successful", signIn());
155+
156+
// Check credentials are available
157+
final AWSCredentials credentials = auth.getCredentials();
158+
assertNotNull("Credentials are null", credentials);
159+
assertNotNull("Access key is null", credentials.getAWSAccessKeyId());
160+
assertNotNull("Secret key is null", credentials.getAWSSecretKey());
161+
162+
Tokens tokens = auth.getTokens();
163+
assertNotNull(tokens);
164+
165+
Token accessToken = tokens.getAccessToken();
166+
assertNotNull(accessToken);
167+
assertTrue("Access token should not be expired", accessToken.getExpiration().after(new Date()));
168+
Token idToken = tokens.getIdToken();
169+
assertNotNull(idToken);
170+
assertTrue("Id token should not be expired", idToken.getExpiration().after(new Date()));
171+
Token refreshToken = tokens.getRefreshToken();
172+
assertNotNull(refreshToken);
173+
174+
}
175+
176+
private boolean signIn() {
177+
auth.signIn(USERNAME, PASSWORD, null, new Callback<SignInResult>() {
178+
@Override
179+
public void onResult(final SignInResult signInResult) {
180+
runOnUiThread(new Runnable() {
181+
@Override
182+
public void run() {
183+
Log.d(TAG, "Sign-in callback state: " + signInResult.getSignInState());
184+
switch (signInResult.getSignInState()) {
185+
case DONE:
186+
signUpLatch.countDown();
187+
break;
188+
case CUSTOM_CHALLENGE:
189+
confirmSignIn();
190+
break;
191+
default:
192+
Log.e("APP", "Unexpected sign-in confirmation state: " + signInResult.getSignInState());
193+
break;
194+
}
195+
}
196+
});
197+
}
198+
199+
@Override
200+
public void onError(Exception e) {
201+
Log.e(TAG, "Sign-in error", e);
202+
}
203+
});
204+
205+
try {
206+
signUpLatch.await();
207+
return true;
208+
} catch (InterruptedException e) {
209+
e.printStackTrace();
210+
}
211+
return false;
212+
}
213+
214+
private void confirmSignIn() {
215+
final Map<String, String> res = new HashMap<String, String>();
216+
res.put(CognitoServiceConstants.CHLG_RESP_ANSWER, "1133");
217+
auth.confirmSignIn(res, new Callback<SignInResult>() {
218+
@Override
219+
public void onResult(final SignInResult signInResult) {
220+
runOnUiThread(new Runnable() {
221+
@Override
222+
public void run() {
223+
Log.d(TAG, "Sign-in callback state: " + signInResult.getSignInState());
224+
switch (signInResult.getSignInState()) {
225+
default:
226+
Log.e(TAG, "Unexpected sign-in confirmation call: " + signInResult.getSignInState());
227+
signUpLatch.countDown();
228+
break;
229+
}
230+
}
231+
});
232+
}
233+
234+
@Override
235+
public void onError(Exception e) {
236+
Log.e(TAG, "Confirm sign-in error", e);
237+
signUpLatch.countDown();
238+
}
239+
});
240+
241+
try {
242+
signUpLatch.await();
243+
} catch (InterruptedException e) {
244+
e.printStackTrace();
245+
}
246+
}
247+
}

aws-android-sdk-mobile-client/src/androidTest/java/com/amazonaws/mobile/client/AWSMobileClientNetworkIssueTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
118
package com.amazonaws.mobile.client;
219

320
import android.content.Context;

aws-android-sdk-mobile-client/src/androidTest/java/com/amazonaws/mobile/client/AWSMobileClientOAuth2Test.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
118
package com.amazonaws.mobile.client;
219

320
import android.content.Context;
@@ -20,13 +37,8 @@
2037

2138
import java.util.concurrent.CountDownLatch;
2239

23-
import static android.support.test.espresso.action.ViewActions.click;
24-
import static android.support.test.espresso.matcher.ViewMatchers.withText;
25-
import static org.hamcrest.CoreMatchers.allOf;
26-
import static org.hamcrest.CoreMatchers.is;
2740
import static org.junit.Assert.assertEquals;
2841
import static org.junit.Assert.assertNotNull;
29-
import static org.junit.Assert.assertNull;
3042
import static org.junit.Assert.fail;
3143

3244
public class AWSMobileClientOAuth2Test extends AWSMobileClientTestBase {

aws-android-sdk-mobile-client/src/androidTest/java/com/amazonaws/mobile/client/AWSMobileClientOfflineTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
117
package com.amazonaws.mobile.client;
218

319
import android.content.Context;

aws-android-sdk-mobile-client/src/androidTest/java/com/amazonaws/mobile/client/AWSMobileClientPersistenceTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
118
package com.amazonaws.mobile.client;
219

320
import android.content.Context;

aws-android-sdk-mobile-client/src/androidTest/java/com/amazonaws/mobile/client/AWSMobileClientPersistenceWithRestartabilityTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
118
package com.amazonaws.mobile.client;
219

320
import android.content.Context;

aws-android-sdk-mobile-client/src/androidTest/java/com/amazonaws/mobile/client/AWSMobileClientTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public static void deleteAllUsers(final String userpoolId) {
152152
listUsersResult = getUserpoolLL().listUsers(listUsersRequest);
153153
for (UserType user : listUsersResult.getUsers()) {
154154
if (USERNAME.equals(user.getUsername())
155-
|| "bimin".equals(user.getUsername())) {
155+
|| "bimin".equals(user.getUsername()) || "customAuthTestUser".equals(user.getUsername())) {
156156
// This user is saved to test the identity id permanence
157157
continue;
158158
}

0 commit comments

Comments
 (0)