Added CORS, added specific type for token in AuthToken.cs, added login page to blazor client
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
using ApplicationLayer.Services.AuthServices.Common;
|
||||||
|
using Domains.Users;
|
||||||
|
|
||||||
|
namespace ApplicationLayer.InfrastructureServicesInterfaces;
|
||||||
|
|
||||||
|
/// Generates jwt-tokens
|
||||||
|
public interface ITokenGenerator
|
||||||
|
{
|
||||||
|
/// returns jwt-token for specific user
|
||||||
|
AuthToken CreateToken(User user);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
namespace ApplicationLayer.Services.AuthServices.Common
|
||||||
|
{
|
||||||
|
public record AuthToken(string Token);
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
|
using ApplicationLayer.InfrastructureServicesInterfaces;
|
||||||
|
using ApplicationLayer.Services.AuthServices.Common;
|
||||||
|
using ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
|
||||||
using ApplicationLayer.Services.AuthServices.NeededServices;
|
using ApplicationLayer.Services.AuthServices.NeededServices;
|
||||||
using ApplicationLayer.Services.AuthServices.Requests;
|
using ApplicationLayer.Services.AuthServices.Requests;
|
||||||
using Domains.Users;
|
using Domains.Users;
|
||||||
@@ -7,7 +9,7 @@ namespace ApplicationLayer.Services.AuthServices.LoginService;
|
|||||||
|
|
||||||
public class DevelopmentLoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
|
public class DevelopmentLoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
|
||||||
{
|
{
|
||||||
async Task<string> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken)
|
async Task<AuthToken> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (request.AuthData is { Email: "admin@mail.ru", Password: "admin" })
|
if (request.AuthData is { Email: "admin@mail.ru", Password: "admin" })
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ApplicationLayer.Services.AuthServices.Requests;
|
using ApplicationLayer.Services.AuthServices.Common;
|
||||||
|
using ApplicationLayer.Services.AuthServices.Requests;
|
||||||
|
|
||||||
namespace ApplicationLayer.Services.AuthServices.LoginService;
|
namespace ApplicationLayer.Services.AuthServices.LoginService;
|
||||||
|
|
||||||
@@ -7,5 +8,5 @@ public interface ILoginService
|
|||||||
{
|
{
|
||||||
/// Handle login request
|
/// Handle login request
|
||||||
/// <returns>JWT-token</returns>
|
/// <returns>JWT-token</returns>
|
||||||
Task<string> LoginAsync(LoginRequest request, CancellationToken cancellationToken);
|
Task<AuthToken> LoginAsync(LoginRequest request, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
|
using ApplicationLayer.InfrastructureServicesInterfaces;
|
||||||
|
using ApplicationLayer.Services.AuthServices.Common;
|
||||||
|
using ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
|
||||||
using ApplicationLayer.Services.AuthServices.NeededServices;
|
using ApplicationLayer.Services.AuthServices.NeededServices;
|
||||||
using ApplicationLayer.Services.AuthServices.Requests;
|
using ApplicationLayer.Services.AuthServices.Requests;
|
||||||
|
|
||||||
@@ -7,7 +9,7 @@ namespace ApplicationLayer.Services.AuthServices.LoginService;
|
|||||||
/// <inheritdoc cref="ILoginService" />
|
/// <inheritdoc cref="ILoginService" />
|
||||||
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
|
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
|
||||||
{
|
{
|
||||||
async Task<string> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken)
|
async Task<AuthToken> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await users.FindByEmailAsync(request.AuthData.Email, cancellationToken);
|
var user = await users.FindByEmailAsync(request.AuthData.Email, cancellationToken);
|
||||||
if (user is null || user.Password != request.AuthData.Password)
|
if (user is null || user.Password != request.AuthData.Password)
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
using Domains.Users;
|
|
||||||
|
|
||||||
namespace ApplicationLayer.Services.AuthServices.NeededServices;
|
|
||||||
|
|
||||||
/// Generates jwt-tokens
|
|
||||||
public interface ITokenGenerator
|
|
||||||
{
|
|
||||||
/// returns jwt-token for specific user
|
|
||||||
string CreateToken(User user);
|
|
||||||
}
|
|
||||||
@@ -11,4 +11,8 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.1" PrivateAssets="all"/>
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.1" PrivateAssets="all"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\VisaApiClient\VisaApiClient.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
@page "/login"
|
||||||
|
@using VisaApiClient
|
||||||
|
|
||||||
|
<PageTitle>Authentication</PageTitle>
|
||||||
|
|
||||||
|
<EditForm Model="loginData" OnValidSubmit="TryLogin">
|
||||||
|
<DataAnnotationsValidator/>
|
||||||
|
<label >Email: <InputText @bind-Value="loginData.Email"/></label>
|
||||||
|
<label >Password: <InputText @bind-Value="loginData.Password"/></label>
|
||||||
|
<input type="submit" value="Login"/>
|
||||||
|
</EditForm>
|
||||||
|
<p>@loginResult</p>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private AuthData loginData = new();
|
||||||
|
private string loginResult = String.Empty;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private Client Client { get; set; } = null!;
|
||||||
|
|
||||||
|
private async Task TryLogin(EditContext obj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var token = await Client.LoginAsync(loginData.Email, loginData.Password);
|
||||||
|
Client.SetAuthToken(token);
|
||||||
|
loginResult = "Logged in successfully";
|
||||||
|
}
|
||||||
|
catch (ApiException<ProblemDetails> e)
|
||||||
|
{
|
||||||
|
loginResult = e.Result.Detail!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
|
using VisaApiClient;
|
||||||
|
|
||||||
namespace BlazorWebAssemblyVisaApiClient;
|
namespace BlazorWebAssemblyVisaApiClient;
|
||||||
|
|
||||||
@@ -11,7 +12,13 @@ public class Program
|
|||||||
builder.RootComponents.Add<App>("#app");
|
builder.RootComponents.Add<App>("#app");
|
||||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||||
|
|
||||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
//todo move to launch settings
|
||||||
|
const string baseAddress = "https://localhost:44370";
|
||||||
|
|
||||||
|
//todo make pretty
|
||||||
|
builder.Services.AddScoped(_ => new HttpClient { BaseAddress = new Uri(baseAddress) });
|
||||||
|
builder.Services.AddScoped<Client>(sp =>
|
||||||
|
new Client(baseAddress, sp.GetRequiredService<HttpClient>()));
|
||||||
|
|
||||||
await builder.Build().RunAsync();
|
await builder.Build().RunAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using ApplicationLayer.InfrastructureServicesInterfaces;
|
using ApplicationLayer.InfrastructureServicesInterfaces;
|
||||||
|
using ApplicationLayer.Services.AuthServices.Common;
|
||||||
using ApplicationLayer.Services.AuthServices.NeededServices;
|
using ApplicationLayer.Services.AuthServices.NeededServices;
|
||||||
using Domains.Users;
|
using Domains.Users;
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ public class TokenGenerator(TokenGeneratorOptions options, JwtSecurityTokenHandl
|
|||||||
: ITokenGenerator
|
: ITokenGenerator
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="ITokenGenerator.CreateToken"/>
|
/// <inheritdoc cref="ITokenGenerator.CreateToken"/>
|
||||||
public string CreateToken(User user)
|
public AuthToken CreateToken(User user)
|
||||||
{
|
{
|
||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
{
|
{
|
||||||
@@ -29,6 +30,6 @@ public class TokenGenerator(TokenGeneratorOptions options, JwtSecurityTokenHandl
|
|||||||
signingCredentials: options.Credentials,
|
signingCredentials: options.Credentials,
|
||||||
claims: claims);
|
claims: claims);
|
||||||
|
|
||||||
return tokenHandler.WriteToken(token);
|
return new AuthToken(tokenHandler.WriteToken(token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class UsersController(
|
|||||||
|
|
||||||
/// <summary> Returns JWT-token for authentication </summary>
|
/// <summary> Returns JWT-token for authentication </summary>
|
||||||
[HttpGet("login")]
|
[HttpGet("login")]
|
||||||
[ProducesResponseType<string>(StatusCodes.Status200OK)]
|
[ProducesResponseType<AuthToken>(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
public async Task<IActionResult> Login(string email, string password, CancellationToken cancellationToken)
|
public async Task<IActionResult> Login(string email, string password, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ public static class DependencyInjection
|
|||||||
|
|
||||||
services.AddProblemDetails();
|
services.AddProblemDetails();
|
||||||
|
|
||||||
|
services.AddCors(options => options.AddPolicy("policy", builder =>
|
||||||
|
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
|
||||||
|
|
||||||
services.AddControllers(opts => opts.Filters.Add<GlobalExceptionsFilter>())
|
services.AddControllers(opts => opts.Filters.Add<GlobalExceptionsFilter>())
|
||||||
.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
|
.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ public static class PipelineRequest
|
|||||||
|
|
||||||
app.UseStatusCodePages();
|
app.UseStatusCodePages();
|
||||||
|
|
||||||
|
app.UseCors("policy");
|
||||||
|
|
||||||
app.UseAuthentication()
|
app.UseAuthentication()
|
||||||
.UseAuthorization();
|
.UseAuthorization();
|
||||||
|
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ namespace VisaApiClient
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Success</returns>
|
/// <returns>Success</returns>
|
||||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
public virtual System.Threading.Tasks.Task<string> LoginAsync(string? email, string? password)
|
public virtual System.Threading.Tasks.Task<AuthToken> LoginAsync(string? email, string? password)
|
||||||
{
|
{
|
||||||
return LoginAsync(email, password, System.Threading.CancellationToken.None);
|
return LoginAsync(email, password, System.Threading.CancellationToken.None);
|
||||||
}
|
}
|
||||||
@@ -424,7 +424,7 @@ namespace VisaApiClient
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Success</returns>
|
/// <returns>Success</returns>
|
||||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
public virtual async System.Threading.Tasks.Task<string> LoginAsync(string? email, string? password, System.Threading.CancellationToken cancellationToken)
|
public virtual async System.Threading.Tasks.Task<AuthToken> LoginAsync(string? email, string? password, System.Threading.CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var client_ = _httpClient;
|
var client_ = _httpClient;
|
||||||
var disposeClient_ = false;
|
var disposeClient_ = false;
|
||||||
@@ -475,7 +475,7 @@ namespace VisaApiClient
|
|||||||
var status_ = (int)response_.StatusCode;
|
var status_ = (int)response_.StatusCode;
|
||||||
if (status_ == 200)
|
if (status_ == 200)
|
||||||
{
|
{
|
||||||
var objectResponse_ = await ReadObjectResponseAsync<string>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
var objectResponse_ = await ReadObjectResponseAsync<AuthToken>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||||
if (objectResponse_.Object == null)
|
if (objectResponse_.Object == null)
|
||||||
{
|
{
|
||||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||||
@@ -1583,6 +1583,14 @@ namespace VisaApiClient
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||||
|
public partial class AuthToken
|
||||||
|
{
|
||||||
|
[Newtonsoft.Json.JsonProperty("token", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||||
|
public string? Token { get; set; } = default!;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
|
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||||
public enum AuthorityRequestStatuses
|
public enum AuthorityRequestStatuses
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ namespace VisaApiClient
|
|||||||
{
|
{
|
||||||
public class ClientBase
|
public class ClientBase
|
||||||
{
|
{
|
||||||
private const string LoginPath = "users/login";
|
protected AuthToken? AuthToken { get; private set; }
|
||||||
|
|
||||||
protected string? AuthToken { get; private set; }
|
public void SetAuthToken(AuthToken token)
|
||||||
|
|
||||||
protected void SetAuthToken(string token)
|
|
||||||
{
|
{
|
||||||
AuthToken = token;
|
AuthToken = token;
|
||||||
}
|
}
|
||||||
@@ -16,7 +14,9 @@ namespace VisaApiClient
|
|||||||
protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken)
|
protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var msg = new HttpRequestMessage();
|
var msg = new HttpRequestMessage();
|
||||||
msg.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", AuthToken);
|
|
||||||
|
msg.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", AuthToken?.Token);
|
||||||
|
|
||||||
return Task.FromResult(msg);
|
return Task.FromResult(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,15 +25,11 @@ namespace VisaApiClient
|
|||||||
=> await Task.CompletedTask;
|
=> await Task.CompletedTask;
|
||||||
|
|
||||||
protected async Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, StringBuilder urlBuilder, CancellationToken cancellationToken)
|
protected async Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, StringBuilder urlBuilder, CancellationToken cancellationToken)
|
||||||
=> await Task.CompletedTask;
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
protected async Task ProcessResponseAsync(HttpClient client, HttpResponseMessage response, CancellationToken cancellationToken)
|
protected async Task ProcessResponseAsync(HttpClient client, HttpResponseMessage response, CancellationToken cancellationToken)
|
||||||
{
|
=> await Task.CompletedTask;
|
||||||
if (response.RequestMessage!.RequestUri!.AbsolutePath == LoginPath)
|
|
||||||
{
|
|
||||||
var token = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
||||||
SetAuthToken(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user