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: add support for .NET SDK V4 #245

Merged
merged 1 commit into from
Oct 22, 2024
Merged
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
13 changes: 13 additions & 0 deletions .autover/changes/e128958a-1f95-4d56-9b25-9de91316fb26.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Projects": [
{
"Name": "Amazon.AspNetCore.Identity.Cognito",
"Type": "Major",
"ChangelogMessages": [
"Updated the .NET SDK dependencies to the latest version 4.0.0-preview.4",
"Marked project as trimmable",
"Added SourceLink support"
]
}
]
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,60 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net8.0</TargetFrameworks>
<CodeAnalysisRuleSet>../ruleset.xml</CodeAnalysisRuleSet>
<Version>3.0.2</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Amazon.AspNetCore.Identity.Cognito</PackageId>
<Title>ASP.NET Core Identity Provider for Amazon Cognito</Title>
<Product>Amazon.AspNetCore.Identity.Cognito</Product>
<Description>Simplifies using Amazon Cognito as a membership storage solution for building ASP.NET Core web applications using ASP.NET Core Identity.</Description>
<Authors>Amazon Web Services</Authors>
<PackageTags>AWS;Amazon;aws-sdk-v3;Cognito;Identity</PackageTags>
<PackageTags>AWS;Amazon;aws-sdk-v4;Cognito;Identity</PackageTags>
<PackageProjectUrl>https://github.com/aws/aws-aspnet-cognito-identity-provider/</PackageProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/aws/aws-aspnet-cognito-identity-provider/</RepositoryUrl>
<Company>Amazon Web Services</Company>
<SignAssembly>true</SignAssembly>
<AssemblyVersion>3.0.2</AssemblyVersion>
<FileVersion>3.0.2</FileVersion>

<EmbedUntrackedSources>true</EmbedUntrackedSources>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>

<Version>4.0.0-preview.1</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0'">
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">

<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Amazon.Extensions.CognitoAuthentication" Version="2.5.2" />
<PackageReference Include="AWSSDK.CognitoIdentity" Version="3.7.300.74" />
<PackageReference Include="AWSSDK.CognitoIdentityProvider" Version="3.7.303.19" />
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.300" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.1">
<PackageReference Include="Amazon.Extensions.CognitoAuthentication" Version="3.0.0-preview.1" />
<PackageReference Include="AWSSDK.CognitoIdentity" Version="4.0.0-preview.4" />
<PackageReference Include="AWSSDK.CognitoIdentityProvider" Version="4.0.0-preview.4" />
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.0-preview.4" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.8.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<None Include="../../LICENSE" Pack="true" PackagePath="" />
<None Include="../../icon.png" Pack="true" PackagePath="" />
<None Include="../../README.md" Pack="true" PackagePath="" />
</ItemGroup>

<Choose>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public string Normalize(string key)
}
#endif

