Skip to content

Commit

Permalink
Changed SSO Authentication to cache SSO unique ID, for determining wh…
Browse files Browse the repository at this point in the history
…ether we've seen them before, before registering or authenticating them.
  • Loading branch information
jezzsantos committed Jan 13, 2025
1 parent dab3dbd commit 5640fa1
Show file tree
Hide file tree
Showing 30 changed files with 495 additions and 249 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ CREATE TABLE [dbo].[SSOUser]
[FirstName] [nvarchar](max) NULL,
[LastName] [nvarchar](max) NULL,
[ProviderName] [nvarchar](max) NULL,
[ProviderUId] [nvarchar](max) NULL,
[Timezone] [nvarchar](max) NULL,
[Tokens] [nvarchar](max) NULL,
[UserId] [nvarchar](100) NULL,
Expand Down
7 changes: 7 additions & 0 deletions src/Application.Resources.Shared/Identity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,11 @@ public enum TokenType
OtherToken = 0, // e.g. idToken
AccessToken = 1, // access_token
RefreshToken = 2 // refresh_token
}

public class SSOUser : IIdentifiableResource
{
public required string Id { get; set; }

public required string ProviderUId { get; set; }
}
3 changes: 0 additions & 3 deletions src/Application.Services.Shared/IEndUsersService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ namespace Application.Services.Shared;

