Skip to content

Commit

Permalink
Fixed ISSOService interface to be clearer and to use correct user
Browse files Browse the repository at this point in the history
  • Loading branch information
jezzsantos committed Nov 25, 2024
1 parent 570b894 commit d4f6dfd
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 77 deletions.
24 changes: 22 additions & 2 deletions src/Application.Services.Shared/ISSOService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,32 @@

namespace Application.Services.Shared;

/// <summary>
/// Defines a service to access and manage the SSO AuthTokens for a user
/// </summary>
public interface ISSOService
{
/// <summary>
/// Retrieves the list of tokens for the current user
/// </summary>
Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensAsync(ICallerContext caller,
string userId,
CancellationToken cancellationToken);

Task<Result<ProviderAuthenticationTokens, Error>> RefreshTokenAsync(ICallerContext caller, string userId,
/// <summary>
/// Retrieves the list of tokens for the specified user
/// </summary>
Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensOnBehalfOfUserAsync(ICallerContext caller,
string userId, CancellationToken cancellationToken);

/// <summary>
/// Refreshes the specified <see cref="refreshToken" /> for the current user
/// </summary>
Task<Result<ProviderAuthenticationTokens, Error>> RefreshTokenAsync(ICallerContext caller,
string providerName, string refreshToken, CancellationToken cancellationToken);

/// <summary>
/// Refreshes the specified <see cref="refreshToken" /> for the specified user
/// </summary>
Task<Result<ProviderAuthenticationTokens, Error>> RefreshTokenOnBehalfOfUserAsync(ICallerContext caller,
string userId, string providerName, string refreshToken, CancellationToken cancellationToken);
}
6 changes: 4 additions & 2 deletions src/IdentityApplication.UnitTests/SSOProvidersServiceSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,8 @@ public async Task WhenGetTokensAsyncAndNoTokens_ThenReturnsNone()
It.IsAny<CancellationToken>()))
.ReturnsAsync(ssoUser.ToOptional());

var result = await _service.GetTokensAsync(_caller.Object, "auserid".ToId(), CancellationToken.None);
var result =
await _service.GetTokensOnBehalfOfUserAsync(_caller.Object, "auserid".ToId(), CancellationToken.None);

result.Should().BeSuccess();
result.Value.Count.Should().Be(0);
Expand All @@ -367,7 +368,8 @@ public async Task WhenGetTokensAsync_ThenReturnsError()
It.IsAny<CancellationToken>()))
.ReturnsAsync(ssoUser.ToOptional());

var result = await _service.GetTokensAsync(_caller.Object, "auserid".ToId(), CancellationToken.None);
var result =
await _service.GetTokensOnBehalfOfUserAsync(_caller.Object, "auserid".ToId(), CancellationToken.None);

result.Should().BeSuccess();
result.Value.Count.Should().Be(1);
Expand Down
25 changes: 13 additions & 12 deletions src/IdentityApplication.UnitTests/SingleSignOnApplicationSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,14 +319,14 @@ public async Task WhenAuthenticateAndPersonExists_ThenIssuesToken()
}

