Skip to content
This repository has been archived by the owner on Jul 9, 2024. It is now read-only.

Commit

Permalink
Implemented account sign-out. (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
Utar94 authored May 7, 2024
1 parent ecac279 commit 3b6a755
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Logitar.Portal.Contracts.Sessions;
using Logitar.Portal.Contracts.Users;
using MediatR;

namespace Logitar.Master.Application.Accounts.Commands;

public record SignOutCommand : IRequest<Unit>
{
public Guid? SessionId { get; }
public Guid? UserId { get; }

public SignOutCommand(Session session)
{
SessionId = session.Id;
}
public SignOutCommand(User user)
{
UserId = user.Id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using MediatR;

namespace Logitar.Master.Application.Accounts.Commands;

internal class SignOutCommandHandler : IRequestHandler<SignOutCommand, Unit>
{
private readonly ISessionService _sessionService;
private readonly IUserService _userService;

public SignOutCommandHandler(ISessionService sessionService, IUserService userService)
{
_sessionService = sessionService;
_userService = userService;
}

public async Task<Unit> Handle(SignOutCommand command, CancellationToken cancellationToken)
{
if (command.SessionId.HasValue)
{
_ = await _sessionService.SignOutAsync(command.SessionId.Value, cancellationToken);
}
else if (command.UserId.HasValue)
{
_ = await _userService.SignOutAsync(command.UserId.Value, cancellationToken);
}

return Unit.Value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public interface ISessionService
Task<Session?> FindAsync(Guid id, CancellationToken cancellationToken = default);
Task<Session> RenewAsync(string refreshToken, IEnumerable<CustomAttribute>? customAttributes = null, CancellationToken cancellationToken = default);
Task<Session> SignInAsync(User user, string password, IEnumerable<CustomAttribute>? customAttributes = null, CancellationToken cancellationToken = default);
Task<Session?> SignOutAsync(Guid id, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ public interface IUserService
Task<User> CompleteProfileAsync(Guid userId, CompleteProfilePayload payload, Phone? phone = null, CancellationToken cancellationToken = default);
Task<User> CreateAsync(Email email, CancellationToken cancellationToken = default);
Task<User?> FindAsync(string uniqueName, CancellationToken cancellationToken = default);
Task<User?> FindAsync(Guid id, CancellationToken cancellationToken = default);
Task<User?> FindAsync(Guid userId, CancellationToken cancellationToken = default);
Task<User> SaveProfileAsync(Guid userId, SaveProfilePayload payload, CancellationToken cancellationToken = default);
Task<User?> SignOutAsync(Guid userId, CancellationToken cancellationToken = default);
Task<User> UpdateEmailAsync(Guid userId, Email email, CancellationToken cancellationToken = default);
Task<User> UpdatePhoneAsync(Guid userId, Phone phone, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public async Task<Session> SignInAsync(User user, string password, IEnumerable<C
RequestContext context = new(user.Id.ToString(), cancellationToken);
return await _sessionClient.SignInAsync(payload, context);
}

public async Task<Session?> SignOutAsync(Guid id, CancellationToken cancellationToken)
{
RequestContext context = new(cancellationToken);
return await _sessionClient.SignOutAsync(id, context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ public async Task<User> CreateAsync(Email email, CancellationToken cancellationT
return await _userClient.ReadAsync(id: null, uniqueName, identifier: null, context);
}

public async Task<User?> FindAsync(Guid id, CancellationToken cancellationToken)
public async Task<User?> FindAsync(Guid userId, CancellationToken cancellationToken)
{
RequestContext context = new(cancellationToken);
return await _userClient.ReadAsync(id, uniqueName: null, identifier: null, context);
return await _userClient.ReadAsync(userId, uniqueName: null, identifier: null, context);
}

public async Task<User> SaveProfileAsync(Guid userId, SaveProfilePayload profile, CancellationToken cancellationToken)
Expand All @@ -72,6 +72,12 @@ public async Task<User> SaveProfileAsync(Guid userId, SaveProfilePayload profile
return await _userClient.UpdateAsync(userId, payload, context) ?? throw new InvalidOperationException($"The user 'Id={userId}' could not be found.");
}

public async Task<User?> SignOutAsync(Guid userId, CancellationToken cancellationToken)
{
RequestContext context = new(userId.ToString(), cancellationToken);
return await _userClient.SignOutAsync(userId, context);
}

public async Task<User> UpdateEmailAsync(Guid userId, Email email, CancellationToken cancellationToken)
{
UpdateUserPayload payload = new()
Expand Down
22 changes: 22 additions & 0 deletions backend/src/Logitar.Master/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ public async Task<ActionResult<SignInResponse>> SignInAsync([FromBody] SignInPay
return Ok(new SignInResponse(result));
}

[HttpPost("/auth/sign/out")]
[Authorize]
public async Task<ActionResult> SignOutAsync(bool everywhere, CancellationToken cancellationToken)
{
Session? session = HttpContext.GetSession();
User? user = HttpContext.GetUser();
if (everywhere && user != null)
{
SignOutCommand command = new(user);
await _requestPipeline.ExecuteAsync(command, cancellationToken);
}
else if (!everywhere && session != null)
{
SignOutCommand command = new(session);
await _requestPipeline.ExecuteAsync(command, cancellationToken);
}

HttpContext.SignOut();

return NoContent();
}

[HttpPost("/auth/token")]
public async Task<ActionResult<GetTokenResponse>> TokenAsync([FromBody] GetTokenPayload payload, CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Logitar.Portal.Contracts.Sessions;
using Logitar.Portal.Contracts.Users;
using Moq;

namespace Logitar.Master.Application.Accounts.Commands;

[Trait(Traits.Category, Categories.Integration)]
public class SignOutCommandTests : IntegrationTests
{
public SignOutCommandTests() : base()
{
}

[Fact(DisplayName = "It should sign out a session when a session ID is provided.")]
public async Task It_should_sign_out_a_session_when_a_session_Id_is_provided()
{
User user = new(Faker.Person.UserName)
{
Id = ActorId.ToGuid()
};
Session session = new(user)
{
Id = Guid.NewGuid()
};

SignOutCommand command = new(session);
await Pipeline.ExecuteAsync(command, CancellationToken);

SessionService.Verify(x => x.SignOutAsync(session.Id, CancellationToken), Times.Once);
UserService.Verify(x => x.SignOutAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Never);
}

[Fact(DisplayName = "It should sign out an user when an user ID is provided.")]
public async Task It_should_sign_out_an_user_when_an_user_Id_is_provided()
{
User user = new(Faker.Person.UserName)
{
Id = ActorId.ToGuid()
};

SignOutCommand command = new(user);
await Pipeline.ExecuteAsync(command, CancellationToken);

SessionService.Verify(x => x.SignOutAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Never);
UserService.Verify(x => x.SignOutAsync(user.Id, CancellationToken), Times.Once);
}
}

0 comments on commit 3b6a755

Please sign in to comment.