public interface IEndUsersService
{
Task<Result<Optional<EndUser>, Error>> FindPersonByEmailPrivateAsync(ICallerContext caller, string emailAddress,
CancellationToken cancellationToken);

Task<Result<EndUserWithMemberships, Error>> GetMembershipsPrivateAsync(ICallerContext caller, string id,
CancellationToken cancellationToken);

Expand Down
36 changes: 21 additions & 15 deletions src/Common/CountryCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,63 @@ public static class CountryCodes
public static readonly CountryCodeIso3166 Australia = CountryCodeIso3166.Create("Australia", "AU", "AUS", "036");
public static readonly CountryCodeIso3166 UnitedStates =
CountryCodeIso3166.Create("United States of America", "US", "USA", "840");
public static readonly CountryCodeIso3166 Default = UnitedStates;
public static readonly CountryCodeIso3166 Default = UnitedStates; //EXTEND: set your default country code
public static readonly CountryCodeIso3166 NewZealand = CountryCodeIso3166.Create("New Zealand", "NZ", "NZL", "554");

#if TESTINGONLY
public static readonly CountryCodeIso3166 Test = CountryCodeIso3166.Create("Test", "XX", "XXX", "001");
internal static readonly CountryCodeIso3166 Test = CountryCodeIso3166.Create("Test", "XX", "XXX", "001");
#endif

/// <summary>
/// Whether the specified timezone by its <see cref="countryCodeAlpha3" /> exists
/// Returns the specified timezone by its <see cref="countryCode" /> if it exists,
/// which tries to match the 3 letter <see cref="CountryCodeIso3166.Alpha3" /> first,
/// then tries to match the 2 letter <see cref="CountryCodeIso3166.Alpha2" />,
/// then tries to match the number <see cref="CountryCodeIso3166.Numeric" /> last.
/// </summary>
public static bool Exists(string? countryCodeAlpha3)
public static bool Exists(string? countryCode)
{
if (countryCodeAlpha3.NotExists())
if (countryCode.NotExists())
{
return false;
}

return Find(countryCodeAlpha3).Exists();
return Find(countryCode).Exists();
}

/// <summary>
/// Returns the specified timezone by its <see cref="countryCodeAlpha3" /> if it exists
/// Returns the specified timezone by its <see cref="countryCode" /> if it exists,
/// which tries to match the 3 letter <see cref="CountryCodeIso3166.Alpha3" /> first,
/// then tries to match the 2 letter <see cref="CountryCodeIso3166.Alpha2" />,
/// then tries to match the number <see cref="CountryCodeIso3166.Numeric" /> last.
/// </summary>
public static CountryCodeIso3166? Find(string? countryCodeAlpha3)
public static CountryCodeIso3166? Find(string? countryCode)
{
if (countryCodeAlpha3.NotExists())
if (countryCode.NotExists())
{
return null;
}

#if TESTINGONLY
if (countryCodeAlpha3 == Test.Alpha3
|| countryCodeAlpha3 == Test.Alpha2
|| countryCodeAlpha3 == Test.Numeric)
if (countryCode == Test.Alpha3
|| countryCode == Test.Alpha2
|| countryCode == Test.Numeric)
{
return Test;
}
#endif
var alpha3 = CountryCodesResolver.GetByAlpha3Code(countryCodeAlpha3);
var alpha3 = CountryCodesResolver.GetByAlpha3Code(countryCode);
if (alpha3.Exists())
{
return CountryCodeIso3166.Create(alpha3.Name, alpha3.Alpha2, alpha3.Alpha3, alpha3.NumericCode);
}

var alpha2 = CountryCodesResolver.GetByAlpha2Code(countryCodeAlpha3);
var alpha2 = CountryCodesResolver.GetByAlpha2Code(countryCode);
if (alpha2.Exists())
{
return CountryCodeIso3166.Create(alpha2.Name, alpha2.Alpha2, alpha2.Alpha3, alpha2.NumericCode);
}

var numeric = CountryCodesResolver.GetList().FirstOrDefault(cc => cc.NumericCode == countryCodeAlpha3);
var numeric = CountryCodesResolver.GetList().FirstOrDefault(cc => cc.NumericCode == countryCode);
if (numeric.Exists())
{
return CountryCodeIso3166.Create(numeric.Name, numeric.Alpha2, numeric.Alpha3, numeric.NumericCode);
Expand Down
9 changes: 8 additions & 1 deletion src/Common/Timezones.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@ namespace Common;

public static class Timezones
{
//See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
public const string GmtIANA = "GMT";
public const string NewZealandIANA = "Pacific/Auckland";
public const string NewZealandWindows = "New Zealand Standard Time";
public const string SydneyIANA = "Australia/Sydney";
public const string UniversalCoordinatedIANA = "Etc/UTC";
public const string UniversalCoordinatedWindows = "UTC";
public const string UsPacificIANA = "US/Pacific";
public static readonly TimezoneIANA Sydney = TimezoneIANA.Create(SydneyIANA, TimeSpan.FromHours(10),
"AEST", TimeSpan.FromHours(11), "AEDT");
public static readonly TimezoneIANA NewZealand = TimezoneIANA.Create(NewZealandIANA, TimeSpan.FromHours(12),
"NZST", TimeSpan.FromHours(13), "NZDT");
public static readonly TimezoneIANA Default = NewZealand;
public static readonly TimezoneIANA UsPacific = TimezoneIANA.Create(UsPacificIANA, TimeSpan.FromHours(-8),
"PST", TimeSpan.FromHours(-7), "PDT");
public static readonly TimezoneIANA Default = UsPacific; //EXTEND: set your default country code
public static readonly TimezoneIANA Gmt = TimezoneIANA.Create(GmtIANA, TimeSpan.FromHours(0),
"GMT", TimeSpan.FromHours(0), "GMT");

#if TESTINGONLY
public static readonly TimezoneIANA Test = TimezoneIANA.Create("testTimezone", TimeSpan.FromHours(1), "TSST",
Expand Down
2 changes: 2 additions & 0 deletions src/Domain.Events.Shared/Identities/SSOUsers/DetailsAdded.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ public DetailsAdded()
public string? LastName { get; set; }

public required string Timezone { get; set; }

public required string ProviderUId { get; set; }
}
39 changes: 0 additions & 39 deletions src/EndUsersApplication.UnitTests/EndUsersApplicationSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -592,45 +592,6 @@ public async Task WhenUnassignPlatformRolesAsync_ThenUnassigns()
}
#endif

[Fact]
public async Task WhenFindPersonByEmailAsyncAndNotExists_ThenReturnsNone()
{
_userProfilesService.Setup(ups =>
ups.FindPersonByEmailAddressPrivateAsync(It.IsAny<ICallerContext>(), It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(Optional<UserProfile>.None);
_invitationRepository.Setup(rep =>
rep.FindInvitedGuestByEmailAddressAsync(It.IsAny<EmailAddress>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(Optional.None<EndUserRoot>());

var result =
await _application.FindPersonByEmailAddressAsync(_caller.Object, "[email protected]",
CancellationToken.None);

result.Should().BeSuccess();
result.Value.Should().BeNone();
}

[Fact]
public async Task WhenFindPersonByEmailAsyncAndExists_ThenReturns()
{
var endUser = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value;
_userProfilesService.Setup(ups =>
ups.FindPersonByEmailAddressPrivateAsync(It.IsAny<ICallerContext>(), It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(Optional<UserProfile>.None);
_invitationRepository.Setup(rep =>
rep.FindInvitedGuestByEmailAddressAsync(It.IsAny<EmailAddress>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(endUser.ToOptional());

var result =
await _application.FindPersonByEmailAddressAsync(_caller.Object, "[email protected]",
CancellationToken.None);

result.Should().BeSuccess();
result.Value.Value.Id.Should().Be("anid");
}

[Fact]
public async Task WhenGetMembershipsAndNotRegisteredOrMemberAsync_ThenReturnsUser()
{
Expand Down
21 changes: 0 additions & 21 deletions src/EndUsersApplication/EndUsersApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,27 +140,6 @@ public async Task<Result<EndUser, Error>> ChangeDefaultMembershipAsync(ICallerCo
return user.ToUser();
}

public async Task<Result<Optional<EndUser>, Error>> FindPersonByEmailAddressAsync(ICallerContext caller,
string emailAddress, CancellationToken cancellationToken)
{
var email = EmailAddress.Create(emailAddress);
if (email.IsFailure)
{
return email.Error;
}

var retrieved =
await FindRegisteredPersonOrInvitedGuestByEmailAddressAsync(caller, email.Value, cancellationToken);
if (retrieved.IsFailure)
{
return retrieved.Error;
}

return retrieved.Value.HasValue
? retrieved.Value.Value.User.ToUser().ToOptional()
: Optional<EndUser>.None;
}

public async Task<Result<EndUserWithMemberships, Error>> GetMembershipsAsync(ICallerContext caller, string id,
CancellationToken cancellationToken)
{
Expand Down
3 changes: 0 additions & 3 deletions src/EndUsersApplication/IEndUsersApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ Task<Result<EndUser, Error>> AssignPlatformRolesAsync(ICallerContext caller, str
Task<Result<EndUser, Error>> ChangeDefaultMembershipAsync(ICallerContext caller, string organizationId,
CancellationToken cancellationToken);

Task<Result<Optional<EndUser>, Error>> FindPersonByEmailAddressAsync(ICallerContext caller, string emailAddress,
CancellationToken cancellationToken);

Task<Result<EndUserWithMemberships, Error>> GetMembershipsAsync(ICallerContext caller, string id,
CancellationToken cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ public EndUsersInProcessServiceClient(IEndUsersApplication endUsersApplication)
_endUsersApplication = endUsersApplication;
}

public async Task<Result<Optional<EndUser>, Error>> FindPersonByEmailPrivateAsync(ICallerContext caller,
string emailAddress, CancellationToken cancellationToken)
{
return await _endUsersApplication.FindPersonByEmailAddressAsync(caller, emailAddress, cancellationToken);
}

public async Task<Result<EndUserWithMemberships, Error>> GetMembershipsPrivateAsync(ICallerContext caller,
string id, CancellationToken cancellationToken)
{
Expand Down
Loading

0 comments on commit 5640fa1

Please sign in to comment.