[Fact]
public async Task WhenRefreshTokenAsyncAndProviderUserNotExists_ThenReturnsError()
public async Task WhenRefreshTokenOnBehalfOfUserAsyncAndProviderUserNotExists_ThenReturnsError()
{
_ssoProvidersService.Setup(sp =>
sp.FindByUserIdAsync(It.IsAny<ICallerContext>(), It.IsAny<Identifier>(), It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(Optional<ISSOAuthenticationProvider>.None);

var result = await _application.RefreshTokenAsync(_caller.Object, "auserid", "aprovidername",
var result = await _application.RefreshTokenOnBehalfOfUserAsync(_caller.Object, "auserid", "aprovidername",
"arefreshtoken", CancellationToken.None);

result.Should().BeError(ErrorCode.NotAuthenticated);
Expand All @@ -341,14 +341,14 @@ public async Task WhenRefreshTokenAsyncAndProviderUserNotExists_ThenReturnsError
}

[Fact]
public async Task WhenRefreshTokenAsyncAndEndUserNotExists_ThenReturnsError()
public async Task WhenRefreshTokenOnBehalfOfUserAsyncAndEndUserNotExists_ThenReturnsError()
{
_endUsersService.Setup(eus =>
eus.GetUserPrivateAsync(It.IsAny<ICallerContext>(), It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(Error.EntityNotFound());

var result = await _application.RefreshTokenAsync(_caller.Object, "auserid", "aprovidername",
var result = await _application.RefreshTokenOnBehalfOfUserAsync(_caller.Object, "auserid", "aprovidername",
"arefreshtoken", CancellationToken.None);

result.Should().BeError(ErrorCode.NotAuthenticated);
Expand All @@ -366,7 +366,7 @@ public async Task WhenRefreshTokenAsyncAndEndUserNotExists_ThenReturnsError()
}

[Fact]
public async Task WhenRefreshTokenAsyncAndPersonIsSuspended_ThenReturnsError()
public async Task WhenRefreshTokenOnBehalfOfUserAsyncAndPersonIsSuspended_ThenReturnsError()
{
_endUsersService.Setup(eus =>
eus.GetUserPrivateAsync(It.IsAny<ICallerContext>(), It.IsAny<string>(),
Expand All @@ -382,7 +382,7 @@ public async Task WhenRefreshTokenAsyncAndPersonIsSuspended_ThenReturnsError()
It.IsAny<CancellationToken>()))
.ReturnsAsync(Error.Unexpected("amessage"));

var result = await _application.RefreshTokenAsync(_caller.Object, "auserid", "aprovidername",
var result = await _application.RefreshTokenOnBehalfOfUserAsync(_caller.Object, "auserid", "aprovidername",
"arefreshtoken", CancellationToken.None);

result.Should().BeError(ErrorCode.EntityExists, Resources.SingleSignOnApplication_AccountSuspended);
Expand All @@ -398,7 +398,7 @@ public async Task WhenRefreshTokenAsyncAndPersonIsSuspended_ThenReturnsError()
}

[Fact]
public async Task WhenRefreshTokenAsyncAndRefreshErrors_ThenReturnsError()
public async Task WhenRefreshTokenOnBehalfOfUserAsyncAndRefreshErrors_ThenReturnsError()
{
_endUsersService.Setup(eus =>
eus.GetUserPrivateAsync(It.IsAny<ICallerContext>(), It.IsAny<string>(),
Expand All @@ -414,7 +414,7 @@ public async Task WhenRefreshTokenAsyncAndRefreshErrors_ThenReturnsError()
It.IsAny<CancellationToken>()))
.ReturnsAsync(Error.Unexpected("amessage"));

var result = await _application.RefreshTokenAsync(_caller.Object, "auserid", "aprovidername",
var result = await _application.RefreshTokenOnBehalfOfUserAsync(_caller.Object, "auserid", "aprovidername",
"arefreshtoken", CancellationToken.None);

result.Should().BeError(ErrorCode.NotAuthenticated);
Expand All @@ -432,7 +432,7 @@ public async Task WhenRefreshTokenAsyncAndRefreshErrors_ThenReturnsError()
}

[Fact]
public async Task WhenRefreshTokenAsync_ThenRefreshed()
public async Task WhenRefreshTokenOnBehalfOfUserAsync_ThenRefreshed()
{
_endUsersService.Setup(eus =>
eus.GetUserPrivateAsync(It.IsAny<ICallerContext>(), It.IsAny<string>(),
Expand Down Expand Up @@ -472,7 +472,7 @@ public async Task WhenRefreshTokenAsync_ThenRefreshed()
]
});

var result = await _application.RefreshTokenAsync(_caller.Object, "auserid", "aprovidername",
var result = await _application.RefreshTokenOnBehalfOfUserAsync(_caller.Object, "auserid", "aprovidername",
"arefreshtoken", CancellationToken.None);

result.Should().BeSuccess();
Expand Down Expand Up @@ -506,7 +506,8 @@ public async Task WhenGetTokensAsync_ThenReturnsTokens()
{
var datum = DateTime.UtcNow;
_ssoProvidersService.Setup(sps =>
sps.GetTokensAsync(It.IsAny<ICallerContext>(), It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
sps.GetTokensOnBehalfOfUserAsync(It.IsAny<ICallerContext>(), It.IsAny<Identifier>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<ProviderAuthenticationTokens>
{
new()
Expand Down Expand Up @@ -536,7 +537,7 @@ public async Task WhenGetTokensAsync_ThenReturnsTokens()
}
});

var result = await _application.GetTokensAsync(_caller.Object, "auserid", CancellationToken.None);
var result = await _application.GetTokensOnBehalfOfUserAsync(_caller.Object, "auserid", CancellationToken.None);

result.Should().BeSuccess();
result.Value.Count.Should().Be(1);
Expand Down
21 changes: 10 additions & 11 deletions src/IdentityApplication/ApplicationServices/ISSOProvidersService.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
using Application.Interfaces;
using Application.Resources.Shared;
using Common;
using Domain.Common.ValueObjects;

namespace IdentityApplication.ApplicationServices;

/// <summary>
/// Defines a service for accessing registered <see cref="ISSOAuthenticationProvider" />s
/// Defines a service for accessing the registered <see cref="ISSOAuthenticationProvider" />s
/// </summary>
public interface ISSOProvidersService
{
Task<Result<Optional<ISSOAuthenticationProvider>, Error>> FindByProviderNameAsync(string providerName,
CancellationToken cancellationToken);

Task<Result<Optional<ISSOAuthenticationProvider>, Error>> FindByUserIdAsync(ICallerContext caller,
Identifier userId, string providerName,
CancellationToken cancellationToken);
string userId, string providerName, CancellationToken cancellationToken);

Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensAsync(ICallerContext caller,
Identifier userId, CancellationToken cancellationToken);

Task<Result<Error>> SaveUserInfoAsync(ICallerContext caller, string providerName, Identifier userId,
SSOUserInfo userInfo,
CancellationToken cancellationToken);

Task<Result<Error>> SaveUserTokensAsync(ICallerContext caller, string providerName, Identifier userId,
ProviderAuthenticationTokens tokens,
CancellationToken cancellationToken);
Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensOnBehalfOfUserAsync(ICallerContext caller,
string userId, CancellationToken cancellationToken);

Task<Result<Error>> SaveUserInfoAsync(ICallerContext caller, string providerName, string userId,
SSOUserInfo userInfo, CancellationToken cancellationToken);

Task<Result<Error>> SaveUserTokensAsync(ICallerContext caller, string providerName, string userId,
ProviderAuthenticationTokens tokens, CancellationToken cancellationToken);
}
88 changes: 50 additions & 38 deletions src/IdentityApplication/ApplicationServices/SSOProvidersService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public Task<Result<Optional<ISSOAuthenticationProvider>, Error>> FindByProviderN
}

public async Task<Result<Optional<ISSOAuthenticationProvider>, Error>> FindByUserIdAsync(ICallerContext caller,
Identifier userId, string providerName, CancellationToken cancellationToken)
string userId, string providerName, CancellationToken cancellationToken)
{
var provider =
_authenticationProviders.FirstOrDefault(provider => provider.ProviderName.EqualsIgnoreCase(providerName));
Expand All @@ -54,7 +54,7 @@ public async Task<Result<Optional<ISSOAuthenticationProvider>, Error>> FindByUse
return Optional<ISSOAuthenticationProvider>.None;
}

var retrieved = await _repository.FindByUserIdAsync(provider.ProviderName, userId, cancellationToken);
var retrieved = await _repository.FindByUserIdAsync(provider.ProviderName, userId.ToId(), cancellationToken);
if (retrieved.IsFailure)
{
return retrieved.Error;
Expand All @@ -77,42 +77,18 @@ public async Task<Result<Optional<ISSOAuthenticationProvider>, Error>> FindByUse
}

public async Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensAsync(ICallerContext caller,
Identifier userId, CancellationToken cancellationToken)
CancellationToken cancellationToken)
{
var allTokens = new List<ProviderAuthenticationTokens>();
foreach (var provider in _authenticationProviders)
{
var retrievedUser = await _repository.FindByUserIdAsync(provider.ProviderName, userId, cancellationToken);
if (retrievedUser.IsFailure)
{
return retrievedUser.Error;
}

if (!retrievedUser.Value.HasValue)
{
continue;
}

var user = retrievedUser.Value.Value;
var viewed = user.ViewUser(caller.ToCallerId());
if (viewed.IsFailure)
{
return viewed.Error;
}

if (!user.Tokens.HasValue)
{
continue;
}

var tokens = user.Tokens.Value;
allTokens.Add(tokens.ToProviderAuthenticationTokens(provider.ProviderName, _encryptionService));
}
return await GetTokensInternalAsync(caller.ToCallerId(), cancellationToken);
}

return allTokens;
public async Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensOnBehalfOfUserAsync(
ICallerContext caller, string userId, CancellationToken cancellationToken)
{
return await GetTokensInternalAsync(userId.ToId(), cancellationToken);
}

public async Task<Result<Error>> SaveUserInfoAsync(ICallerContext caller, string providerName, Identifier userId,
public async Task<Result<Error>> SaveUserInfoAsync(ICallerContext caller, string providerName, string userId,
SSOUserInfo userInfo, CancellationToken cancellationToken)
{
var retrievedProvider = await FindByProviderNameAsync(providerName, cancellationToken);
Expand All @@ -128,7 +104,7 @@ public async Task<Result<Error>> SaveUserInfoAsync(ICallerContext caller, string

var provider = retrievedProvider.Value.Value;
var retrievedUser =
await _repository.FindByUserIdAsync(provider.ProviderName, userId, cancellationToken);
await _repository.FindByUserIdAsync(provider.ProviderName, userId.ToId(), cancellationToken);
if (retrievedUser.IsFailure)
{
return retrievedUser.Error;
Expand All @@ -141,7 +117,7 @@ public async Task<Result<Error>> SaveUserInfoAsync(ICallerContext caller, string
}
else
{
var created = SSOUserRoot.Create(_recorder, _identifierFactory, providerName, userId);
var created = SSOUserRoot.Create(_recorder, _identifierFactory, providerName, userId.ToId());
if (created.IsFailure)
{
return created.Error;
Expand Down Expand Up @@ -205,7 +181,7 @@ public async Task<Result<Error>> SaveUserInfoAsync(ICallerContext caller, string
return Result.Ok;
}

public async Task<Result<Error>> SaveUserTokensAsync(ICallerContext caller, string providerName, Identifier userId,
public async Task<Result<Error>> SaveUserTokensAsync(ICallerContext caller, string providerName, string userId,
ProviderAuthenticationTokens tokens, CancellationToken cancellationToken)
{
var retrievedProvider = await FindByProviderNameAsync(providerName, cancellationToken);
Expand All @@ -221,7 +197,7 @@ public async Task<Result<Error>> SaveUserTokensAsync(ICallerContext caller, stri

var provider = retrievedProvider.Value.Value;
var retrievedUser =
await _repository.FindByUserIdAsync(provider.ProviderName, userId, cancellationToken);
await _repository.FindByUserIdAsync(provider.ProviderName, userId.ToId(), cancellationToken);
if (retrievedUser.IsFailure)
{
return retrievedUser.Error;
Expand Down Expand Up @@ -263,6 +239,42 @@ public async Task<Result<Error>> SaveUserTokensAsync(ICallerContext caller, stri

return Result.Ok;
}

private async Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensInternalAsync(
Identifier userId, CancellationToken cancellationToken)
{
var allTokens = new List<ProviderAuthenticationTokens>();
foreach (var provider in _authenticationProviders)
{
var retrievedUser = await _repository.FindByUserIdAsync(provider.ProviderName, userId, cancellationToken);
if (retrievedUser.IsFailure)
{
return retrievedUser.Error;
}

if (!retrievedUser.Value.HasValue)
{
continue;
}

var user = retrievedUser.Value.Value;
var viewed = user.ViewUser(userId);
if (viewed.IsFailure)
{
return viewed.Error;
}

if (!user.Tokens.HasValue)
{
continue;
}

var tokens = user.Tokens.Value;
allTokens.Add(tokens.ToProviderAuthenticationTokens(provider.ProviderName, _encryptionService));
}

return allTokens;
}
}

internal static class SSOProvidersServiceConversionExtensions
Expand Down
12 changes: 8 additions & 4 deletions src/IdentityApplication/ISingleSignOnApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ Task<Result<AuthenticateTokens, Error>> AuthenticateAsync(ICallerContext caller,
string providerName, string authCode, string? username, CancellationToken cancellationToken);

Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensAsync(ICallerContext caller,
string userId,
CancellationToken cancellationToken);

Task<Result<ProviderAuthenticationTokens, Error>> RefreshTokenAsync(ICallerContext caller, string userId,
string providerName,
string refreshToken, CancellationToken cancellationToken);
Task<Result<IReadOnlyList<ProviderAuthenticationTokens>, Error>> GetTokensOnBehalfOfUserAsync(ICallerContext caller,
string userId, CancellationToken cancellationToken);

Task<Result<ProviderAuthenticationTokens, Error>> RefreshTokenAsync(ICallerContext caller,
string providerName, string refreshToken, CancellationToken cancellationToken);

Task<Result<ProviderAuthenticationTokens, Error>> RefreshTokenOnBehalfOfUserAsync(ICallerContext caller,
string userId, string providerName, string refreshToken, CancellationToken cancellationToken);
}
Loading

0 comments on commit d4f6dfd

Please sign in to comment.