Skip to content

Commit

Permalink
test(auth): gen2 integ tests (#3673)
Browse files Browse the repository at this point in the history
  • Loading branch information
lawmicha authored May 6, 2024
1 parent 7846328 commit f078184
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class AWSAuthBaseTest: XCTestCase {
}

var amplifyConfigurationFile = "testconfiguration/AWSCognitoAuthPluginIntegrationTests-amplifyconfiguration"
let amplifyOutputsFile =
var amplifyOutputsFile =
"testconfiguration/AWSCognitoAuthPluginIntegrationTests-amplify_outputs"
let credentialsFile = "testconfiguration/AWSCognitoAuthPluginIntegrationTests-credentials"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class AuthDeleteUserTests: AWSAuthBaseTest {
do {
_ = try await AuthSignInHelper.signInUser(username: username, password: password)
XCTFail("signIn after account deletion should fail")
} catch AuthError.notAuthorized {
// App clients with "Prevent user existence errors" enabled will return this.
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html
} catch let error as AuthError {
switch error {
case .service(_, _, let underlying):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class TOTPSetupWhenAuthenticatedTests: AWSAuthBaseTest {
password: password,
email: randomEmail
)

XCTAssertTrue(didSucceed, "Signup and sign in should succeed")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class TOTPSetupWhenUnauthenticatedTests: AWSAuthBaseTest {
override func setUp() async throws {
// Use a custom configuration these tests
amplifyConfigurationFile = "testconfiguration/AWSCognitoAuthPluginMFARequiredIntegrationTests-amplifyconfiguration"
amplifyOutputsFile = "testconfiguration/AWSCognitoAuthPluginMFARequiredIntegrationTests-amplify_outputs"
try await super.setUp()
AuthSessionHelper.clearSession()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The following steps demonstrate how to setup the integration tests for auth plug

The integration test require auth configured with AWS Cognito User Pool and AWS Cognito Identity Pool.

Create the auth resource with the following use cases:
- Sign in with username
- A pre-sign up lambda to auto confirm a user signing up.
- MFA enabled and optional for TOTP.

Example `amplify add auth` steps (some of these steps may not be out of date)
```
amplify add auth
Expand Down Expand Up @@ -87,8 +93,17 @@ This will create a amplifyconfiguration.json file in your local, copy that file
For Auth Device tests:
Follow steps here (https://docs.amplify.aws/lib/auth/device_features/q/platform/ios/#configure-auth-category)[https://docs.amplify.aws/lib/auth/device_features/q/platform/ios/#configure-auth-category] and select "Always" for "Do you want to remember your user's devices?"

For User Attributes tests:
Follow steps here (https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-email-phone-verification.html?icmpid=docs_cognito_console_help_panel#user-pool-settings-verifications-verify-attribute-updates) and uncheck "Keep original attribute value active when an update is pending".

For MFA required tests (tests in `TOTPSetupWhenUnauthenticatedTests.swift`):

1. Create a new amplify project (`amplify init`) and follow the same steps as above, except MFA is required.
2. Then copy over the configuration as
``` ~/.aws-amplify/amplify-ios/testconfiguration/AWSCognitoAuthPluginMFARequiredIntegrationTests-amplifyconfiguration.json
```

# Schema: AuthGen2IntegrationTests
# Schema: AuthGen2IntegrationTests

## Schema: AuthGen2IntegrationTests

Expand All @@ -104,21 +119,21 @@ At the time this was written, it follows the steps from here https://docs.amplif
{
...
"devDependencies": {
"@aws-amplify/backend": "^0.13.0-beta.14",
"@aws-amplify/backend-cli": "^0.12.0-beta.16",
"aws-cdk": "^2.134.0",
"aws-cdk-lib": "^2.134.0",
"@aws-amplify/backend": "^0.15.0",
"@aws-amplify/backend-cli": "^0.15.0",
"aws-cdk": "^2.139.0",
"aws-cdk-lib": "^2.139.0",
"constructs": "^10.3.0",
"esbuild": "^0.20.2",
"tsx": "^4.7.1",
"typescript": "^5.4.3"
"tsx": "^4.7.3",
"typescript": "^5.4.5"
},
"dependencies": {
"aws-amplify": "^6.0.25"
}
"aws-amplify": "^6.2.0"
},
}

```

2. Update `amplify/auth/resource.ts`. The resulting file should look like this

```ts
Expand All @@ -132,6 +147,11 @@ export const auth = defineAuth({
loginWith: {
email: true
},
multifactor: {
mode: 'OPTIONAL',
totp: true,
sms: true,
},
triggers: {
// configure a trigger to point to a function definition
preSignUp: defineFunction({
Expand All @@ -155,6 +175,8 @@ export const handler: PreSignUpTriggerHandler = async (event) => {
Update `backend.ts`

```ts
// Override sign in with username as the username

const { cfnUserPool } = backend.auth.resources.cfnResources
cfnUserPool.usernameAttributes = []

Expand All @@ -171,14 +193,30 @@ cfnUserPool.addPropertyOverride(
},
}
);

// Enable Device Tracking
// https://docs.amplify.aws/react/build-a-backend/auth/concepts/multi-factor-authentication/#remember-a-device

cfnUserPool.addPropertyOverride('DeviceConfiguration', {
ChallengeRequiredOnNewDevice: true,
DeviceOnlyRememberedOnUserPrompt: false
});

// Disable verifying updates to email addresses
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-userattributeupdatesettings.html

cfnUserPool.addPropertyOverride('UserAttributeUpdateSettings', {
AttributesRequireVerificationBeforeUpdate: []
});

```

4. Deploy the backend with npx amplify sandbox

For example, this deploys to a sandbox env and generates the amplify_outputs.json file.

```
npx amplify sandbox --config-out-dir ./config --config-version 1 --profile [PROFILE]
npx amplify sandbox --config-out-dir ./config --profile [PROFILE]
```

5. Copy the `amplify_outputs.json` file over to the test directory as `AWSCognitoAuthPluginIntegrationTests-amplify_outputs.json`. The tests will automatically pick this file up. Create the directories in this path first if it currently doesn't exist.
Expand All @@ -187,6 +225,19 @@ npx amplify sandbox --config-out-dir ./config --config-version 1 --profile [PROF
cp amplify_outputs.json ~/.aws-amplify/amplify-ios/testconfiguration/AWSCognitoAuthPluginIntegrationTests-amplify_outputs.json
```

6. For MFA required (tests in `TOTPSetupWhenUnauthenticatedTests.swift`), update `amplify/auth/resource.ts` with multifactor mode required.

```
// ...
multifactor: {
mode: 'REQUIRED',
// ...
```

7. Deploy, it may be easier to deploy a separate backend for MFA required using branches (see below) copy over the configuration as
``` ~/.aws-amplify/amplify-ios/testconfiguration/AWSCognitoAuthPluginMFARequiredIntegrationTests-amplify_outputs.json
```

### Deploying from a branch (Optional)

If you want to be able utilize Git commits for deployments
Expand All @@ -206,5 +257,5 @@ If you want to be able utilize Git commits for deployments
7. Generate the `amplify_outputs.json` configuration file

```
npx amplify generate config --branch main --app-id [APP_ID] --profile [AWS_PROFILE] --config-version 1
npx amplify generate outputs --branch main --app-id [APP_ID] --profile [AWS_PROFILE]
```
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ class AuthConfirmResetPasswordTests: AWSAuthBaseTest {
/// - When:
/// - I invoke confirmResetPassword with the user
/// - Then:
/// - I should get a userNotFound error.
///
/// - I should get a userNotFound error. (Gen1 - PreventUserExistenceErrors disabled)
/// - I should get a codeExpired error. (Gen2 - PreventUserExistenceErrors enabled)
/// (https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html#cognito-user-pool-managing-errors-password-reset)
func testUserNotFoundResetPassword() async throws {
do {
try await Amplify.Auth.confirmResetPassword(for: "user-non-exists", with: "password", confirmationCode: "123", options: nil)
XCTFail("resetPassword with non existing user should not return result")
} catch AuthError.service(_, _, let error as AWSCognitoAuthError) where [.userNotFound, .limitExceeded].contains(error) {
} catch AuthError.service(_, _, let error as AWSCognitoAuthError) where [.userNotFound, .codeExpired, .limitExceeded].contains(error) {
return
} catch {
XCTFail("Expected .userNotFound or .limitExceeded error. received: \(error)")
XCTFail("Expected .userNotFound, .codeExpired, or .limitExceeded error. received: \(error)")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,21 @@ class AuthResetPasswordTests: AWSAuthBaseTest {
///
func testUserNotFoundResetPassword() async throws {
do {
_ = try await Amplify.Auth.resetPassword(for: "user-non-exists", options: nil)
XCTFail("resetPassword with non existing user should not return result")
let randomUserNotExists = UUID().uuidString
let result = try await Amplify.Auth.resetPassword(for: randomUserNotExists, options: nil)

// App clients with "Prevent user existence errors" enabled will return a simulated result
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html#cognito-user-pool-managing-errors-password-reset
// Gen2 configuration is enabled with Prevent User existence errors, while Gen1 backend is not.
if useGen2Configuration {
XCTAssertFalse(result.isPasswordReset)
guard case .confirmResetPasswordWithCode = result.nextStep else {
XCTFail("Expected confirmResultPasswordCode in result, result: \(result)")
return
}
} else {
XCTFail("resetPassword with non existing user should not return result, result returned: \(result)")
}
} catch AuthError.service(_, _, let error as AWSCognitoAuthError) where [.userNotFound, .limitExceeded].contains(error) {
return
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class AuthSRPSignInTests: AWSAuthBaseTest {
XCTFail("SignIn with unknown user should not succeed")
} catch AuthError.notAuthorized {
// App clients with "Prevent user existence errors" enabled will return this.
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html
} catch let error as AuthError {
let underlyingError = error.underlyingError as? AWSCognitoAuthError
switch underlyingError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,26 @@ class AuthConfirmSignUpTests: AWSAuthBaseTest {
/// - When:
/// - I invoke confirmSignUp with the user
/// - Then:
/// - I should get a userNotFound error.
/// - I should get a userNotFound error. (Gen1 - PreventUserExistenceErrors disabled)
/// - I should get a codeMismatch error. (Gen2 - PreventUserExistenceErrors enabled)
/// (https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html#cognito-user-pool-managing-errors-password-reset)
///
func testUserNotFoundConfirmSignUp() async throws {
do {
_ = try await Amplify.Auth.confirmSignUp(for: "user-non-exists", confirmationCode: "232")
XCTFail("Confirm signUp with non existing user should not return result")
} catch {
guard let authError = error as? AuthError, let cognitoError = authError.underlyingError as? AWSCognitoAuthError,
case .userNotFound = cognitoError else {
XCTFail("Should return userNotFound")
guard let authError = error as? AuthError, let cognitoError = authError.underlyingError as? AWSCognitoAuthError else {
XCTFail("Should return cognitoAuthError")
return
}

switch cognitoError {
case .userNotFound, .codeMismatch:
return
default:
XCTFail("Should be either `userNotFound` or `codeMismatch`")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,19 @@ class AuthResendSignUpCodeTests: AWSAuthBaseTest {
///
func testUserNotFoundResendSignUpCode() async throws {
do {
_ = try await Amplify.Auth.resendSignUpCode(for: "user-non-exists")
XCTFail("resendSignUpCode with non existing user should not return result")
let result = try await Amplify.Auth.resendSignUpCode(for: "user-non-exists")

// App clients with "Prevent user existence errors" enabled will return a simulated result
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html#cognito-user-pool-managing-errors-password-reset
// Gen2 configuration is enabled with Prevent User existence errors, while Gen1 backend is not.
if useGen2Configuration {
guard case .email = result.destination else {
XCTFail("Expected email detination in result, result: \(result)")
return
}
} else {
XCTFail("resendSignUpCode with non existing user should not return result, result returned: \(result)")
}
} catch let error as AuthError {
let underlyingError = error.underlyingError as? AWSCognitoAuthError
switch underlyingError {
Expand Down

0 comments on commit f078184

Please sign in to comment.