#if NETCOREAPP3_1
#if !NETSTANDARD2_0
/// <summary>
/// Returns a normalized representation of the specified <paramref name="name"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class CognitoPasswordValidator : IPasswordValidator<CognitoUser>
/// <param name="user">The user whose password should be validated.</param>
/// <param name="password">The password supplied for validation</param>
/// <returns>The task object representing the asynchronous operation, containing the <see cref="IdentityResult"/>
/// of the operation.
/// of the operation.</returns>
public async Task<IdentityResult> ValidateAsync(UserManager<CognitoUser> manager, CognitoUser user, string password)
{
// Retrieve the password policy set by the user's user pool
Expand All @@ -43,28 +43,31 @@ public async Task<IdentityResult> ValidateAsync(UserManager<CognitoUser> manager
var errorDescriber = new IdentityErrorDescriber();
var errors = new List<IdentityError>();

if (password is null)
password = string.Empty;

if (password.Length < passwordPolicy.MinimumLength)
{
errors.Add(errorDescriber.PasswordTooShort(passwordPolicy.MinimumLength));
errors.Add(errorDescriber.PasswordTooShort(passwordPolicy.MinimumLength ?? 0));
}

if (!password.Any(char.IsLower) && passwordPolicy.RequireLowercase)
if (!password.Any(char.IsLower) && (passwordPolicy.RequireLowercase ?? false))
{
errors.Add(errorDescriber.PasswordRequiresLower());
}

if (!password.Any(char.IsUpper) && passwordPolicy.RequireUppercase)
if (!password.Any(char.IsUpper) && (passwordPolicy.RequireUppercase ?? false))
{
errors.Add(errorDescriber.PasswordRequiresUpper());
}

if (!password.Any(char.IsNumber) && passwordPolicy.RequireNumbers)
if (!password.Any(char.IsNumber) && (passwordPolicy.RequireNumbers ?? false))
{
errors.Add(errorDescriber.PasswordRequiresDigit());
}

var passwordContainsASymbol = password.IndexOfAny(CognitoSymbols) >= 0;
if (!passwordContainsASymbol && passwordPolicy.RequireSymbols)
if (!passwordContainsASymbol && (passwordPolicy.RequireSymbols ?? false))
{
errors.Add(errorDescriber.PasswordRequiresNonAlphanumeric());
}
Expand Down
2 changes: 1 addition & 1 deletion src/Amazon.AspNetCore.Identity.Cognito/CognitoRoleStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public virtual async Task<TRole> FindByNameAsync(string roleName, CancellationTo
}, cancellationToken).ConfigureAwait(false);

return new CognitoRole(response.Group.GroupName, response.Group.Description,
response.Group.Precedence, response.Group.RoleArn) as TRole;
response.Group.Precedence ?? 0, response.Group.RoleArn) as TRole;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class CognitoSignInManager<TUser> : SignInManager<TUser> where TUser : Co
private const string Cognito2FAChallengeNameType = "Cognito2FAChallengeNameType";
private const string Cognito2FAProviderKey = "Amazon Cognito 2FA";

