Skip to content

Commit

Permalink
Code style refactor (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
lanedirt committed Jun 13, 2024
1 parent d456932 commit 6d03719
Show file tree
Hide file tree
Showing 24 changed files with 399 additions and 191 deletions.
20 changes: 9 additions & 11 deletions src/AliasVault.Api/Controllers/AliasController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ namespace AliasVault.Api.Controllers;
/// <param name="userManager">UserManager instance.</param>
public class AliasController(AliasDbContext context, UserManager<IdentityUser> userManager) : AuthenticatedRequestController(userManager)
{
private readonly AliasDbContext _context = context;

/// <summary>
/// Get all alias items for the current user.
/// </summary>
Expand All @@ -39,7 +37,7 @@ public async Task<IActionResult> GetItems()
}

// Logic to retrieve items for the user.
var aliases = await _context.Logins
var aliases = await context.Logins
.Include(x => x.Identity)
.Include(x => x.Service)
.Where(x => x.UserId == user.Id)
Expand Down Expand Up @@ -70,7 +68,7 @@ public async Task<IActionResult> GetAlias(Guid aliasId)
return Unauthorized();
}

var aliasObject = await _context.Logins
var aliasObject = await context.Logins
.Include(x => x.Passwords)
.Include(x => x.Identity)
.Include(x => x.Service)
Expand Down Expand Up @@ -175,8 +173,8 @@ public async Task<IActionResult> Insert([FromBody] Alias model)
UpdatedAt = DateTime.UtcNow,
};

await _context.Logins.AddAsync(login);
await _context.SaveChangesAsync();
await context.Logins.AddAsync(login);
await context.SaveChangesAsync();

return Ok(login.Id);
}
Expand All @@ -197,7 +195,7 @@ public async Task<IActionResult> Update(Guid aliasId, [FromBody] Alias model)
}

// Get the existing entry.
var login = await _context.Logins
var login = await context.Logins
.Include(x => x.Identity)
.Include(x => x.Service)
.Include(x => x.Passwords)
Expand Down Expand Up @@ -228,7 +226,7 @@ public async Task<IActionResult> Update(Guid aliasId, [FromBody] Alias model)
login.Service.Url = model.Service.Url;
login.Service.UpdatedAt = DateTime.UtcNow;

await _context.SaveChangesAsync();
await context.SaveChangesAsync();

return Ok(login.Id);
}
Expand All @@ -247,13 +245,13 @@ public async Task<IActionResult> Delete(Guid aliasId)
return Unauthorized();
}

var login = await _context.Logins
var login = await context.Logins
.Where(x => x.Id == aliasId)
.Where(x => x.UserId == user.Id)
.FirstAsync();

_context.Logins.Remove(login);
await _context.SaveChangesAsync();
context.Logins.Remove(login);
await context.SaveChangesAsync();

return Ok();
}
Expand Down
14 changes: 10 additions & 4 deletions src/AliasVault.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,13 @@

await app.RunAsync();

/// <summary>
/// For starting the WebAPI project in-memory from E2ETests project.
/// </summary>
public class AliasVaultApiProgram { }
namespace AliasVault.Api
{
/// <summary>
/// Explicit program class definition. This is required in order to start the WebAPI project
/// in-memory from E2ETests project via WebApplicationFactory.
/// </summary>
public partial class Program
{
}
}
9 changes: 9 additions & 0 deletions src/AliasVault.WebApp/AliasVault.WebApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
<DocumentationFile>bin\Release\net8.0\AliasVault.WebApp.xml</DocumentationFile>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<Optimize>True</Optimize>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.6" />
Expand Down
10 changes: 5 additions & 5 deletions src/AliasVault.WebApp/Auth/Components/InputTextField.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" />

