Skip to content
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

feat: remove dependency on amplifyconfiguration.json #58

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
136 changes: 11 additions & 125 deletions Sources/Authenticator/Configuration/AmplifyConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: []),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗ This is a change in existing behaviour. Currently, the Authenticator requires these password settings to be explicitly set, showing an error if that is not the case.
But now we will just default to not having password validations.

I don't feel strongly against this, but I think Android also requires these values so we should coordinate to make the change there as well. And also call it out in the release notes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great callout, will follow up on this

verificationMechanisms: configuration.verificationMechanisms
)
case .identityPools, .none:
Self.log.error("Unable to retrieve configuration")
lawmicha marked this conversation as resolved.
Show resolved Hide resolved
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]

Expand Down
7 changes: 4 additions & 3 deletions Sources/Authenticator/Models/SignUpAttribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -145,7 +146,7 @@ public enum SignUpAttribute: Equatable, Hashable {
#endif
}

extension CognitoConfiguration.VerificationMechanism {
extension VerificationMechanism {
var asSignUpAttribute: SignUpAttribute {
switch self {
case .email:
Expand All @@ -156,7 +157,7 @@ extension CognitoConfiguration.VerificationMechanism {
}
}

extension CognitoConfiguration.SignUpAttribute {
extension SignUpAttributeType {
var asSignUpAttribute: SignUpAttribute {
switch self {
case .email:
Expand Down Expand Up @@ -189,7 +190,7 @@ extension CognitoConfiguration.SignUpAttribute {
}
}

extension CognitoConfiguration.UsernameAttribute {
extension UsernameAttribute {
var asSignUpAttribute: SignUpAttribute {
switch self {
case .username:
Expand Down
7 changes: 4 additions & 3 deletions Sources/Authenticator/Models/SignUpField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -417,7 +418,7 @@ extension SignUpField where Self == BaseSignUpField {
}

static func signUpField(
from attribute: CognitoConfiguration.SignUpAttribute,
from attribute: SignUpAttributeType,
isRequired: Bool
) -> SignUpField {
switch attribute {
Expand Down Expand Up @@ -451,7 +452,7 @@ extension SignUpField where Self == BaseSignUpField {
}

static func signUpField(
from attribute: CognitoConfiguration.VerificationMechanism
from attribute: VerificationMechanism
) -> SignUpField {
switch attribute {
case .email:
Expand All @@ -462,7 +463,7 @@ extension SignUpField where Self == BaseSignUpField {
}

static func signUpField(
from attribute: CognitoConfiguration.UsernameAttribute
from attribute: UsernameAttribute
) -> SignUpField {
switch attribute {
case .username:
Expand Down
2 changes: 1 addition & 1 deletion Sources/Authenticator/States/SignInState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion Sources/Authenticator/States/SignUpState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/Authenticator/Utilities/FieldValidator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Header: View,
Expand Down Expand Up @@ -44,7 +45,7 @@ public struct ConfirmResetPasswordView<Header: View,
using: { value in
let configuration = state.configuration.passwordProtectionSettings
return FieldValidators.password(
minLength: configuration.minLength,
minLength: configuration.minLength ?? 0,
characterPolicy: configuration.characterPolicy.asPasswordCharactersPolicy()
)(value)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Amplify
import SwiftUI
@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin

/// Represents the content being displayed when the ``Authenticator`` is in the ``AuthenticatorStep/confirmSignInWithNewPassword`` step.
public struct ConfirmSignInWithNewPasswordView<Header: View,
Expand Down Expand Up @@ -40,7 +41,7 @@ public struct ConfirmSignInWithNewPasswordView<Header: View,
using: { value in
let configuration = state.configuration.passwordProtectionSettings
return FieldValidators.password(
minLength: configuration.minLength,
minLength: configuration.minLength ?? 0,
characterPolicy: configuration.characterPolicy.asPasswordCharactersPolicy()
)(value)
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/Authenticator/Views/ResetPasswordView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Amplify
import SwiftUI
@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin

/// Represents the content being displayed when the ``Authenticator`` is in the ``AuthenticatorStep/resetPassword`` step.
///
Expand Down Expand Up @@ -75,7 +76,7 @@ public struct ResetPasswordView<Header: View,
}

@ViewBuilder private func createUsernameInput(
for usernameAttribute: CognitoConfiguration.UsernameAttribute
for usernameAttribute: UsernameAttribute
) -> some View {
switch usernameAttribute {
case .username:
Expand Down
3 changes: 2 additions & 1 deletion Sources/Authenticator/Views/SignInView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -153,7 +154,7 @@ public struct SignInView<Header: View,
}

@ViewBuilder private func createUsernameInput(
for usernameAttribute: CognitoConfiguration.UsernameAttribute
for usernameAttribute: UsernameAttribute
) -> some View {
switch usernameAttribute {
case .username:
Expand Down
3 changes: 2 additions & 1 deletion Sources/Authenticator/Views/SignUpView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Header: View,
Expand Down Expand Up @@ -199,7 +200,7 @@ extension SignUpView {
if case .password = field.attributeType {
let configuration = self.state.configuration.passwordProtectionSettings
return FieldValidators.password(
minLength: configuration.minLength,
minLength: configuration.minLength ?? 0,
characterPolicy: configuration.characterPolicy.asPasswordCharactersPolicy()
)(value)
} else if case .passwordConfirmation = field.attributeType, value != self.state.password {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Foundation
@testable import Authenticator
@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin

class MockAuthenticatorState: AuthenticatorStateProtocol {
var authenticationService: AuthenticationService = MockAuthenticationService()
Expand Down
1 change: 1 addition & 0 deletions Tests/AuthenticatorTests/States/SignUpStateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Amplify
@testable import Authenticator
import XCTest
@_spi(InternalAmplifyConfiguration) import AWSCognitoAuthPlugin

class SignUpStateTests: XCTestCase {
private var state: SignUpState!
Expand Down
Loading