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.Requests;
|
||||
using Domains.Users;
|
||||
@@ -7,7 +9,7 @@ namespace ApplicationLayer.Services.AuthServices.LoginService;
|
||||
|
||||
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" })
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ApplicationLayer.Services.AuthServices.Requests;
|
||||
using ApplicationLayer.Services.AuthServices.Common;
|
||||
using ApplicationLayer.Services.AuthServices.Requests;
|
||||
|
||||
namespace ApplicationLayer.Services.AuthServices.LoginService;
|
||||
|
||||
@@ -7,5 +8,5 @@ public interface ILoginService
|
||||
{
|
||||
/// Handle login request
|
||||
/// <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.Requests;
|
||||
|
||||
@@ -7,7 +9,7 @@ namespace ApplicationLayer.Services.AuthServices.LoginService;
|
||||
/// <inheritdoc cref="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);
|
||||
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"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\VisaApiClient\VisaApiClient.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</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.WebAssembly.Hosting;
|
||||
using VisaApiClient;
|
||||
|
||||
namespace BlazorWebAssemblyVisaApiClient;
|
||||
|
||||
@@ -11,7 +12,13 @@ public class Program
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using ApplicationLayer.InfrastructureServicesInterfaces;
|
||||
using ApplicationLayer.Services.AuthServices.Common;
|
||||
using ApplicationLayer.Services.AuthServices.NeededServices;
|
||||
using Domains.Users;
|
||||
|
||||
@@ -14,7 +15,7 @@ public class TokenGenerator(TokenGeneratorOptions options, JwtSecurityTokenHandl
|
||||
: ITokenGenerator
|
||||
{
|
||||
/// <inheritdoc cref="ITokenGenerator.CreateToken"/>
|
||||
public string CreateToken(User user)
|
||||
public AuthToken CreateToken(User user)
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
@@ -29,6 +30,6 @@ public class TokenGenerator(TokenGeneratorOptions options, JwtSecurityTokenHandl
|
||||
signingCredentials: options.Credentials,
|
||||
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>
|
||||
[HttpGet("login")]
|
||||
[ProducesResponseType<string>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<AuthToken>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<IActionResult> Login(string email, string password, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -43,6 +43,9 @@ public static class DependencyInjection
|
||||
|
||||
services.AddProblemDetails();
|
||||
|
||||
services.AddCors(options => options.AddPolicy("policy", builder =>
|
||||
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
|
||||
|
||||
services.AddControllers(opts => opts.Filters.Add<GlobalExceptionsFilter>())
|
||||
.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ public static class PipelineRequest
|
||||
|
||||
app.UseStatusCodePages();
|
||||
|
||||
app.UseCors("policy");
|
||||
|
||||
app.UseAuthentication()
|
||||
.UseAuthorization();
|
||||
|
||||
|
||||
@@ -413,7 +413,7 @@ namespace VisaApiClient
|
||||
/// </summary>
|
||||
/// <returns>Success</returns>
|
||||
/// <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);
|
||||
}
|
||||
@@ -424,7 +424,7 @@ namespace VisaApiClient
|
||||
/// </summary>
|
||||
/// <returns>Success</returns>
|
||||
/// <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 disposeClient_ = false;
|
||||
@@ -475,7 +475,7 @@ namespace VisaApiClient
|
||||
var status_ = (int)response_.StatusCode;
|
||||
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)
|
||||
{
|
||||
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))")]
|
||||
public enum AuthorityRequestStatuses
|
||||
{
|
||||
|
||||
@@ -4,11 +4,9 @@ namespace VisaApiClient
|
||||
{
|
||||
public class ClientBase
|
||||
{
|
||||
private const string LoginPath = "users/login";
|
||||
protected AuthToken? AuthToken { get; private set; }
|
||||
|
||||
protected string? AuthToken { get; private set; }
|
||||
|
||||
protected void SetAuthToken(string token)
|
||||
public void SetAuthToken(AuthToken token)
|
||||
{
|
||||
AuthToken = token;
|
||||
}
|
||||
@@ -16,7 +14,9 @@ namespace VisaApiClient
|
||||
protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -25,15 +25,11 @@ namespace VisaApiClient
|
||||
=> await Task.CompletedTask;
|
||||
|
||||
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)
|
||||
{
|
||||
if (response.RequestMessage!.RequestUri!.AbsolutePath == LoginPath)
|
||||
{
|
||||
var token = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
SetAuthToken(token);
|
||||
}
|
||||
}
|
||||
=> await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user