Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import org.json.JSONObject
/**
* A Cognito implementation of the Auth Plugin.
*/
class AWSCognitoAuthPlugin : AuthPlugin<AWSCognitoAuthServiceBehavior>() {
class AWSCognitoAuthPlugin : AuthPlugin<AWSCognitoAuthService>() {
companion object {
const val AWS_COGNITO_AUTH_LOG_NAMESPACE = "amplify:aws-cognito-auth:%s"
private const val AWS_COGNITO_AUTH_PLUGIN_KEY = "awsCognitoAuthPlugin"
Expand Down Expand Up @@ -111,7 +111,7 @@ class AWSCognitoAuthPlugin : AuthPlugin<AWSCognitoAuthServiceBehavior>() {
val authEnvironment = AuthEnvironment(
context,
configuration,
AWSCognitoAuthServiceBehavior.fromConfiguration(configuration),
AWSCognitoAuthService.fromConfiguration(configuration),
credentialStoreClient,
configuration.userPool?.let { UserContextDataProvider(context, it.poolId!!, it.appClient!!) },
HostedUIClient.create(context, configuration.oauth, logger),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import aws.sdk.kotlin.services.cognitoidentityprovider.CognitoIdentityProviderCl
import aws.smithy.kotlin.runtime.http.endpoints.Endpoint
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration

interface AWSCognitoAuthServiceBehavior {
interface AWSCognitoAuthService {
var cognitoIdentityProviderClient: CognitoIdentityProviderClient?
var cognitoIdentityClient: CognitoIdentityClient?

companion object {
internal fun fromConfiguration(configuration: AuthConfiguration): AWSCognitoAuthServiceBehavior {
internal fun fromConfiguration(configuration: AuthConfiguration): AWSCognitoAuthService {
val cognitoIdentityProviderClient = configuration.userPool?.let { it ->

CognitoIdentityProviderClient {
Expand All @@ -40,7 +40,7 @@ interface AWSCognitoAuthServiceBehavior {
CognitoIdentityClient { this.region = it.region }
}

return object : AWSCognitoAuthServiceBehavior {
return object : AWSCognitoAuthService {
override var cognitoIdentityProviderClient = cognitoIdentityProviderClient
override var cognitoIdentityClient = cognitoIdentityClient
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import java.util.UUID
internal class AuthEnvironment internal constructor(
val context: Context,
val configuration: AuthConfiguration,
val cognitoAuthService: AWSCognitoAuthServiceBehavior,
val cognitoAuthService: AWSCognitoAuthService,
val credentialStoreClient: StoreClientBehavior,
private val userContextDataProvider: UserContextDataProvider? = null,
val hostedUIClient: HostedUIClient?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ import com.amplifyframework.auth.exceptions.ServiceException
open class GlobalSignOutException(cause: Throwable) : ServiceException(
"Failed to sign out globally",
"See attached exception for more details. GlobalSignOut can be retried using the " +
"CognitoIdentityProviderClient is accessible from the escape hatch.",
"CognitoIdentityProviderClient accessible from the escape hatch.",
cause
)
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class AWSCognitoAuthPluginFeatureTest(private val testCase: FeatureTestCase) {
.getJSONObject("awsCognitoAuthPlugin")
val authConfiguration = AuthConfiguration.fromJson(configJSONObject)

val authService = mockk<AWSCognitoAuthServiceBehavior> {
val authService = mockk<AWSCognitoAuthService> {
every { cognitoIdentityProviderClient } returns mockCognitoIPClient
every { cognitoIdentityClient } returns mockCognitoIdClient
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ class AWSCognitoAuthPluginTest {

@Test
fun verifyEscapeHatch() {
val expectedEscapeHatch = mockk<AWSCognitoAuthServiceBehavior>()
val expectedEscapeHatch = mockk<AWSCognitoAuthService>()
every { realPlugin.escapeHatch() } returns expectedEscapeHatch

assertEquals(expectedEscapeHatch, authPlugin.escapeHatch)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class AWSCognitoAuthServiceBehaviorTest {
class AWSCognitoAuthServiceTest {

// Robolectric needed due to internals of AWSCognitoAuthServiceBehavior initialization
@Test
Expand All @@ -45,7 +45,7 @@ class AWSCognitoAuthServiceBehaviorTest {
authFlowType = AuthFlowType.USER_SRP_AUTH
)

val testObject = AWSCognitoAuthServiceBehavior.fromConfiguration(config)
val testObject = AWSCognitoAuthService.fromConfiguration(config)

assertEquals(expectedIdentityPoolConfigRegion, testObject.cognitoIdentityClient!!.config.region)
assertEquals(expectedUserPoolRegion, testObject.cognitoIdentityProviderClient!!.config.region)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class RealAWSCognitoAuthPluginTest {
)

private val mockCognitoIPClient = mockk<CognitoIdentityProviderClient>()
private var authService = mockk<AWSCognitoAuthServiceBehavior> {
private var authService = mockk<AWSCognitoAuthService> {
every { cognitoIdentityProviderClient } returns mockCognitoIPClient
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ open class StateTransitionTestBase {
internal lateinit var configuration: AuthConfiguration

@Mock
internal lateinit var cognitoAuthService: AWSCognitoAuthServiceBehavior
internal lateinit var cognitoAuthService: AWSCognitoAuthService

@Mock
internal lateinit var mockAuthActions: AuthActions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.amplifyframework.auth.cognito.featuretest.FeatureTestCase
import com.amplifyframework.auth.cognito.featuretest.generators.authstategenerators.AuthStateJsonGenerator
import com.amplifyframework.auth.cognito.featuretest.generators.testcasegenerators.ConfirmSignInTestCaseGenerator
import com.amplifyframework.auth.cognito.featuretest.generators.testcasegenerators.DeleteUserTestCaseGenerator
import com.amplifyframework.auth.cognito.featuretest.generators.testcasegenerators.FetchAuthSessionTestCaseGenerator
import com.amplifyframework.auth.cognito.featuretest.generators.testcasegenerators.ResetPasswordTestCaseGenerator
import com.amplifyframework.auth.cognito.featuretest.generators.testcasegenerators.SignInTestCaseGenerator
import com.amplifyframework.auth.cognito.featuretest.generators.testcasegenerators.SignOutTestCaseGenerator
Expand All @@ -40,7 +41,8 @@ object JsonGenerator {
SignInTestCaseGenerator,
SignOutTestCaseGenerator,
ConfirmSignInTestCaseGenerator,
DeleteUserTestCaseGenerator
DeleteUserTestCaseGenerator,
FetchAuthSessionTestCaseGenerator,
)

fun generate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,23 @@ package com.amplifyframework.auth.cognito.featuretest.generators

import aws.sdk.kotlin.services.cognitoidentity.model.CognitoIdentityException
import aws.sdk.kotlin.services.cognitoidentityprovider.model.CognitoIdentityProviderException
import aws.smithy.kotlin.runtime.time.Instant
import com.amplifyframework.auth.AuthException
import com.amplifyframework.auth.cognito.featuretest.FeatureTestCase
import com.amplifyframework.auth.cognito.featuretest.serializers.CognitoIdentityExceptionSerializer
import com.amplifyframework.auth.cognito.featuretest.serializers.CognitoIdentityProviderExceptionSerializer
import com.amplifyframework.auth.cognito.featuretest.serializers.deserializeToAuthState
import com.amplifyframework.auth.cognito.featuretest.serializers.serialize
import com.amplifyframework.auth.result.AuthSessionResult
import com.amplifyframework.statemachine.codegen.states.AuthState
import com.google.gson.Gson
import java.io.BufferedWriter
import java.io.File
import java.io.FileOutputStream
import java.io.FileWriter
import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredMemberProperties
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
Expand Down Expand Up @@ -166,11 +171,13 @@ fun Any?.toJsonElement(): JsonElement {
is Boolean -> JsonPrimitive(this)
is Number -> JsonPrimitive(this)
is String -> JsonPrimitive(this)
is Instant -> JsonPrimitive(this.epochSeconds)
is AuthException -> toJsonElement()
is CognitoIdentityProviderException -> Json.encodeToJsonElement(
CognitoIdentityProviderExceptionSerializer,
this
)
is AuthSessionResult<*> -> toJsonElement()
is CognitoIdentityException -> Json.encodeToJsonElement(CognitoIdentityExceptionSerializer, this)
else -> gsonBasedSerializer(this)
}
Expand All @@ -187,6 +194,10 @@ fun AuthException.toJsonElement(): JsonElement {
return responseMap.toJsonElement()
}

fun AuthSessionResult<*>.toJsonElement(): JsonElement {
return (if (type == AuthSessionResult.Type.SUCCESS) value else error).toJsonElement()
}

/**
* Uses Gson to convert objects which cannot be serialized,
* tries to convert to map of params to vals
Expand All @@ -196,6 +207,19 @@ fun gsonBasedSerializer(value: Any): JsonElement {
return try {
gson.fromJson(gson.toJson(value).toString(), Map::class.java).toJsonElement()
} catch (ex: Exception) {
JsonPrimitive(value.toString())
reflectionBasedSerializer(value)
}
}

/**
* Final fallback to serialize by using reflection, traversing the object members and converting it to Map.
* Note that this method is similar to what Gson does. But Gson fails when there is name collision in parent and child
* classes.
*/
fun reflectionBasedSerializer(value: Any): JsonElement {
return (value::class as KClass<*>).declaredMemberProperties.filter {
it.visibility == KVisibility.PUBLIC
}.associate {
it.name to it.getter.call(value)
}.toMap().toJsonElement()
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ object AuthStateJsonGenerator : SerializableProvider {
const val dummyToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VySWQiLCJ1c2VybmFtZSI6InVzZXJuYW1l" +
"IiwiZXhwIjoxNTE2MjM5MDIyLCJvcmlnaW5fanRpIjoib3JpZ2luX2p0aSJ9.Xqa-vjJe5wwwsqeRAdHf8kTBn_rYSkDn2lB7xj9Z1xU"

const val accessKeyId = "someAccessKey"
const val secretAccessKey = "someSecretKey"
const val identityId = "someIdentityId"
const val expiration: Long = 2342134
const val userId = "userId"

private const val username = "username"

private val signedInData = SignedInData(
userId = "userId",
userId = userId,
username = username,
signedInDate = Date.from(Instant.ofEpochSecond(0)),
signInMethod = SignInMethod.ApiBased(SignInMethod.ApiBased.AuthType.USER_SRP_AUTH),
Expand All @@ -60,12 +66,12 @@ object AuthStateJsonGenerator : SerializableProvider {
AuthorizationState.SessionEstablished(
AmplifyCredential.UserAndIdentityPool(
signedInData,
identityId = "someIdentityId",
identityId = identityId,
AWSCredentials(
accessKeyId = "someAccessKey",
secretAccessKey = "someSecretKey",
accessKeyId = accessKeyId,
secretAccessKey = secretAccessKey,
sessionToken = dummyToken,
expiration = 2342134
expiration = expiration
)
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.amplifyframework.auth.cognito.featuretest.generators.testcasegenerators

import com.amplifyframework.auth.AWSCredentials
import com.amplifyframework.auth.cognito.AWSCognitoAuthSession
import com.amplifyframework.auth.cognito.AWSCognitoUserPoolTokens
import com.amplifyframework.auth.cognito.featuretest.API
import com.amplifyframework.auth.cognito.featuretest.AuthAPI
import com.amplifyframework.auth.cognito.featuretest.CognitoType
import com.amplifyframework.auth.cognito.featuretest.ExpectationShapes
import com.amplifyframework.auth.cognito.featuretest.FeatureTestCase
import com.amplifyframework.auth.cognito.featuretest.MockResponse
import com.amplifyframework.auth.cognito.featuretest.PreConditions
import com.amplifyframework.auth.cognito.featuretest.ResponseType
import com.amplifyframework.auth.cognito.featuretest.generators.SerializableProvider
import com.amplifyframework.auth.cognito.featuretest.generators.authstategenerators.AuthStateJsonGenerator
import com.amplifyframework.auth.cognito.featuretest.generators.toJsonElement
import com.amplifyframework.auth.exceptions.ConfigurationException
import com.amplifyframework.auth.exceptions.SignedOutException
import com.amplifyframework.auth.result.AuthSessionResult
import kotlinx.serialization.json.JsonObject

object FetchAuthSessionTestCaseGenerator : SerializableProvider {

private val mockedInitiateAuthResponse = MockResponse(
CognitoType.CognitoIdentityProvider,
"initiateAuth",
ResponseType.Success,
mapOf(
"authenticationResult" to mapOf(
"idToken" to AuthStateJsonGenerator.dummyToken,
"accessToken" to AuthStateJsonGenerator.dummyToken,
"refreshToken" to AuthStateJsonGenerator.dummyToken,
"expiresIn" to 300
)
).toJsonElement()
)

private val expectedSuccess = AWSCognitoAuthSession(
isSignedIn = true,
identityIdResult = AWSCognitoAuthSession.getIdentityIdResult("someIdentityId"),
awsCredentialsResult = AuthSessionResult.success(
AWSCredentials.createAWSCredentials(
AuthStateJsonGenerator.accessKeyId,
AuthStateJsonGenerator.secretAccessKey,
AuthStateJsonGenerator.dummyToken,
AuthStateJsonGenerator.expiration
)
),
userSubResult = AuthSessionResult.success(AuthStateJsonGenerator.userId),
userPoolTokensResult = AuthSessionResult.success(
AWSCognitoUserPoolTokens(
accessToken = AuthStateJsonGenerator.dummyToken,
idToken = AuthStateJsonGenerator.dummyToken,
refreshToken = AuthStateJsonGenerator.dummyToken
)
)
).toJsonElement()

private val apiReturnValidation = ExpectationShapes.Amplify(
AuthAPI.fetchAuthSession,
ResponseType.Success,
expectedSuccess,
)

private val baseCase = FeatureTestCase(
description = "AuthSession object is successfully returned for UserAndIdentity Pool",
preConditions = PreConditions(
"authconfiguration.json",
"SignedIn_SessionEstablished.json",
mockedResponses = listOf()
),
api = API(
name = AuthAPI.fetchAuthSession,
params = JsonObject(emptyMap()),
JsonObject(emptyMap())
),
validations = listOf(apiReturnValidation)
)

private val refreshSuccessCase: FeatureTestCase = baseCase.copy(
description = "AuthSession object is successfully returned after refresh",
preConditions = baseCase.preConditions.copy(
mockedResponses = listOf(mockedInitiateAuthResponse)
),
api = API(
name = AuthAPI.fetchAuthSession,
params = JsonObject(emptyMap()),
options = mapOf("forceRefresh" to true).toJsonElement(),
),
validations = baseCase.validations
)

private val identityPoolCase: FeatureTestCase = baseCase.copy(
description = "AuthSession object is successfully returned for Identity Pool",
preConditions = baseCase.preConditions.copy(
state = "SignedOut_IdentityPoolConfigured.json"
),
api = baseCase.api,
validations = listOf(
ExpectationShapes.Amplify(
AuthAPI.fetchAuthSession,
ResponseType.Success,
AWSCognitoAuthSession(
isSignedIn = false,
identityIdResult = AWSCognitoAuthSession.getIdentityIdResult("someIdentityId"),
awsCredentialsResult = AuthSessionResult.success(
AWSCredentials.createAWSCredentials(
AuthStateJsonGenerator.accessKeyId,
AuthStateJsonGenerator.secretAccessKey,
AuthStateJsonGenerator.dummyToken,
AuthStateJsonGenerator.expiration
)
),
userSubResult = AuthSessionResult.failure(SignedOutException()),
userPoolTokensResult = AuthSessionResult.failure(SignedOutException())
).toJsonElement()
)
)
)

private val userPoolCase: FeatureTestCase = baseCase.copy(
description = "AuthSession object is successfully returned for User Pool",
preConditions = baseCase.preConditions.copy(
state = "SignedIn_UserPoolSessionEstablished.json"
),
api = baseCase.api,
validations = listOf(
ExpectationShapes.Amplify(
AuthAPI.fetchAuthSession,
ResponseType.Success,
AWSCognitoAuthSession(
isSignedIn = true,
identityIdResult = AuthSessionResult.failure(
ConfigurationException(
"Could not retrieve Identity ID",
"Cognito Identity not configured. Please check amplifyconfiguration.json file."
)
),
awsCredentialsResult = AuthSessionResult.failure(
ConfigurationException(
"Could not fetch AWS Cognito credentials",
"Cognito Identity not configured. Please check amplifyconfiguration.json file."
)
),
userSubResult = AuthSessionResult.success(AuthStateJsonGenerator.userId),
userPoolTokensResult = AuthSessionResult.success(
AWSCognitoUserPoolTokens(
accessToken = AuthStateJsonGenerator.dummyToken,
idToken = AuthStateJsonGenerator.dummyToken,
refreshToken = AuthStateJsonGenerator.dummyToken
)
)
).toJsonElement()
)
)
)

override val serializables: List<Any> = listOf(baseCase, refreshSuccessCase, identityPoolCase, userPoolCase)
}
Loading