mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-22 16:43:24 -04:00
184 lines
6.4 KiB
C#
184 lines
6.4 KiB
C#
//-----------------------------------------------------------------------
|
|
// <copyright file="LoginBase.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.Client.Auth.Pages.Base;
|
|
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using AliasVault.Client.Services.Auth;
|
|
using AliasVault.Shared.Models.WebApi;
|
|
using AliasVault.Shared.Models.WebApi.Auth;
|
|
using Blazored.LocalStorage;
|
|
using Cryptography;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.JSInterop;
|
|
|
|
/// <summary>
|
|
/// Base authorize page that all pages that are part of the logged in website should inherit from.
|
|
/// All pages that inherit from this class will require the user to be logged in and have a confirmed email.
|
|
/// Also, a default set of breadcrumbs is added in the parent OnInitialized method.
|
|
/// </summary>
|
|
public class LoginBase : OwningComponentBase
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the NavigationManager.
|
|
/// </summary>
|
|
[Inject]
|
|
public NavigationManager NavigationManager { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the HttpClient.
|
|
/// </summary>
|
|
[Inject]
|
|
public HttpClient Http { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the AuthenticationStateProvider.
|
|
/// </summary>
|
|
[Inject]
|
|
public AuthenticationStateProvider AuthStateProvider { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the GlobalNotificationService.
|
|
/// </summary>
|
|
[Inject]
|
|
public GlobalNotificationService GlobalNotificationService { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the IJSRuntime.
|
|
/// </summary>
|
|
[Inject]
|
|
public IJSRuntime Js { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the DbService.
|
|
/// </summary>
|
|
[Inject]
|
|
public DbService DbService { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the AuthService.
|
|
/// </summary>
|
|
[Inject]
|
|
public AuthService AuthService { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the LocalStorage.
|
|
/// </summary>
|
|
[Inject]
|
|
public ILocalStorageService LocalStorage { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Parses the response content and displays the server validation errors.
|
|
/// </summary>
|
|
/// <param name="responseContent">Response content.</param>
|
|
/// <returns>List of errors if something went wrong.</returns>
|
|
public static List<string> ParseResponse(string responseContent)
|
|
{
|
|
var returnErrors = new List<string>();
|
|
|
|
var errorResponse = System.Text.Json.JsonSerializer.Deserialize<ServerValidationErrorResponse>(responseContent);
|
|
if (errorResponse is not null)
|
|
{
|
|
foreach (var error in errorResponse.Errors)
|
|
{
|
|
returnErrors.AddRange(error.Value);
|
|
}
|
|
}
|
|
|
|
return returnErrors;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the username from the authentication state asynchronously.
|
|
/// </summary>
|
|
/// <param name="username">Username.</param>
|
|
/// <param name="password">Password.</param>
|
|
/// <returns>List of errors if something went wrong.</returns>
|
|
protected async Task<List<string>> ProcessLoginAsync(string username, string password)
|
|
{
|
|
// Sanitize username
|
|
username = username.ToLowerInvariant().Trim();
|
|
|
|
// Send request to server with email to get server ephemeral public key.
|
|
var result = await Http.PostAsJsonAsync("api/v1/Auth/login", new LoginRequest(username));
|
|
var responseContent = await result.Content.ReadAsStringAsync();
|
|
|
|
if (!result.IsSuccessStatusCode)
|
|
{
|
|
return ParseResponse(responseContent);
|
|
}
|
|
|
|
var loginResponse = JsonSerializer.Deserialize<LoginResponse>(responseContent);
|
|
if (loginResponse == null)
|
|
{
|
|
return
|
|
[
|
|
"An error occurred while processing the login request.",
|
|
];
|
|
}
|
|
|
|
// 3. Client derives shared session key.
|
|
byte[] passwordHash = await Encryption.DeriveKeyFromPasswordAsync(password, loginResponse.Salt);
|
|
var passwordHashString = BitConverter.ToString(passwordHash).Replace("-", string.Empty);
|
|
|
|
var clientEphemeral = Srp.GenerateEphemeralClient();
|
|
var privateKey = Srp.DerivePrivateKey(loginResponse.Salt, username, passwordHashString);
|
|
var clientSession = Srp.DeriveSessionClient(
|
|
privateKey,
|
|
clientEphemeral.Secret,
|
|
loginResponse.ServerEphemeral,
|
|
loginResponse.Salt,
|
|
username);
|
|
|
|
// 4. Client sends proof of session key to server.
|
|
result = await Http.PostAsJsonAsync("api/v1/Auth/validate", new ValidateLoginRequest(username, clientEphemeral.Public, clientSession.Proof));
|
|
responseContent = await result.Content.ReadAsStringAsync();
|
|
|
|
if (!result.IsSuccessStatusCode)
|
|
{
|
|
return ParseResponse(responseContent);
|
|
}
|
|
|
|
var validateLoginResponse = JsonSerializer.Deserialize<ValidateLoginResponse>(responseContent);
|
|
if (validateLoginResponse == null)
|
|
{
|
|
return
|
|
[
|
|
"An error occurred while processing the login request.",
|
|
];
|
|
}
|
|
|
|
// 5. Client verifies proof.
|
|
Srp.VerifySession(clientEphemeral.Public, clientSession, validateLoginResponse.ServerSessionProof);
|
|
|
|
// Store the tokens in local storage.
|
|
await AuthService.StoreAccessTokenAsync(validateLoginResponse.Token.Token);
|
|
await AuthService.StoreRefreshTokenAsync(validateLoginResponse.Token.RefreshToken);
|
|
|
|
// Store the encryption key in memory.
|
|
AuthService.StoreEncryptionKey(passwordHash);
|
|
|
|
await AuthStateProvider.GetAuthenticationStateAsync();
|
|
GlobalNotificationService.ClearMessages();
|
|
|
|
// Redirect to the page the user was trying to access before if set.
|
|
var localStorageReturnUrl = await LocalStorage.GetItemAsync<string>("returnUrl");
|
|
if (!string.IsNullOrEmpty(localStorageReturnUrl))
|
|
{
|
|
NavigationManager.NavigateTo(localStorageReturnUrl);
|
|
}
|
|
else
|
|
{
|
|
NavigationManager.NavigateTo("/");
|
|
}
|
|
|
|
return [];
|
|
}
|
|
}
|