Skip to content

Commit

Permalink
fix(auth): Fix timestamp issue with v1 to v2 migration
Browse files Browse the repository at this point in the history
V1 was storing session expiration timestamp as epoch milliseconds and V2 was reading/storing it as epoch seconds. The migration code did not account for this and users that migrated would store a session expiration that would be many many years in the future.

This change adds some logic to the migration code to detect this and covert from milli to sec for new users that still need to migrate.

This change also adds a check in the isValidSession function for users who have already migrated and needs a way to fix themselves.
  • Loading branch information
vincetran committed Apr 30, 2024
1 parent 9e94af8 commit b39cdf9
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import com.amplifyframework.statemachine.codegen.data.DeviceMetadata
import com.amplifyframework.statemachine.codegen.data.FederatedToken
import com.amplifyframework.statemachine.codegen.data.SignInMethod
import com.amplifyframework.statemachine.codegen.data.SignedInData
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.Date
import java.util.Locale

Expand Down Expand Up @@ -180,7 +182,17 @@ internal class AWSCognitoLegacyCredentialStore(
val accessKey = idAndCredentialsKeyValue.get(namespace(AK_KEY))
val secretKey = idAndCredentialsKeyValue.get(namespace(SK_KEY))
val sessionToken = idAndCredentialsKeyValue.get(namespace(ST_KEY))
val expiration = idAndCredentialsKeyValue.get(namespace(EXP_KEY))?.toLongOrNull()
var expiration = idAndCredentialsKeyValue.get(namespace(EXP_KEY))?.toLongOrNull()

// V2 AWSCredential expiration is in epoch seconds whereas legacy expiration may be in epoch milliseconds
// Session expiration should be within 24 hours so if we see a date in the far future, we can assume the
// timestamp is encoded in milliseconds.
val expirationInstant = expiration?.let { Instant.ofEpochSecond(it) }
if (expiration != null &&
expirationInstant?.isAfter(Instant.now().plus(365, ChronoUnit.DAYS)) == true
) {
expiration /= 1000
}

return if (accessKey == null && secretKey == null && sessionToken == null) {
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.amplifyframework.auth.cognito.helpers
import com.amplifyframework.statemachine.codegen.data.AWSCredentials
import com.amplifyframework.statemachine.codegen.data.CognitoUserPoolTokens
import java.time.Instant
import java.time.temporal.ChronoUnit

internal object SessionHelper {
/**
Expand Down Expand Up @@ -68,6 +69,14 @@ internal object SessionHelper {
*/
fun isValidSession(awsCredentials: AWSCredentials): Boolean {
val currentTimeStamp = Instant.now()
return currentTimeStamp < awsCredentials.expiration?.let { Instant.ofEpochSecond(it) }
val credentialsExpirationInSecond = awsCredentials.expiration?.let { Instant.ofEpochSecond(it) }

// Check if current timestamp is BEFORE expiration && next year is AFTER expiration
// The latter check is to fix v1 > v2 migration issues as found in:
// https://github.com/aws-amplify/amplify-android/issues/2789
return (
currentTimeStamp < credentialsExpirationInSecond &&
currentTimeStamp.plus(365, ChronoUnit.DAYS) > credentialsExpirationInSecond
)
}
}

0 comments on commit b39cdf9

Please sign in to comment.