Skip to content

Commit

Permalink
FEAT: Adding new endpoint to return token
Browse files Browse the repository at this point in the history
  • Loading branch information
fmattioli committed Jul 9, 2024
1 parent 7c9cb88 commit e10e422
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ public class UserController(IMediator mediator) : Controller
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
//[Authorize(Policy = "TokenManager")]
//[RequiredScope("tokenmanager-read")]
public async Task<IActionResult> CreateUser([FromRoute] string tenant, [FromBody] AddUserRequest addUserRequest, CancellationToken cancellationToken)
{
addUserRequest.Tenant = tenant;
var result = await _mediator.Send(new CreateUserCommand(addUserRequest), cancellationToken);
var result = await _mediator.Send(new CreateUserCommand(tenant, addUserRequest), cancellationToken);

var response = new ResponseResult<string>();
if (result.IsSuccess)
Expand All @@ -46,22 +47,20 @@ public async Task<IActionResult> CreateUser([FromRoute] string tenant, [FromBody
/// </summary>
/// <returns>A status code related to the operation.</returns>
[HttpPost]
[Route("login", Name = nameof(Login))]
[Route("login/{tenant}", Name = nameof(Login))]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Authorize(Policy = "TokenManager")]
[RequiredScope("tokenmanager-read")]
public async Task<IActionResult> Login([FromBody] LoginUserRequest loginUserRequest, CancellationToken cancellationToken)
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Login([FromRoute] string tenant, [FromBody] LoginUserRequest loginUserRequest, CancellationToken cancellationToken)
{
var result = await _mediator.Send(new LoginUserCommand(loginUserRequest), cancellationToken);
var result = await _mediator.Send(new LoginUserCommand(tenant, loginUserRequest), cancellationToken);

var response = new ResponseResult<TokenResponse>();
if (result.IsSuccess)
{
response.Result = result.Value;
response.DetailMessage = "Token generated with succesfully";
return Created();
return Ok(response);
}

response.DetailMessage = result.Error.Description;
Expand Down
17 changes: 9 additions & 8 deletions src/Feijuca.Keycloak.TokenManager/TokenManager.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@

var app = builder.Build();

app.UseExceptionHandler()
.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "TokenManager.Api");
c.OAuthClientId(applicationSettings!.AuthSettings!.Resource);
c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
});
app.UseCors("AllowAllOrigins")
.UseExceptionHandler()
.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "TokenManager.Api");
c.OAuthClientId(applicationSettings!.AuthSettings!.Resource);
c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
});

app.UseHttpsRedirection()
.UseAuthorization();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

