Skip to content

Commit bfae00e

Browse files
authored
Merge pull request #164 from jeroenost/master
Fix Cognito User Pools token refresh and failure handling
2 parents af07d18 + 6ff8bc2 commit bfae00e

File tree

2 files changed

+43
-15
lines changed

2 files changed

+43
-15
lines changed

aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/CognitoUser.java

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import android.os.Handler;
2323
import android.util.Log;
2424

25+
import com.amazonaws.AmazonClientException;
2526
import com.amazonaws.AmazonServiceException;
27+
import com.amazonaws.SDKGlobalConfiguration;
2628
import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationContinuation;
2729
import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationDetails;
2830
import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ForgotPasswordContinuation;
@@ -102,6 +104,8 @@
102104
*/
103105
public class CognitoUser {
104106
private final String TAG = "CognitoUser";
107+
/** Default threshold for refreshing session credentials */
108+
public static final int DEFAULT_THRESHOLD_SECONDS = 500;
105109

106110
/**
107111
* Application context.
@@ -619,6 +623,24 @@ public void getSession(final AuthenticationHandler callback) {
619623
}
620624
}
621625

626+
/**
627+
* Returns true if a new session needs to be started. A new session
628+
* is needed when no session has been started yet, or if the last session is
629+
* within the configured refresh threshold.
630+
*
631+
* @return True if a new session needs to be started.
632+
*/
633+
private boolean needsNewSession(CognitoUserSession userSession) {
634+
if (userSession == null) {
635+
return true;
636+
}
637+
long currentTime = System.currentTimeMillis()
638+
- SDKGlobalConfiguration.getGlobalTimeOffset() * 1000;
639+
long timeRemaining = userSession.getIdToken().getExpiration().getTime()
640+
- currentTime;
641+
return timeRemaining < (DEFAULT_THRESHOLD_SECONDS * 1000);
642+
}
643+
622644
/**
623645
* Call this method for valid, cached tokens for this user.
624646
*
@@ -629,33 +651,36 @@ private CognitoUserSession getCachedSession() {
629651
throw new CognitoNotAuthorizedException("User-ID is null");
630652
}
631653

632-
if (cipSession != null) {
633-
if (cipSession.isValid()) {
634-
return cipSession;
635-
}
654+
if (!needsNewSession(cipSession)) {
655+
return cipSession;
636656
}
637657

638658
// Read cached tokens
639659
CognitoUserSession cachedTokens = readCachedTokens();
640660

641-
// Return cached tokens if they are still valid
642-
if (cachedTokens.isValid()) {
661+
// Return cached tokens if they are still valid with some margin
662+
if (!needsNewSession(cachedTokens)) {
643663
cipSession = cachedTokens;
644-
return cipSession;
664+
return cipSession;
645665
}
646666

647-
// Clear any cached tokens, since none of them are valid.
648-
clearCachedTokens();
649-
650667
if (cachedTokens.getRefreshToken() != null) {
651668
// Use Refresh token to get new tokens
652669
try {
653670
cipSession = refreshSessionInternal(cachedTokens.getRefreshToken());
654671
cacheTokens(cipSession);
655672
return cipSession;
656-
} catch (Exception e) {
673+
} catch (CognitoNotAuthorizedException e) {
674+
// Clear any cached tokens, since none of them are valid.
675+
clearCachedTokens();
657676
// Could not get new tokens from refresh. Should authenticate user.
658-
throw new CognitoNotAuthorizedException("user is not authenticated");
677+
throw new CognitoNotAuthorizedException("user is not authenticated",e);
678+
} catch (AmazonClientException e) {
679+
// General IO errors - not clearing cached tokens
680+
throw new AmazonClientException("failed to get new tokens from refresh",e);
681+
} catch (Exception e) {
682+
// Errors like NetworkOnMainThreadException etc - not clearing cached tokens.
683+
throw new AmazonClientException("failed to get new tokens from refresh",e);
659684
}
660685
}
661686
throw new CognitoNotAuthorizedException("user is not authenticated");
@@ -2018,7 +2043,7 @@ private CognitoUserSession refreshSessionInternal(CognitoRefreshToken refreshTok
20182043
cognitoIdentityProviderClient.refreshTokens(refreshTokensRequest);
20192044
AuthenticationResultType authenticationResult = refreshTokensResult.getAuthenticationResult();
20202045

2021-
if (authenticationResult != null) {
2046+
if (authenticationResult == null) {
20222047
throw new CognitoNotAuthorizedException("user is not authenticated");
20232048
}
20242049

aws-android-sdk-cognitoidentityprovider/src/main/java/com/amazonaws/mobileconnectors/cognitoidentityprovider/CognitoUserSession.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.amazonaws.mobileconnectors.cognitoidentityprovider;
1919

20+
import com.amazonaws.SDKGlobalConfiguration;
2021
import com.amazonaws.mobileconnectors.cognitoidentityprovider.tokens.CognitoAccessToken;
2122
import com.amazonaws.mobileconnectors.cognitoidentityprovider.tokens.CognitoIdToken;
2223
import com.amazonaws.mobileconnectors.cognitoidentityprovider.tokens.CognitoRefreshToken;
@@ -27,6 +28,8 @@
2728
* This wraps all Cognito tokens for a user.
2829
*/
2930
public class CognitoUserSession {
31+
/** Default threshold for refreshing session credentials */
32+
public static final int DEFAULT_THRESHOLD_SECONDS = 500;
3033
/**
3134
* Cognito identity token.
3235
*/
@@ -88,8 +91,8 @@ public CognitoRefreshToken getRefreshToken() {
8891
* @return boolean to indicate if the access and id tokens have not expired.
8992
*/
9093
public boolean isValid() {
91-
Date currentTimeStamp = new Date();
92-
94+
Date currentTimeStamp = new Date(System.currentTimeMillis()
95+
- SDKGlobalConfiguration.getGlobalTimeOffset() * 1000);
9396
try {
9497
return (currentTimeStamp.before(idToken.getExpiration())
9598
& currentTimeStamp.before(accessToken.getExpiration()));

0 commit comments

Comments
 (0)