Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/change-file-in-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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') }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/closed-issue-message.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: Closed Issue Message
on:
issues:
types: [closed]
permissions:
issues: write

jobs:
auto_comment:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/stale_issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
schedule:
- cron: "0 0 * * *"

permissions:
issues: write
pull-requests: write

jobs:
cleanup:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* permissions and limitations under the License.
*/

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Xunit;
Expand All @@ -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<AttributeType>()
SignUpRequest signUpRequest = new SignUpRequest()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"},
},
ValidationData = new List<AttributeType>()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}
}
};
ClientId = pool.ClientID,
Password = "PassWord1!",
Username = "User5",
UserAttributes = new List<AttributeType>()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"},
},
ValidationData = new List<AttributeType>()
{
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* permissions and limitations under the License.
*/

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Xunit;
Expand All @@ -27,24 +28,33 @@ public class AuthenticationCreateUserTests : BaseAuthenticationTestClass
{
public AuthenticationCreateUserTests() : base()
{
AdminCreateUserRequest createUserRequest = new AdminCreateUserRequest()
try
{
TemporaryPassword = "PassWord1!",
Username = "User5",
UserAttributes = new List<AttributeType>()
AdminCreateUserRequest createUserRequest = new AdminCreateUserRequest()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"},
},
ValidationData = new List<AttributeType>()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}
},
UserPoolId = pool.PoolID
};
TemporaryPassword = "PassWord1!",
Username = "User5",
UserAttributes = new List<AttributeType>()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"},
},
ValidationData = new List<AttributeType>()
{
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AttributeType>()
SignUpRequest signUpRequest = new SignUpRequest()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"},
},
ValidationData = new List<AttributeType>()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"}
}
};
ClientId = pool.ClientID,
Password = "PassWord1!",
Username = "User5",
UserAttributes = new List<AttributeType>()
{
new AttributeType() {Name=CognitoConstants.UserAttrEmail, Value="xxx@yyy.zzz"},
},
ValidationData = new List<AttributeType>()
{
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public partial class BaseAuthenticationTestClass : IDisposable
protected AmazonCognitoIdentityProviderClient provider;
protected CognitoUserPool pool;
protected CognitoUser user;
private readonly string _testInstanceId;

static BaseAuthenticationTestClass()
{
Expand All @@ -39,64 +40,78 @@ static BaseAuthenticationTestClass()

public BaseAuthenticationTestClass()
{
UserPoolPolicyType passwordPolicy = new UserPoolPolicyType();
List<SchemaAttributeType> requiredAttributes = new List<SchemaAttributeType>();
List<string> verifiedAttributes = new List<string>();
_testInstanceId = Guid.NewGuid().ToString("N")[..8]; // Short unique identifier

try
{
UserPoolPolicyType passwordPolicy = new UserPoolPolicyType();
List<SchemaAttributeType> requiredAttributes = new List<SchemaAttributeType>();
List<string> verifiedAttributes = new List<string>();

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
}
}

/// <summary>
Expand All @@ -105,18 +120,38 @@ public BaseAuthenticationTestClass()
/// </summary>
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}");
}
}
}
Expand Down
Loading
Loading