diff --git a/.github/workflows/change-file-in-pr.yml b/.github/workflows/change-file-in-pr.yml index 01e9a61..3160ed7 100644 --- a/.github/workflows/change-file-in-pr.yml +++ b/.github/workflows/change-file-in-pr.yml @@ -4,6 +4,9 @@ on: pull_request: types: [opened, synchronize, reopened, labeled] +permissions: + contents: read + jobs: check-files-in-directory: if: ${{ !contains(github.event.pull_request.labels.*.name, 'Release Not Needed') && !contains(github.event.pull_request.labels.*.name, 'Release PR') }} diff --git a/.github/workflows/closed-issue-message.yml b/.github/workflows/closed-issue-message.yml index 3340afb..11d5a82 100644 --- a/.github/workflows/closed-issue-message.yml +++ b/.github/workflows/closed-issue-message.yml @@ -2,6 +2,9 @@ name: Closed Issue Message on: issues: types: [closed] +permissions: + issues: write + jobs: auto_comment: runs-on: ubuntu-latest diff --git a/.github/workflows/stale_issues.yml b/.github/workflows/stale_issues.yml index 76ef673..d6e631f 100644 --- a/.github/workflows/stale_issues.yml +++ b/.github/workflows/stale_issues.yml @@ -5,6 +5,10 @@ on: schedule: - cron: "0 0 * * *" +permissions: + issues: write + pull-requests: write + jobs: cleanup: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index d08d4e8..e43f281 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## Release 2025-07-29 ### Amazon.Extensions.CognitoAuthentication (3.1.1) -* Fix Auth with device and device confirmation +* **BREAKING CHANGE**: Fix Auth with device and device confirmation. The third parameter in `GenerateDeviceVerifier()` method has been renamed from `username` to `deviceKey`. Existing code will compile but fail at runtime - update calls to pass the device key instead of username. ## Release 2025-06-03 diff --git a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationConfirmUserTests.cs b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationConfirmUserTests.cs index 3afebcb..11c97a5 100644 --- a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationConfirmUserTests.cs +++ b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationConfirmUserTests.cs @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +using System; using System.Threading.Tasks; using System.Collections.Generic; using Xunit; @@ -26,30 +27,39 @@ public class AuthenticationConfirmUserTests : BaseAuthenticationTestClass { public AuthenticationConfirmUserTests() : base() { - SignUpRequest signUpRequest = new SignUpRequest() + try { - ClientId = pool.ClientID, - Password = "PassWord1!", - Username = "User5", - UserAttributes = new List() + SignUpRequest signUpRequest = new SignUpRequest() { - new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}, - }, - ValidationData = new List() - { - new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"} - } - }; + ClientId = pool.ClientID, + Password = "PassWord1!", + Username = "User5", + UserAttributes = new List() + { + new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}, + }, + ValidationData = new List() + { + new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"} + } + }; - SignUpResponse signUpResponse = provider.SignUpAsync(signUpRequest).Result; + SignUpResponse signUpResponse = provider.SignUpAsync(signUpRequest).Result; - AdminConfirmSignUpRequest confirmRequest = new AdminConfirmSignUpRequest() + AdminConfirmSignUpRequest confirmRequest = new AdminConfirmSignUpRequest() + { + Username = "User5", + UserPoolId = pool.PoolID + }; + AdminConfirmSignUpResponse confirmResponse = provider.AdminConfirmSignUpAsync(confirmRequest).Result; + user = new CognitoUser("User5", pool.ClientID, pool, provider); + } + catch (Exception ex) { - Username = "User5", - UserPoolId = pool.PoolID - }; - AdminConfirmSignUpResponse confirmResponse = provider.AdminConfirmSignUpAsync(confirmRequest).Result; - user = new CognitoUser("User5", pool.ClientID, pool, provider); + Console.WriteLine($"AuthenticationConfirmUserTests constructor failed: {ex.Message}"); + Dispose(); // Clean up the user pool that was created in base constructor + throw; // Re-throw so test still fails as expected + } } //Tests SRP authentication flow for web applications diff --git a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationCreateUserTests.cs b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationCreateUserTests.cs index 054ca3e..7787147 100644 --- a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationCreateUserTests.cs +++ b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationCreateUserTests.cs @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +using System; using System.Threading.Tasks; using System.Collections.Generic; using Xunit; @@ -27,24 +28,33 @@ public class AuthenticationCreateUserTests : BaseAuthenticationTestClass { public AuthenticationCreateUserTests() : base() { - AdminCreateUserRequest createUserRequest = new AdminCreateUserRequest() + try { - TemporaryPassword = "PassWord1!", - Username = "User5", - UserAttributes = new List() + AdminCreateUserRequest createUserRequest = new AdminCreateUserRequest() { - new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}, - }, - ValidationData = new List() - { - new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"} - }, - UserPoolId = pool.PoolID - }; + TemporaryPassword = "PassWord1!", + Username = "User5", + UserAttributes = new List() + { + new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}, + }, + ValidationData = new List() + { + new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"} + }, + UserPoolId = pool.PoolID + }; - AdminCreateUserResponse createReponse = provider.AdminCreateUserAsync(createUserRequest).Result; + AdminCreateUserResponse createReponse = provider.AdminCreateUserAsync(createUserRequest).Result; - user = new CognitoUser("User5", pool.ClientID, pool, provider); + user = new CognitoUser("User5", pool.ClientID, pool, provider); + } + catch (Exception ex) + { + Console.WriteLine($"AuthenticationCreateUserTests constructor failed: {ex.Message}"); + Dispose(); // Clean up the user pool that was created in base constructor + throw; // Re-throw so test still fails as expected + } } // Tests the sessionauthentication process with a NEW_PASSWORD_REQURIED flow diff --git a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationSignUpUserTests.cs b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationSignUpUserTests.cs index 2267bf5..08490f1 100644 --- a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationSignUpUserTests.cs +++ b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/AuthenticationSignUpUserTests.cs @@ -27,23 +27,32 @@ public class AuthenticationSignUpUserTests : BaseAuthenticationTestClass { public AuthenticationSignUpUserTests() : base() { - SignUpRequest signUpRequest = new SignUpRequest() + try { - ClientId = pool.ClientID, - Password = "PassWord1!", - Username = "User5", - UserAttributes = new List() + SignUpRequest signUpRequest = new SignUpRequest() { - new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}, - }, - ValidationData = new List() - { - new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"} - } - }; + ClientId = pool.ClientID, + Password = "PassWord1!", + Username = "User5", + UserAttributes = new List() + { + new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}, + }, + ValidationData = new List() + { + new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"} + } + }; - SignUpResponse signUpResponse = provider.SignUpAsync(signUpRequest).Result; - user = new CognitoUser("User5", pool.ClientID, pool, provider); + SignUpResponse signUpResponse = provider.SignUpAsync(signUpRequest).Result; + user = new CognitoUser("User5", pool.ClientID, pool, provider); + } + catch (Exception ex) + { + Console.WriteLine($"AuthenticationSignUpUserTests constructor failed: {ex.Message}"); + Dispose(); // Clean up the user pool that was created in base constructor + throw; // Re-throw so test still fails as expected + } } // Tests the SignUp method (using random, dummy email) diff --git a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/BaseAuthenticationTestClass.cs b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/BaseAuthenticationTestClass.cs index 7987d4a..915e117 100644 --- a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/BaseAuthenticationTestClass.cs +++ b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/BaseAuthenticationTestClass.cs @@ -31,6 +31,7 @@ public partial class BaseAuthenticationTestClass : IDisposable protected AmazonCognitoIdentityProviderClient provider; protected CognitoUserPool pool; protected CognitoUser user; + private readonly string _testInstanceId; static BaseAuthenticationTestClass() { @@ -39,64 +40,78 @@ static BaseAuthenticationTestClass() public BaseAuthenticationTestClass() { - UserPoolPolicyType passwordPolicy = new UserPoolPolicyType(); - List requiredAttributes = new List(); - List verifiedAttributes = new List(); + _testInstanceId = Guid.NewGuid().ToString("N")[..8]; // Short unique identifier + + try + { + UserPoolPolicyType passwordPolicy = new UserPoolPolicyType(); + List requiredAttributes = new List(); + List verifiedAttributes = new List(); - provider = new AmazonCognitoIdentityProviderClient(); + provider = new AmazonCognitoIdentityProviderClient(); - AdminCreateUserConfigType adminCreateUser = new AdminCreateUserConfigType() - { - UnusedAccountValidityDays = 8, - AllowAdminCreateUserOnly = false - }; + AdminCreateUserConfigType adminCreateUser = new AdminCreateUserConfigType() + { + UnusedAccountValidityDays = 8, + AllowAdminCreateUserOnly = false + }; - passwordPolicy.PasswordPolicy = new PasswordPolicyType() - { - MinimumLength = 8, - RequireNumbers = true, - RequireSymbols = true, - RequireUppercase = true, - RequireLowercase = true - }; - - SchemaAttributeType tempSchema = new SchemaAttributeType() - { - Required = true, - Name = CognitoConstants.UserAttrEmail, - AttributeDataType = AttributeDataType.String - }; - requiredAttributes.Add(tempSchema); - verifiedAttributes.Add(CognitoConstants.UserAttrEmail); - - CreateUserPoolRequest createPoolRequest = new CreateUserPoolRequest - { - PoolName = "testPool_" + DateTime.UtcNow.ToString("yyyyMMdd_HHmmss"), - Policies = passwordPolicy, - Schema = requiredAttributes, - AdminCreateUserConfig = adminCreateUser, - MfaConfiguration = "OFF", - AutoVerifiedAttributes = verifiedAttributes, - DeviceConfiguration = new DeviceConfigurationType() + passwordPolicy.PasswordPolicy = new PasswordPolicyType() { - ChallengeRequiredOnNewDevice = false, - DeviceOnlyRememberedOnUserPrompt = false - } - }; - CreateUserPoolResponse createPoolResponse = provider.CreateUserPoolAsync(createPoolRequest).Result; - UserPoolType userPoolCreated = createPoolResponse.UserPool; + MinimumLength = 8, + RequireNumbers = true, + RequireSymbols = true, + RequireUppercase = true, + RequireLowercase = true + }; - CreateUserPoolClientRequest clientRequest = new CreateUserPoolClientRequest() - { - ClientName = "App_" + DateTime.UtcNow.ToString("yyyyMMdd_HHmmss"), - UserPoolId = userPoolCreated.Id, - GenerateSecret = false, + SchemaAttributeType tempSchema = new SchemaAttributeType() + { + Required = true, + Name = CognitoConstants.UserAttrEmail, + AttributeDataType = AttributeDataType.String + }; + requiredAttributes.Add(tempSchema); + verifiedAttributes.Add(CognitoConstants.UserAttrEmail); + + CreateUserPoolRequest createPoolRequest = new CreateUserPoolRequest + { + PoolName = "testPool_" + DateTime.UtcNow.ToString("yyyyMMdd_HHmmss"), + Policies = passwordPolicy, + Schema = requiredAttributes, + AdminCreateUserConfig = adminCreateUser, + MfaConfiguration = "OFF", + AutoVerifiedAttributes = verifiedAttributes, + DeviceConfiguration = new DeviceConfigurationType() + { + ChallengeRequiredOnNewDevice = false, + DeviceOnlyRememberedOnUserPrompt = false + } + }; + CreateUserPoolResponse createPoolResponse = provider.CreateUserPoolAsync(createPoolRequest).Result; + UserPoolType userPoolCreated = createPoolResponse.UserPool; + + CreateUserPoolClientRequest clientRequest = new CreateUserPoolClientRequest() + { + ClientName = "App_" + DateTime.UtcNow.ToString("yyyyMMdd_HHmmss"), + UserPoolId = userPoolCreated.Id, + GenerateSecret = false, - }; - CreateUserPoolClientResponse clientResponse = provider.CreateUserPoolClientAsync(clientRequest).Result; - UserPoolClientType clientCreated = clientResponse.UserPoolClient; + }; + CreateUserPoolClientResponse clientResponse = provider.CreateUserPoolClientAsync(clientRequest).Result; + UserPoolClientType clientCreated = clientResponse.UserPoolClient; - pool = new CognitoUserPool(userPoolCreated.Id, clientCreated.ClientId, provider, ""); + pool = new CognitoUserPool(userPoolCreated.Id, clientCreated.ClientId, provider, ""); + + // Log user pool creation + Console.WriteLine($"[{_testInstanceId}] Created user pool: {createPoolRequest.PoolName} (ID: {userPoolCreated.Id}) at {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"); + } + catch (Exception ex) + { + Console.WriteLine($"[{_testInstanceId}] Constructor failed: {ex.Message}"); + Dispose(); // Clean up any resources that were created + throw; // Re-throw the original exception so test fails as expected + } } /// @@ -105,18 +120,38 @@ public BaseAuthenticationTestClass() /// public virtual void Dispose() { - try + // Handle partial construction - pool might be null if constructor failed early + if (pool?.PoolID != null) { - provider.DeleteUserPoolAsync(new DeleteUserPoolRequest() + Console.WriteLine($"[{_testInstanceId}] Disposing user pool: {pool.PoolID} at {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"); + try + { + provider?.DeleteUserPoolAsync(new DeleteUserPoolRequest() + { + UserPoolId = pool.PoolID + }).Wait(); + + Console.WriteLine($"[{_testInstanceId}] Successfully disposed user pool: {pool.PoolID}"); + } + catch (Exception ex) { - UserPoolId = pool.PoolID - }).Wait(); + Console.WriteLine($"[{_testInstanceId}] ERROR disposing user pool {pool.PoolID}: {ex.Message}"); + System.Diagnostics.Debug.WriteLine($"Full exception details: {ex}"); + } + } + else + { + Console.WriteLine($"[{_testInstanceId}] Dispose called but no user pool to clean up (partial construction)"); + } - provider.Dispose(); + // Always dispose the provider if it exists + try + { + provider?.Dispose(); } catch (Exception ex) { - System.Diagnostics.Debug.WriteLine(ex.Message); + Console.WriteLine($"[{_testInstanceId}] ERROR disposing provider: {ex.Message}"); } } } diff --git a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/MfaAuthenticationTests.cs b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/MfaAuthenticationTests.cs index ffe1493..b989327 100644 --- a/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/MfaAuthenticationTests.cs +++ b/test/Amazon.Extensions.CognitoAuthentication.IntegrationTests/MfaAuthenticationTests.cs @@ -62,14 +62,16 @@ await Assert.ThrowsAsync(() => user.RespondToSmsMfaAuthAs /// public MfaAuthenticationTests() { - //Delete pool created by BaseAuthenticationTestClass - if(pool != null) + try { - provider.DeleteUserPoolAsync(new DeleteUserPoolRequest() + //Delete pool created by BaseAuthenticationTestClass + if(pool != null) { - UserPoolId = pool.PoolID - }).Wait(); - } + provider.DeleteUserPoolAsync(new DeleteUserPoolRequest() + { + UserPoolId = pool.PoolID + }).Wait(); + } UserPoolPolicyType passwordPolicy = new UserPoolPolicyType(); List requiredAttributes = new List(); @@ -217,6 +219,13 @@ public MfaAuthenticationTests() AdminConfirmSignUpResponse confirmResponse = provider.AdminConfirmSignUpAsync(confirmRequest).Result; this.user = new CognitoUser("User5", clientCreated.ClientId, pool, provider); + } + catch (Exception ex) + { + Console.WriteLine($"MfaAuthenticationTests constructor failed: {ex.Message}"); + Dispose(); // Clean up any resources that were created + throw; // Re-throw so test still fails as expected + } } ///