From 64261f0d68a0c2ad023339409b1fc31462a3f134 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:13:23 -0400 Subject: [PATCH 1/3] feat: remove dependency on amplifyconfiguration.json --- .../Configuration/AmplifyConfiguration.swift | 136 ++---------------- .../Models/SignUpAttribute.swift | 7 +- .../Authenticator/Models/SignUpField.swift | 7 +- .../Authenticator/States/SignInState.swift | 2 +- .../Authenticator/States/SignUpState.swift | 3 +- .../Utilities/FieldValidator.swift | 5 +- .../Views/ConfirmResetPasswordView.swift | 3 +- .../ConfirmSignInWithNewPasswordView.swift | 3 +- .../Views/ResetPasswordView.swift | 3 +- Sources/Authenticator/Views/SignInView.swift | 3 +- Sources/Authenticator/Views/SignUpView.swift | 3 +- .../Mocks/MockAuthenticatorState.swift | 1 + .../States/SignUpStateTests.swift | 1 + 13 files changed, 37 insertions(+), 140 deletions(-) diff --git a/Sources/Authenticator/Configuration/AmplifyConfiguration.swift b/Sources/Authenticator/Configuration/AmplifyConfiguration.swift index d13485c..9db3f6e 100644 --- a/Sources/Authenticator/Configuration/AmplifyConfiguration.swift +++ b/Sources/Authenticator/Configuration/AmplifyConfiguration.swift @@ -35,139 +35,25 @@ struct AmplifyConfiguration { throw AmplifyConfigurationError.missingPlugin } - guard let configuration = plugin.jsonConfiguration, - let cognitoConfiguration = configuration.value(at: "Auth.Default") else { - Self.log.error("Unable to read Auth.Default from the configuration") + switch plugin.authConfiguration { + case .userPools(let configuration), .userPoolsAndIdentityPools(let configuration, _): + self.cognito = CognitoConfiguration( + usernameAttributes: configuration.usernameAttributes, + signupAttributes: configuration.signUpAttributes, + passwordProtectionSettings: configuration.passwordProtectionSettings ?? .init(minLength: 0, characterPolicy: []), + verificationMechanisms: configuration.verificationMechanisms + ) + case .identityPools, .none: + Self.log.error("Unable to retrieve configuration") throw AmplifyConfigurationError.missingConfiguration } - - guard case .object(let passwordSettings) = cognitoConfiguration["passwordProtectionSettings"] else { - Self.log.error("passwordProtectionSettings is missing") - throw AmplifyConfigurationError.incorrectConfiguration - } - - var minLength: Int - if case .number(let value) = passwordSettings["passwordPolicyMinLength"] { - minLength = Int(value) - } else if case .string(let value) = passwordSettings["passwordPolicyMinLength"], - let intValue = Int(value) { - minLength = intValue - } else { - Self.log.error("passwordPolicyMinLength is missing") - throw AmplifyConfigurationError.incorrectConfiguration - } - - var characterPolicy: [CognitoConfiguration.PasswordCharacterPolicy] = [] - if case .array(let characters) = passwordSettings["passwordPolicyCharacters"] { - characterPolicy = characters.compactMap { value in - guard case .string(let string) = value else { - return nil - } - - return .init(rawValue: string) - } - } - - let passwordProtectionSettings = CognitoConfiguration.PasswordProtectionSettings( - minLength: minLength, - characterPolicy: characterPolicy - ) - - var usernameAttributes: [CognitoConfiguration.UsernameAttribute] = [] - if case .array(let attributes) = cognitoConfiguration["usernameAttributes"] { - usernameAttributes = attributes.compactMap { value in - guard case .string(let string) = value else { - return nil - } - - return .init(rawValue: string) - } - } - - var signUpAttributes: [CognitoConfiguration.SignUpAttribute] = [] - if case .array(let attributes) = cognitoConfiguration["signupAttributes"] { - signUpAttributes = attributes.compactMap { value in - guard case .string(let string) = value else { - return nil - } - - return .init(rawValue: string) - } - } - - var verificationMechanisms: [CognitoConfiguration.VerificationMechanism] = [] - if case .array(let attributes) = cognitoConfiguration["verificationMechanisms"] { - verificationMechanisms = attributes.compactMap { value in - guard case .string(let string) = value else { - return nil - } - - return .init(rawValue: string) - } - } - - self.cognito = CognitoConfiguration( - usernameAttributes: usernameAttributes, - signupAttributes: signUpAttributes, - passwordProtectionSettings: passwordProtectionSettings, - verificationMechanisms: verificationMechanisms - ) } } struct CognitoConfiguration { - enum UsernameAttribute: String, Decodable { - case username = "USERNAME" - case email = "EMAIL" - case phoneNumber = "PHONE_NUMBER" - - init?(from authUserAttributeKey: AuthUserAttributeKey) { - switch authUserAttributeKey { - case .email: - self = .email - case .phoneNumber: - self = .phoneNumber - default: - return nil - } - } - } - - enum SignUpAttribute: String, Decodable { - case address = "ADDRESS" - case birthDate = "BIRTHDATE" - case email = "EMAIL" - case familyName = "FAMILY_NAME" - case gender = "GENDER" - case givenName = "GIVEN_NAME" - case middleName = "MIDDLE_NAME" - case name = "NAME" - case nickname = "NICKNAME" - case phoneNumber = "PHONE_NUMBER" - case preferredUsername = "PREFERRED_USERNAME" - case profile = "PROFILE" - case website = "WEBSITE" - } - - enum VerificationMechanism: String, Decodable { - case email = "EMAIL" - case phoneNumber = "PHONE_NUMBER" - } - - enum PasswordCharacterPolicy: String, Decodable { - case lowercase = "REQUIRES_LOWERCASE" - case uppercase = "REQUIRES_UPPERCASE" - case numbers = "REQUIRES_NUMBERS" - case symbols = "REQUIRES_SYMBOLS" - } - - struct PasswordProtectionSettings: Decodable { - var minLength: Int - var characterPolicy: [PasswordCharacterPolicy] - } var usernameAttributes: [UsernameAttribute] - var signupAttributes: [SignUpAttribute] + var signupAttributes: [SignUpAttributeType] var passwordProtectionSettings: PasswordProtectionSettings var verificationMechanisms: [VerificationMechanism] diff --git a/Sources/Authenticator/Models/SignUpAttribute.swift b/Sources/Authenticator/Models/SignUpAttribute.swift index eec46f6..e946d98 100644 --- a/Sources/Authenticator/Models/SignUpAttribute.swift +++ b/Sources/Authenticator/Models/SignUpAttribute.swift @@ -14,6 +14,7 @@ import SwiftUI #if canImport(UIKit) import UIKit #endif +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin /// Represents to which Sign Up attribute a field is associated with. public enum SignUpAttribute: Equatable, Hashable { @@ -145,7 +146,7 @@ public enum SignUpAttribute: Equatable, Hashable { #endif } -extension CognitoConfiguration.VerificationMechanism { +extension VerificationMechanism { var asSignUpAttribute: SignUpAttribute { switch self { case .email: @@ -156,7 +157,7 @@ extension CognitoConfiguration.VerificationMechanism { } } -extension CognitoConfiguration.SignUpAttribute { +extension SignUpAttributeType { var asSignUpAttribute: SignUpAttribute { switch self { case .email: @@ -189,7 +190,7 @@ extension CognitoConfiguration.SignUpAttribute { } } -extension CognitoConfiguration.UsernameAttribute { +extension UsernameAttribute { var asSignUpAttribute: SignUpAttribute { switch self { case .username: diff --git a/Sources/Authenticator/Models/SignUpField.swift b/Sources/Authenticator/Models/SignUpField.swift index 85ce31e..1fabd2b 100644 --- a/Sources/Authenticator/Models/SignUpField.swift +++ b/Sources/Authenticator/Models/SignUpField.swift @@ -7,6 +7,7 @@ import Amplify import SwiftUI +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin /// Represents a field that is displayed in the Sign Up view public protocol SignUpField { @@ -417,7 +418,7 @@ extension SignUpField where Self == BaseSignUpField { } static func signUpField( - from attribute: CognitoConfiguration.SignUpAttribute, + from attribute: SignUpAttributeType, isRequired: Bool ) -> SignUpField { switch attribute { @@ -451,7 +452,7 @@ extension SignUpField where Self == BaseSignUpField { } static func signUpField( - from attribute: CognitoConfiguration.VerificationMechanism + from attribute: VerificationMechanism ) -> SignUpField { switch attribute { case .email: @@ -462,7 +463,7 @@ extension SignUpField where Self == BaseSignUpField { } static func signUpField( - from attribute: CognitoConfiguration.UsernameAttribute + from attribute: UsernameAttribute ) -> SignUpField { switch attribute { case .username: diff --git a/Sources/Authenticator/States/SignInState.swift b/Sources/Authenticator/States/SignInState.swift index f11f579..8d274af 100644 --- a/Sources/Authenticator/States/SignInState.swift +++ b/Sources/Authenticator/States/SignInState.swift @@ -6,7 +6,7 @@ // import Amplify -import AWSCognitoAuthPlugin +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin import SwiftUI /// The state observed by the Sign In content view, representing the ``Authenticator`` is in the ``AuthenticatorStep/signIn`` step. diff --git a/Sources/Authenticator/States/SignUpState.swift b/Sources/Authenticator/States/SignUpState.swift index 696ce28..5449364 100644 --- a/Sources/Authenticator/States/SignUpState.swift +++ b/Sources/Authenticator/States/SignUpState.swift @@ -8,6 +8,7 @@ import Amplify import Foundation import SwiftUI +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin /// The state observed by the Sign Up content view, representing the ``Authenticator`` is in the ``AuthenticatorStep/signUp`` step. public class SignUpState: AuthenticatorBaseState { @@ -37,7 +38,7 @@ public class SignUpState: AuthenticatorBaseState { AuthUserAttribute(key, value: field.value) ) // Check if the current AuthUserAttribute is defined to be the usernameAttribute in Cognito's config - if configuration.usernameAttribute == CognitoConfiguration.UsernameAttribute(from: key) { + if configuration.usernameAttribute == UsernameAttribute(from: key) { username = field.value } } diff --git a/Sources/Authenticator/Utilities/FieldValidator.swift b/Sources/Authenticator/Utilities/FieldValidator.swift index 12f0d73..1632120 100644 --- a/Sources/Authenticator/Utilities/FieldValidator.swift +++ b/Sources/Authenticator/Utilities/FieldValidator.swift @@ -6,6 +6,7 @@ // import Foundation +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin /// Represents an error associated with a Field, typically displayed beneath it. public typealias FieldError = CustomStringConvertible @@ -41,7 +42,7 @@ public struct FieldValidators { /// ~~~ public static let requiresSymbols: Self = .init(name: "requiresSymbols") - init(from policy: CognitoConfiguration.PasswordCharacterPolicy) { + init(from policy: PasswordCharacterPolicy) { switch policy { case .lowercase: self = .requiresLowercase @@ -157,7 +158,7 @@ public struct FieldValidators { } } -extension Array where Element == CognitoConfiguration.PasswordCharacterPolicy { +extension Array where Element == PasswordCharacterPolicy { func asPasswordCharactersPolicy() -> [FieldValidators.PasswordCharactersPolicy] { return compactMap { FieldValidators.PasswordCharactersPolicy(from: $0) } diff --git a/Sources/Authenticator/Views/ConfirmResetPasswordView.swift b/Sources/Authenticator/Views/ConfirmResetPasswordView.swift index 8d7a909..a5abc05 100644 --- a/Sources/Authenticator/Views/ConfirmResetPasswordView.swift +++ b/Sources/Authenticator/Views/ConfirmResetPasswordView.swift @@ -7,6 +7,7 @@ import Amplify import SwiftUI +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin /// Represents the content being displayed when the ``Authenticator`` is in the ``AuthenticatorStep/confirmResetPassword`` step. public struct ConfirmResetPasswordView some View { switch usernameAttribute { case .username: diff --git a/Sources/Authenticator/Views/SignInView.swift b/Sources/Authenticator/Views/SignInView.swift index 304fbed..bd75860 100644 --- a/Sources/Authenticator/Views/SignInView.swift +++ b/Sources/Authenticator/Views/SignInView.swift @@ -7,6 +7,7 @@ import Amplify import SwiftUI +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin /// Represents the content being displayed when the ``Authenticator`` is in the ``AuthenticatorStep/signIn`` step. /// @@ -153,7 +154,7 @@ public struct SignInView some View { switch usernameAttribute { case .username: diff --git a/Sources/Authenticator/Views/SignUpView.swift b/Sources/Authenticator/Views/SignUpView.swift index 201402f..ca2b54b 100644 --- a/Sources/Authenticator/Views/SignUpView.swift +++ b/Sources/Authenticator/Views/SignUpView.swift @@ -7,6 +7,7 @@ import Amplify import SwiftUI +@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin /// Represents the content being displayed when the ``Authenticator`` is in the ``AuthenticatorStep/signUp`` step. public struct SignUpView Date: Wed, 13 Mar 2024 17:15:13 -0400 Subject: [PATCH 2/3] update to use minLength UInt --- Sources/Authenticator/Views/ConfirmResetPasswordView.swift | 2 +- .../Authenticator/Views/ConfirmSignInWithNewPasswordView.swift | 2 +- Sources/Authenticator/Views/SignUpView.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Authenticator/Views/ConfirmResetPasswordView.swift b/Sources/Authenticator/Views/ConfirmResetPasswordView.swift index a5abc05..931a3a1 100644 --- a/Sources/Authenticator/Views/ConfirmResetPasswordView.swift +++ b/Sources/Authenticator/Views/ConfirmResetPasswordView.swift @@ -45,7 +45,7 @@ public struct ConfirmResetPasswordView Date: Wed, 13 Mar 2024 17:16:13 -0400 Subject: [PATCH 3/3] update logging --- Sources/Authenticator/Configuration/AmplifyConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Authenticator/Configuration/AmplifyConfiguration.swift b/Sources/Authenticator/Configuration/AmplifyConfiguration.swift index 9db3f6e..45a006b 100644 --- a/Sources/Authenticator/Configuration/AmplifyConfiguration.swift +++ b/Sources/Authenticator/Configuration/AmplifyConfiguration.swift @@ -44,7 +44,7 @@ struct AmplifyConfiguration { verificationMechanisms: configuration.verificationMechanisms ) case .identityPools, .none: - Self.log.error("Unable to retrieve configuration") + Self.log.error("Unable to retrieve configuration from AWSCognitoAuthPlugin") throw AmplifyConfigurationError.missingConfiguration } }