Skip to content

feat(auth): Expose AuthConfiguration directly #2740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 1, 2024
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
1 change: 1 addition & 0 deletions aws-auth-cognito/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ dependencies {
testImplementation(libs.test.robolectric)
testImplementation(libs.test.androidx.core)
testImplementation(libs.test.kotlin.reflection)
testImplementation(libs.test.kotest.assertions)

androidTestImplementation(libs.gson)
//noinspection GradleDependency
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.amplifyframework.auth.cognito.data.AWSCognitoLegacyCredentialStore
import com.amplifyframework.auth.cognito.testutils.AuthConfigurationProvider
import com.amplifyframework.auth.cognito.testutils.CredentialStoreUtil
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.amplifyframework.auth.cognito.data.AWSCognitoAuthCredentialStore
import com.amplifyframework.auth.cognito.testutils.AuthConfigurationProvider
import com.amplifyframework.auth.cognito.testutils.CredentialStoreUtil
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import com.google.gson.Gson
import org.json.JSONObject
import org.junit.Before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

package com.amplifyframework.auth.cognito.testutils

import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import com.amplifyframework.auth.cognito.AuthConfiguration
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import com.amplifyframework.core.Action
import com.amplifyframework.core.Amplify
import com.amplifyframework.core.Consumer
import com.amplifyframework.core.category.CategoryType
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -100,10 +99,14 @@ class AWSCognitoAuthPlugin : AuthPlugin<AWSCognitoAuthService>() {
private lateinit var pluginConfigurationJSON: JSONObject

@InternalAmplifyApi
@Deprecated("Use getAuthConfiguration instead", replaceWith = ReplaceWith("getAuthConfiguration()"))
fun getPluginConfiguration(): JSONObject {
return pluginConfigurationJSON
}

@InternalAmplifyApi
fun getAuthConfiguration() = realPlugin.configuration

@InternalAmplifyApi
fun addToUserAgent(type: AWSCognitoAuthMetadataType, value: String) {
realPlugin.addToUserAgent(type, value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import aws.sdk.kotlin.services.cognitoidentityprovider.endpoints.CognitoIdentity
import aws.smithy.kotlin.runtime.client.RequestInterceptorContext
import aws.smithy.kotlin.runtime.client.endpoints.Endpoint
import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration

interface AWSCognitoAuthService {
val cognitoIdentityProviderClient: CognitoIdentityProviderClient?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.amplifyframework.auth.cognito

import androidx.annotation.IntRange
import com.amplifyframework.annotations.InternalAmplifyApi
import com.amplifyframework.auth.AuthUserAttributeKey
import com.amplifyframework.auth.cognito.options.AuthFlowType
import com.amplifyframework.statemachine.codegen.data.IdentityPoolConfiguration
import com.amplifyframework.statemachine.codegen.data.OauthConfiguration
import com.amplifyframework.statemachine.codegen.data.UserPoolConfiguration
import org.json.JSONArray
import org.json.JSONObject

@InternalAmplifyApi
enum class UsernameAttribute {
Username,
Email,
PhoneNumber
}

@InternalAmplifyApi
enum class VerificationMechanism {
Email,
PhoneNumber
}

@InternalAmplifyApi
data class PasswordProtectionSettings(
@IntRange(from = 6, to = 99) val length: Int,
val requiresNumber: Boolean,
val requiresSpecial: Boolean,
val requiresUpper: Boolean,
val requiresLower: Boolean
)

/**
* Configuration options for [AWSCognitoAuthPlugin].
*/
@InternalAmplifyApi
data class AuthConfiguration internal constructor(
val userPool: UserPoolConfiguration?,
val identityPool: IdentityPoolConfiguration?,
val oauth: OauthConfiguration?,
val authFlowType: AuthFlowType,
val signUpAttributes: List<AuthUserAttributeKey>,
val usernameAttributes: List<UsernameAttribute>,
val verificationMechanisms: List<VerificationMechanism>,
val passwordProtectionSettings: PasswordProtectionSettings?
) {

internal companion object {
/**
* Returns an AuthConfiguration instance from JSON
* @return populated AuthConfiguration instance.
*/
fun fromJson(
pluginJson: JSONObject,
configName: String = "Default"
): AuthConfiguration {
val authConfig = pluginJson.optJSONObject("Auth")?.optJSONObject(configName)

val signUpAttributes = authConfig?.optJSONArray("signupAttributes")?.map {
AuthUserAttributeKey.custom(getString(it).lowercase())
} ?: emptyList()

val usernameAttributes = authConfig?.optJSONArray("usernameAttributes")?.map {
when (getString(it)) {
"EMAIL" -> UsernameAttribute.Email
"PHONE_NUMBER" -> UsernameAttribute.PhoneNumber
else -> UsernameAttribute.Username
}
} ?: emptyList()

val verificationMechanisms = authConfig?.optJSONArray("verificationMechanisms")?.map {
when (getString(it)) {
"EMAIL" -> VerificationMechanism.Email
else -> VerificationMechanism.PhoneNumber
}
} ?: emptyList()

return AuthConfiguration(
userPool = pluginJson.optJSONObject("CognitoUserPool")?.getJSONObject(configName)?.let {
UserPoolConfiguration.fromJson(it).build()
},
identityPool = pluginJson.optJSONObject("CredentialsProvider")
?.getJSONObject("CognitoIdentity")
?.getJSONObject(configName)?.let {
IdentityPoolConfiguration.fromJson(it).build()
},
oauth = authConfig?.optJSONObject("OAuth")?.let { OauthConfiguration.fromJson(it) },
authFlowType = getAuthenticationFlowType(authConfig?.optString("authenticationFlowType")),
signUpAttributes = signUpAttributes,
usernameAttributes = usernameAttributes,
verificationMechanisms = verificationMechanisms,
passwordProtectionSettings = getPasswordProtectionSettings(authConfig)
)
}
private fun getAuthenticationFlowType(authType: String?): AuthFlowType {
return if (!authType.isNullOrEmpty() && AuthFlowType.values().any { it.name == authType }) {
AuthFlowType.valueOf(authType)
} else {
AuthFlowType.USER_SRP_AUTH
}
}

private fun getPasswordProtectionSettings(authConfig: JSONObject?): PasswordProtectionSettings? {
val passwordSettings = authConfig?.optJSONObject("passwordProtectionSettings") ?: return null
val passwordLength = passwordSettings.optInt("passwordPolicyMinLength")
val passwordRequirements = passwordSettings.optJSONArray("passwordPolicyCharacters")?.map {
getString(it)
} ?: emptyList()
return PasswordProtectionSettings(
length = passwordLength,
requiresNumber = passwordRequirements.contains("REQUIRES_NUMBERS"),
requiresSpecial = passwordRequirements.contains("REQUIRES_SYMBOLS"),
requiresLower = passwordRequirements.contains("REQUIRES_LOWER"),
requiresUpper = passwordRequirements.contains("REQUIRES_UPPER")
)
}

private inline fun <T> JSONArray.map(func: JSONArray.(Int) -> T) = List(length()) {
func(it)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import com.amplifyframework.logging.Logger
import com.amplifyframework.statemachine.Environment
import com.amplifyframework.statemachine.StateMachineEvent
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import com.amplifyframework.statemachine.codegen.data.CredentialType
import com.amplifyframework.statemachine.codegen.data.DeviceMetadata
import com.amplifyframework.statemachine.codegen.events.AuthEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.amplifyframework.auth.cognito.data.AWSCognitoLegacyCredentialStore
import com.amplifyframework.logging.Logger
import com.amplifyframework.statemachine.StateChangeListenerToken
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import com.amplifyframework.statemachine.codegen.data.CredentialType
import com.amplifyframework.statemachine.codegen.events.CredentialStoreEvent
import com.amplifyframework.statemachine.codegen.states.CredentialStoreState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ import com.amplifyframework.logging.Logger
import com.amplifyframework.statemachine.StateChangeListenerToken
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
import com.amplifyframework.statemachine.codegen.data.AuthChallenge
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import com.amplifyframework.statemachine.codegen.data.FederatedToken
import com.amplifyframework.statemachine.codegen.data.HostedUIErrorData
import com.amplifyframework.statemachine.codegen.data.SignInData
Expand Down Expand Up @@ -173,7 +172,7 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.launch

internal class RealAWSCognitoAuthPlugin(
private val configuration: AuthConfiguration,
val configuration: AuthConfiguration,
private val authEnvironment: AuthEnvironment,
private val authStateMachine: AuthStateMachine,
private val logger: Logger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,19 @@
package com.amplifyframework.auth.cognito.data

import android.content.Context
import com.amplifyframework.auth.cognito.AuthConfiguration
import com.amplifyframework.core.store.KeyValueRepository
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import com.amplifyframework.statemachine.codegen.data.AuthCredentialStore
import com.amplifyframework.statemachine.codegen.data.DeviceMetadata
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

internal class AWSCognitoAuthCredentialStore(
val context: Context,
private val authConfiguration: AuthConfiguration,
isPersistenceEnabled: Boolean = true,
keyValueRepoFactory: KeyValueRepositoryFactory = KeyValueRepositoryFactory(),
keyValueRepoFactory: KeyValueRepositoryFactory = KeyValueRepositoryFactory()
) : AuthCredentialStore {

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ package com.amplifyframework.auth.cognito.data

import android.content.Context
import com.amplifyframework.auth.AuthProvider
import com.amplifyframework.auth.cognito.AuthConfiguration
import com.amplifyframework.auth.cognito.helpers.SessionHelper
import com.amplifyframework.auth.cognito.helpers.identityProviderName
import com.amplifyframework.core.store.KeyValueRepository
import com.amplifyframework.statemachine.codegen.data.AWSCredentials
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
import com.amplifyframework.statemachine.codegen.data.AuthCredentialStore
import com.amplifyframework.statemachine.codegen.data.CognitoUserPoolTokens
import com.amplifyframework.statemachine.codegen.data.DeviceMetadata
Expand Down Expand Up @@ -184,7 +184,9 @@ internal class AWSCognitoLegacyCredentialStore(

return if (accessKey == null && secretKey == null && sessionToken == null) {
null
} else AWSCredentials(accessKey, secretKey, sessionToken, expiration)
} else {
AWSCredentials(accessKey, secretKey, sessionToken, expiration)
}
}

private fun retrieveIdentityId() = idAndCredentialsKeyValue.get(namespace(ID_KEY))
Expand Down Expand Up @@ -224,8 +226,11 @@ internal class AWSCognitoLegacyCredentialStore(
val deviceGroupKey = deviceKeyValue.get(DEVICE_GROUP_KEY)
val deviceSecretKey = deviceKeyValue.get(DEVICE_SECRET_KEY)

return if (deviceKey.isNullOrEmpty() && deviceGroupKey.isNullOrEmpty()) DeviceMetadata.Empty
else DeviceMetadata.Metadata(deviceKey ?: "", deviceGroupKey ?: "", deviceSecretKey)
return if (deviceKey.isNullOrEmpty() && deviceGroupKey.isNullOrEmpty()) {
DeviceMetadata.Empty
} else {
DeviceMetadata.Metadata(deviceKey ?: "", deviceGroupKey ?: "", deviceSecretKey)
}
}

private fun retrieveCognitoUserPoolTokens(keys: Map<String, String>): CognitoUserPoolTokens? {
Expand Down

This file was deleted.

Loading