#if NETCOREAPP3_1
#if !NETSTANDARD2_0
public CognitoSignInManager(UserManager<TUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<TUser> claimsFactory,
Expand Down
12 changes: 5 additions & 7 deletions src/Amazon.AspNetCore.Identity.Cognito/CognitoUserManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,10 @@ public override async Task<TUser> FindByEmailAsync(string email)
{
throw new ArgumentNullException(nameof(email));
}
#if NETCOREAPP3_1
email = NormalizeEmail(email);
#endif
#if NETSTANDARD2_0
email = NormalizeKey(email);
#else
email = NormalizeEmail(email);
#endif
var user = await _userStore.FindByEmailAsync(email, CancellationToken).ConfigureAwait(false);
if (user != null)
Expand Down Expand Up @@ -124,11 +123,10 @@ public override async Task<TUser> FindByNameAsync(string userName)
{
throw new ArgumentNullException(nameof(userName));
}
#if NETCOREAPP3_1
userName = NormalizeName(userName);
#endif
#if NETSTANDARD2_0
userName = NormalizeKey(userName);
#else
userName = NormalizeName(userName);
#endif
var user = await _userStore.FindByNameAsync(userName, CancellationToken).ConfigureAwait(false);
if (user != null)
Expand Down Expand Up @@ -180,7 +178,7 @@ private async Task PopulateTokens(TUser user, string claimType, string claimValu
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the AuthFlowResponse object
/// if the specified <paramref name="password" /> matches the one store for the <paramref name="user"/>,
/// otherwise null.</returns>
public virtual Task<AuthFlowResponse> CheckPasswordAsync(TUser user, string password)
public new virtual Task<AuthFlowResponse> CheckPasswordAsync(TUser user, string password)
philasmar marked this conversation as resolved.
Show resolved Hide resolved
{
ThrowIfDisposed();
if (user == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public virtual async Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationT
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);

return details.UserAttributes.Select(att => new Claim(att.Name, att.Value)).ToList();
return details.UserAttributes?.Select(att => new Claim(att.Name, att.Value)).ToList() ?? new List<Claim>();
}
catch (AmazonCognitoIdentityProviderException e)
{
Expand Down Expand Up @@ -134,10 +134,10 @@ public virtual async Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claim
var userClaims = await GetClaimsAsync(user, cancellationToken).ConfigureAwait(false);

// Only removes the claims that the user actually have.
var matchedClaims = userClaims.Select(claim => new { claim.Type, claim.Value })
var matchedClaims = userClaims?.Select(claim => new { claim.Type, claim.Value })
.Intersect(claims.Select(claim => new { claim.Type, claim.Value }));

if (matchedClaims.Any())
if (matchedClaims != null && matchedClaims.Any())
{
try
{
Expand Down Expand Up @@ -182,8 +182,17 @@ public async Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationT
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);

return response.Users.Select(user => _pool.GetUser(user.Username, user.UserStatus,
user.Attributes.ToDictionary(att => att.Name, att => att.Value))).ToList() as IList<TUser>;
return response.Users?
.Select(user =>
_pool.GetUser(
user.Username,
user.UserStatus,
user.Attributes?
.ToDictionary(att => att.Name, att => att.Value)
?? new Dictionary<string, string>()
)
)
.ToList() as IList<TUser> ?? new List<TUser>();
}
catch (AmazonCognitoIdentityProviderException e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Amazon.Extensions.CognitoAuthentication;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -46,11 +47,13 @@ public virtual async Task<TUser> FindByEmailAsync(string normalizedEmail, Cancel
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);

if (result.Users.Count > 0)
if (result.Users != null && result.Users.Count > 0)
{
return _pool.GetUser(result.Users[0].Username,
result.Users[0].UserStatus,
result.Users[0].Attributes.ToDictionary(att => att.Name, att => att.Value)) as TUser;
result.Users[0].Attributes?
.ToDictionary(att => att.Name, att => att.Value) ??
new Dictionary<string, string>()) as TUser;
}
}
catch (AmazonCognitoIdentityProviderException e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ public virtual async Task<IdentityResult> CreateAsync(TUser user, IDictionary<st
cancellationToken.ThrowIfCancellationRequested();
try
{
await _pool.AdminSignupAsync(user.UserID, user.Attributes, validationData).ConfigureAwait(false);
await _pool.AdminSignupAsync(
user.UserID,
user.Attributes ?? new Dictionary<string, string>(),
validationData ?? new Dictionary<string, string>()).ConfigureAwait(false);
return IdentityResult.Success;
}
catch (AmazonCognitoIdentityProviderException e)
Expand Down Expand Up @@ -191,9 +194,9 @@ public virtual async Task<IdentityResult> UpdateAsync(TUser user, CancellationTo
{
// Only update user writable attributes.
var clientConfig = await _pool.GetUserPoolClientConfiguration().ConfigureAwait(false);
var newValues = clientConfig.WriteAttributes
var newValues = clientConfig.WriteAttributes?
.Where(key => user.Attributes.ContainsKey(key))
.ToDictionary(key => key, key => user.Attributes[key]);
.ToDictionary(key => key, key => user.Attributes[key]) ?? new Dictionary<string, string>();

await _cognitoClient.AdminUpdateUserAttributesAsync(new AdminUpdateUserAttributesRequest
{
Expand All @@ -218,7 +221,7 @@ await _cognitoClient.AdminUpdateUserAttributesAsync(new AdminUpdateUserAttribute
internal List<AttributeType> CreateAttributeList(IDictionary<string, string> attributeDict)
{
List<AttributeType> attributeList = new List<AttributeType>();
foreach (KeyValuePair<string, string> data in attributeDict)
foreach (KeyValuePair<string, string> data in attributeDict ?? new Dictionary<string, string>())
{
AttributeType attribute = new AttributeType()
{
Expand Down Expand Up @@ -315,7 +318,7 @@ public virtual async Task<IList<string>> GetRolesAsync(TUser user, CancellationT
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);

return response.Groups.Select(group => group.GroupName).ToList();
return response.Groups?.Select(group => group.GroupName).ToList() ?? new List<string>();
}
catch (AmazonCognitoIdentityProviderException e)
{
Expand All @@ -341,7 +344,7 @@ public virtual async Task<bool> IsInRoleAsync(TUser user, string roleName, Cance
}

var userRoles = await GetRolesAsync(user, cancellationToken).ConfigureAwait(false);
return userRoles.Contains(roleName);
return userRoles?.Contains(roleName) ?? false;
}

/// <summary>
Expand All @@ -364,8 +367,16 @@ public virtual async Task<IList<TUser>> GetUsersInRoleAsync(string roleName, Can
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);

return response.Users.Select(user => _pool.GetUser(user.Username, user.UserStatus,
user.Attributes.ToDictionary(att => att.Name, att => att.Value))).ToList() as IList<TUser>;
return response.Users?
.Select(user =>
_pool.GetUser(
user.Username,
user.UserStatus,
user.Attributes?
.ToDictionary(att => att.Name, att => att.Value)
?? new Dictionary<string, string>()
)
).ToList() as IList<TUser> ?? new List<TUser>();
}
catch (AmazonCognitoIdentityProviderException e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public virtual async Task<bool> GetTwoFactorEnabledAsync(TUser user, Cancellatio
{
var userSettings = await _cognitoClient.AdminGetUserAsync(request, cancellationToken).ConfigureAwait(false);

return userSettings.MFAOptions.Count > 0;
return userSettings.MFAOptions?.Count > 0;
}
catch (AmazonCognitoIdentityProviderException e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,11 @@ public virtual async Task<IEnumerable<CognitoUser>> GetUsersAsync(CognitoAttribu
throw new CognitoServiceException("Failed to retrieve the list of users from Cognito.", e);
}

foreach (var user in response.Users)
foreach (var user in response.Users ?? new List<UserType>())
{
result.Add(new CognitoUser(user.Username, _pool.ClientID, _pool, _cognitoClient, null,
user.UserStatus.Value, user.Username,
user.Attributes.ToDictionary(attribute => attribute.Name, attribute => attribute.Value)));
user.Attributes?.ToDictionary(attribute => attribute.Name, attribute => attribute.Value) ?? new Dictionary<string, string>()));
}

} while (!string.IsNullOrEmpty(response.PaginationToken));
Expand All @@ -327,7 +327,7 @@ public virtual async Task<IdentityResult> CreateAsync(TUser user, string passwor

try
{
await _pool.SignUpAsync(user.UserID, password, user.Attributes, validationData).ConfigureAwait(false);
await _pool.SignUpAsync(user.UserID, password, user.Attributes ?? new Dictionary<string, string>(), validationData).ConfigureAwait(false);
return IdentityResult.Success;
}
catch (AmazonCognitoIdentityProviderException e)
Expand Down Expand Up @@ -509,7 +509,7 @@ private async Task<string> GetAttributeValueAsync(TUser user, string attributeNa
}

var clientConfig = await _pool.GetUserPoolClientConfiguration().ConfigureAwait(false);
if (!clientConfig.ReadAttributes.Contains(attributeName))
if (!clientConfig.ReadAttributes?.Contains(attributeName) ?? true)
{
throw new NotAuthorizedException(string.Format("Reading attribute {0} is not allowed by the user pool client configuration.", attributeName));
}
Expand Down Expand Up @@ -543,7 +543,7 @@ private async Task SetAttributeValueAsync(TUser user, string attributeName, stri

var clientConfig = await _pool.GetUserPoolClientConfiguration().ConfigureAwait(false);

if (!clientConfig.WriteAttributes.Contains(attributeName))
if (!clientConfig.WriteAttributes?.Contains(attributeName) ?? true)
{
throw new NotAuthorizedException(string.Format("Writing to attribute {0} is not allowed by the user pool client configuration.", attributeName));
}
Expand Down
Loading