namespace TokenManager.Application.Services.Commands.Users
{
public record CreateUserCommand(AddUserRequest AddUserRequest) : IRequest<Result>;
public record CreateUserCommand(string Tenant, AddUserRequest AddUserRequest) : IRequest<Result>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class CreateUserCommandHandler(IUserRepository userRepository) : IRequest

public async Task<Result> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var resultUserCreated = await _userRepository.CreateNewUserActions(request. AddUserRequest.Tenant!, request.AddUserRequest.ToDomain());
var resultUserCreated = await _userRepository.CreateNewUserActions(request.Tenant, request.AddUserRequest.ToDomain(request.Tenant));
if (resultUserCreated.IsSuccess)
{
return Result.Success();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

namespace TokenManager.Application.Services.Commands.Users
{
public record LoginUserCommand(LoginUserRequest LoginUser) : IRequest<Result<TokenResponse>>;
public record LoginUserCommand(string Tenant, LoginUserRequest LoginUser) : IRequest<Result<TokenResponse>>;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Contracts.Common;
using MediatR;

using TokenManager.Application.Services.Mappers;
using TokenManager.Application.Services.Responses;
using TokenManager.Domain.Interfaces;
Expand All @@ -13,7 +12,8 @@ public class LoginUserCommandHandler(IUserRepository userRepository) : IRequestH

public async Task<Result<TokenResponse>> Handle(LoginUserCommand request, CancellationToken cancellationToken)
{
var tokenDetailsResult = await _userRepository.LoginAsync(request.LoginUser.ToDomain());
var user = request.LoginUser.ToDomain(request.Tenant);
var tokenDetailsResult = await _userRepository.LoginAsync(request.Tenant, user);
if (tokenDetailsResult.IsSuccess)
{
return Result<TokenResponse>.Success(tokenDetailsResult.Value.ToTokenResponse());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ namespace TokenManager.Application.Services.Mappers
{
public static class UserMapper
{
public static User ToDomain(this AddUserRequest userRequest)
public static User ToDomain(this AddUserRequest userRequest, string tenant)
{
var attributes = userRequest.Attributes!.ToDomain(userRequest.Tenant!);
var attributes = userRequest.Attributes!.ToDomain(tenant);
return new User(userRequest.Username!, userRequest.Password, userRequest.Email!, userRequest.FirstName!, userRequest.LastName!, attributes);
}

Expand All @@ -17,7 +17,7 @@ public static Attributes ToDomain(this AttributesRequest attributes, string tena
return new Attributes(attributes.ZoneInfo, attributes.Birthdate, attributes.PhoneNumber, attributes.Gender, attributes.Fullname, tenant, attributes.Picture);
}

public static User ToDomain(this LoginUserRequest loginUserRequest)
public static User ToDomain(this LoginUserRequest loginUserRequest, string tenant)
{
return new User(loginUserRequest.Username, loginUserRequest.Password);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,5 @@

namespace TokenManager.Application.Services.Requests.User
{
public record AddUserRequest(string Username, string Password, string Email, string FirstName, string LastName, AttributesRequest? Attributes)
{
[JsonIgnore]
public string? Tenant { get; set; }
}
public record AddUserRequest(string Username, string Password, string Email, string FirstName, string LastName, AttributesRequest? Attributes);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace TokenManager.Application.Services.Requests.User
using Newtonsoft.Json;

namespace TokenManager.Application.Services.Requests.User
{
public record LoginUserRequest(string Username, string Password);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ public interface IUserRepository
{
Task<Result<TokenDetails>> GetAccessTokenAsync(string tenant);
Task<Result> CreateNewUserActions(string tenant, User user);
Task<Result<TokenDetails>> LoginAsync(string tenant, User user);
Task<HttpResponseMessage> CreateNewUserAsync(User user);
Task<Result<User>> GetUserAsync(string userName);
Task<Result> ResetPasswordAsync(string userId, string password);
Task<Result> SendEmailVerificationAsync(string userId);
Task<Result<TokenDetails>> LoginAsync(User user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,20 @@ public class UserRepository : IUserRepository
private readonly IAuthService _authService;
private readonly TokenCredentials _tokenCredentials;
private readonly HttpClient _httpClient;

private string urlUserActions = "";
private string _urlUserActions = "";
private static readonly JsonSerializerSettings Settings = new()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore
};

public UserRepository(IHttpClientFactory httpClientFactory, IAuthService authService, TokenCredentials tokenCredentials)
{
_httpClientFactory = httpClientFactory;
_authService = authService;
_tokenCredentials = tokenCredentials;
_httpClient = _httpClientFactory.CreateClient("KeycloakClient");
}

private static readonly JsonSerializerSettings Settings = new()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore
};
}

public async Task<Result<TokenDetails>> GetAccessTokenAsync(string tenant)
{
Expand Down Expand Up @@ -86,29 +84,19 @@ public async Task<Result> CreateNewUserActions(string tenant, User user)
}

return Result.Failure(UserErrors.InvalidUserNameOrPasswordError);
}

private void ConfigureHttpClient(string tenant, string accessToken)
{
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
urlUserActions = _httpClient.BaseAddress
.AppendPathSegment("admin")
.AppendPathSegment("realms")
.AppendPathSegment(tenant)
.AppendPathSegment("users");
}
}

public async Task<HttpResponseMessage> CreateNewUserAsync(User user)
{
var json = JsonConvert.SerializeObject(user, Settings);
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(urlUserActions, httpContent);
var response = await _httpClient.PostAsync(_urlUserActions, httpContent);
return response;
}

public async Task<Result<User>> GetUserAsync(string userName)
{
var url = urlUserActions.SetQueryParam("username", userName);
var url = _urlUserActions.SetQueryParam("username", userName);
var response = await _httpClient.GetAsync(url);
var keycloakUserContent = await response.Content.ReadAsStringAsync();

Expand All @@ -118,7 +106,7 @@ public async Task<Result<User>> GetUserAsync(string userName)

public async Task<Result> ResetPasswordAsync(string userId, string password)
{
var url = urlUserActions
var url = _urlUserActions
.AppendPathSegment(userId)
.AppendPathSegment("reset-password");

Expand All @@ -145,7 +133,7 @@ public async Task<Result> ResetPasswordAsync(string userId, string password)

public async Task<Result> SendEmailVerificationAsync(string userId)
{
var url = urlUserActions
var url = _urlUserActions
.AppendPathSegment(userId);

var requestData = new
Expand All @@ -169,8 +157,14 @@ public async Task<Result> SendEmailVerificationAsync(string userId)
return Result.Failure(UserErrors.InvalidUserNameOrPasswordError);
}

public async Task<Result<TokenDetails>> LoginAsync(User user)
public async Task<Result<TokenDetails>> LoginAsync(string tenant, User user)
{
var urlGetToken = _httpClient.BaseAddress.AppendPathSegment("realms")
.AppendPathSegment(tenant)
.AppendPathSegment("protocol")
.AppendPathSegment("openid-connect")
.AppendPathSegment("token");

var requestData = new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("grant_type", "password"),
Expand All @@ -181,16 +175,7 @@ public async Task<Result<TokenDetails>> LoginAsync(User user)
new KeyValuePair<string, string>("scope", "tokenmanager-write tokenmanager-read"),
]);

var tenant = _authService.GetTenantFromToken();

var url = _httpClient.BaseAddress
.AppendPathSegment("realms")
.AppendPathSegment(tenant)
.AppendPathSegment("protocol")
.AppendPathSegment("openid-connect")
.AppendPathSegment("token");

var response = await _httpClient.PostAsync(url, requestData);
var response = await _httpClient.PostAsync(urlGetToken, requestData);

if (response.IsSuccessStatusCode)
{
Expand All @@ -203,5 +188,16 @@ public async Task<Result<TokenDetails>> LoginAsync(User user)
UserErrors.SetTechnicalMessage(responseMessage);
return Result<TokenDetails>.Failure(UserErrors.InvalidUserNameOrPasswordError);
}

private void ConfigureHttpClient(string tenant, string accessToken)
{
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

_urlUserActions = _httpClient.BaseAddress
.AppendPathSegment("admin")
.AppendPathSegment("realms")
.AppendPathSegment(tenant)
.AppendPathSegment("users");
}
}
}

0 comments on commit e10e422

Please sign in to comment.