@code {
[Parameter] public string Id { get; set; }
[Parameter] public string Value { get; set; }
[Parameter] public string Id { get; set; } = null!;
[Parameter] public string Value { get; set; } = null!;
[Parameter] public EventCallback<string> ValueChanged { get; set; }
[Parameter] public Expression<Func<string>> ValueExpression { get; set; }
[Parameter] public string Placeholder { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object> AdditionalAttributes { get; set; }
[Parameter] public Expression<Func<string>> ValueExpression { get; set; } = null!;
[Parameter] public string Placeholder { get; set; } = null!;
[Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object> AdditionalAttributes { get; set; } = new();
}
2 changes: 1 addition & 1 deletion src/AliasVault.WebApp/Auth/Pages/Login.razor
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

@code {
LoginModel user = new LoginModel();
FullScreenLoadingIndicator loadingIndicator;
FullScreenLoadingIndicator loadingIndicator = new();

protected override async Task OnInitializedAsync()
{
Expand Down
10 changes: 5 additions & 5 deletions src/AliasVault.WebApp/Auth/Pages/Register.razor
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

@code {
RegisterModel user = new RegisterModel();
FullScreenLoadingIndicator loadingIndicator;
FullScreenLoadingIndicator loadingIndicator = new();
List<string> validationErrors = new List<string>();

async Task HandleRegister()
Expand Down Expand Up @@ -114,10 +114,10 @@

public class ValidationErrorResponse
{
public string Type { get; set; }
public string Title { get; set; }
public string Type { get; set; } = null!;
public string Title { get; set; } = null!;
public int Status { get; set; }
public IDictionary<string, string[]> Errors { get; set; }
public string TraceId { get; set; }
public Dictionary<string, string[]> Errors { get; set; } = new();
public string TraceId { get; set; } = null!;
}
}
40 changes: 24 additions & 16 deletions src/AliasVault.WebApp/Auth/Services/AliasVaultApiHandlerService.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
using Microsoft.AspNetCore.Components;
//-----------------------------------------------------------------------
// <copyright file="AliasVaultApiHandlerService.cs" company="lanedirt">
// Copyright (c) lanedirt. All rights reserved.
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
// </copyright>
//-----------------------------------------------------------------------

namespace AliasVault.WebApp.Auth.Services;

using System.Net;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Components;

public class AliasVaultApiHandlerService : DelegatingHandler
/// <summary>
/// This services handles all API requests to the AliasVault API and will add the access token to the request headers.
/// If a 401 unauthorized is returned by the API it will intercept this response and attempt to automatically refresh the access token.
/// </summary>
public class AliasVaultApiHandlerService(IServiceProvider serviceProvider) : DelegatingHandler
{
private readonly IServiceProvider _serviceProvider;

public AliasVaultApiHandlerService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

/// <summary>
/// Override the SendAsync method to add the access token to the request headers.
/// </summary>
/// <param name="request">HttpRequestMessage instance.</param>
/// <param name="cancellationToken">CancellationToken instance.</param>
/// <returns>HttpResponseMessage.</returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Check if the request already contains the refreshed token to prevent infinite loop
Expand All @@ -25,7 +34,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
}

// Set the access token in the Authorization header
var authService = _serviceProvider.GetRequiredService<AuthService>();
var authService = serviceProvider.GetRequiredService<AuthService>();
var token = await authService.GetAccessTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

Expand All @@ -39,21 +48,20 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
{
// Retry the original request with the new access token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", newToken);
// Add a custom header to indicate that this is a retry attempt

// Add a custom header to indicate that the next request is a retry attempt and any failure should be ignored.
request.Headers.Add("X-Ignore-Failure", "true");
response = await base.SendAsync(request, cancellationToken);
return response;
}
else
{
// Refreshing token failed. This might be caused by the refresh token itself expired or has been revoked.
// Remove token from localstorage and redirect to login.
// Refreshing token failed. This might be caused by the expiration or revocation of the refresh token itself.
// Remove the token from local storage and redirect to the login page.
await authService.RemoveTokensAsync();

// Redirect to the login page.
var navigationManager = _serviceProvider.GetRequiredService<NavigationManager>();
var navigationManager = serviceProvider.GetRequiredService<NavigationManager>();
navigationManager.NavigateTo("/user/login");

}
}

Expand Down
70 changes: 48 additions & 22 deletions src/AliasVault.WebApp/Auth/Services/AuthService.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@

using System.Net.Http.Headers;
using Microsoft.AspNetCore.Components.Authorization;
//-----------------------------------------------------------------------
// <copyright file="AuthService.cs" company="lanedirt">
// Copyright (c) lanedirt. All rights reserved.
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
// </copyright>
//-----------------------------------------------------------------------

namespace AliasVault.WebApp.Auth.Services;

using AliasVault.Shared.Models;
using Blazored.LocalStorage;
using System.Net.Http.Json;
using System.Text.Json;
using AliasVault.Shared.Models;
using Blazored.LocalStorage;

/// <summary>
/// This service is responsible for handling authentication-related operations such as refreshing tokens,
/// storing tokens, and revoking tokens.
/// </summary>
public class AuthService
{
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorage;
private const string AccessTokenKey = "token";
private const string RefreshTokenKey = "refreshToken";
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorage;

/// <summary>
/// Initializes a new instance of the <see cref="AuthService"/> class.
/// </summary>
/// <param name="httpClient">The HTTP client.</param>
/// <param name="localStorage">The local storage service.</param>
public AuthService(HttpClient httpClient, ILocalStorageService localStorage)
{
_httpClient = httpClient;
_localStorage = localStorage;
}

/// <summary>
/// Refreshes the access token asynchronously.
/// </summary>
/// <returns>The new access token.</returns>
public async Task<string?> RefreshTokenAsync()
{
// Your logic to get the refresh token and request a new access token
Expand All @@ -30,8 +46,9 @@ public AuthService(HttpClient httpClient, ILocalStorageService localStorage)
var tokenInput = new TokenModel { Token = accessToken, RefreshToken = refreshToken };
using var request = new HttpRequestMessage(HttpMethod.Post, "api/Auth/refresh")
{
Content = JsonContent.Create(tokenInput)
Content = JsonContent.Create(tokenInput),
};

// Add the X-Ignore-Failure header to the request so any failure does not trigger another refresh token request.
request.Headers.Add("X-Ignore-Failure", "true");
var response = await _httpClient.SendAsync(request);
Expand All @@ -55,44 +72,47 @@ public AuthService(HttpClient httpClient, ILocalStorageService localStorage)
}

/// <summary>
/// Retrieve the stored refresh token (e.g., from local storage or a secure place).
/// Retrieves the stored access token asynchronously.
/// </summary>
/// <returns></returns>
/// <returns>The stored access token.</returns>
public async Task<string> GetAccessTokenAsync()
{
return await _localStorage.GetItemAsStringAsync(AccessTokenKey);
return await _localStorage.GetItemAsStringAsync(AccessTokenKey) ?? string.Empty;
}

/// <summary>
/// Store the new access token (e.g., in local storage)
/// Stores the new access token asynchronously.
/// </summary>
/// <param name="newToken"></param>
/// <param name="newToken">The new access token.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task StoreAccessTokenAsync(string newToken)
{
await _localStorage.SetItemAsStringAsync(AccessTokenKey, newToken);
}

/// <summary>
/// Retrieve the stored refresh token (e.g., from local storage or a secure place).
/// Retrieves the stored refresh token asynchronously.
/// </summary>
/// <returns></returns>
/// <returns>The stored refresh token.</returns>
public async Task<string> GetRefreshTokenAsync()
{
return await _localStorage.GetItemAsStringAsync(RefreshTokenKey);
return await _localStorage.GetItemAsStringAsync(RefreshTokenKey) ?? string.Empty;
}

/// <summary>
/// Store the new access token (e.g., in local storage).
/// Stores the new refresh token asynchronously.
/// </summary>
/// <param name="newToken"></param>
/// <param name="newToken">The new refresh token.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task StoreRefreshTokenAsync(string newToken)
{
await _localStorage.SetItemAsStringAsync(RefreshTokenKey, newToken);
}

/// <summary>
/// Remove the stored access and refresh tokens, called when logging out.
/// Removes the stored access and refresh tokens asynchronously, called when logging out.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task RemoveTokensAsync()
{
await _localStorage.RemoveItemAsync(AccessTokenKey);
Expand All @@ -111,15 +131,21 @@ public async Task RemoveTokensAsync()
}

/// <summary>
/// Revoke the access and refresh tokens on the server.
/// Revokes the access and refresh tokens on the server asynchronously.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
private async Task RevokeTokenAsync()
{
var tokenInput = new TokenModel { Token = await GetAccessTokenAsync(), RefreshToken = await GetRefreshTokenAsync() };
var tokenInput = new TokenModel {
Token = await GetAccessTokenAsync(),
RefreshToken = await GetRefreshTokenAsync(),
};

using var request = new HttpRequestMessage(HttpMethod.Post, "api/Auth/revoke")
{
Content = JsonContent.Create(tokenInput)
Content = JsonContent.Create(tokenInput),
};

// Add the X-Ignore-Failure header to the request so any failure does not trigger another refresh token request.
request.Headers.Add("X-Ignore-Failure", "true");
await _httpClient.SendAsync(request);
Expand Down
8 changes: 0 additions & 8 deletions src/AliasVault.WebApp/Components/Alias/Alias.razor
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,10 @@
@code {
[Parameter]
public AliasVault.Shared.Models.WebApi.AliasListEntry Obj { get; set; } = new();
private bool showModal = false;

private void ShowDetails()
{
// Redirect to view page instead for now.
NavigationManager.NavigateTo($"/alias/{Obj.Id}");
//showModal = true;
}

private void CloseDetails()
{
showModal = false;
}

}
Loading

0 comments on commit 6d03719

Please sign in to comment.