From 802e42563e6821be3e2124e5c393dcffb287ce23 Mon Sep 17 00:00:00 2001 From: prtsie Date: Fri, 30 Aug 2024 15:10:25 +0300 Subject: [PATCH] Added CORS, added specific type for token in AuthToken.cs, added login page to blazor client --- .../ITokenGenerator.cs | 11 ++++++ .../Services/AuthServices/Common/AuthToken.cs | 4 +++ .../LoginService/DevelopmentLoginService.cs | 6 ++-- .../LoginService/ILoginService.cs | 5 +-- .../AuthServices/LoginService/LoginService.cs | 6 ++-- .../NeededServices/ITokenGenerator.cs | 10 ------ .../BlazorWebAssemblyVisaApiClient.csproj | 4 +++ .../Pages/Auth.razor | 35 +++++++++++++++++++ .../BlazorWebAssemblyVisaApiClient/Program.cs | 9 ++++- .../Infrastructure/Auth/TokenGenerator.cs | 5 +-- .../Controllers/UsersController.cs | 2 +- .../SchengenVisaApi/DependencyInjection.cs | 3 ++ .../SchengenVisaApi/RequestPipeline.cs | 2 ++ SchengenVisaApi/VisaApiClient/Client.cs | 14 ++++++-- SchengenVisaApi/VisaApiClient/ClientBase.cs | 22 +++++------- 15 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 SchengenVisaApi/ApplicationLayer/InfrastructureServicesInterfaces/ITokenGenerator.cs create mode 100644 SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs delete mode 100644 SchengenVisaApi/ApplicationLayer/Services/AuthServices/NeededServices/ITokenGenerator.cs create mode 100644 SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Pages/Auth.razor diff --git a/SchengenVisaApi/ApplicationLayer/InfrastructureServicesInterfaces/ITokenGenerator.cs b/SchengenVisaApi/ApplicationLayer/InfrastructureServicesInterfaces/ITokenGenerator.cs new file mode 100644 index 0000000..4c5f2e9 --- /dev/null +++ b/SchengenVisaApi/ApplicationLayer/InfrastructureServicesInterfaces/ITokenGenerator.cs @@ -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); +} diff --git a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs new file mode 100644 index 0000000..14ed58f --- /dev/null +++ b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/Common/AuthToken.cs @@ -0,0 +1,4 @@ +namespace ApplicationLayer.Services.AuthServices.Common +{ + public record AuthToken(string Token); +} diff --git a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/DevelopmentLoginService.cs b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/DevelopmentLoginService.cs index 6abead2..ba08317 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/DevelopmentLoginService.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/DevelopmentLoginService.cs @@ -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 ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) + async Task ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) { if (request.AuthData is { Email: "admin@mail.ru", Password: "admin" }) { diff --git a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/ILoginService.cs b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/ILoginService.cs index cb6d347..29818bc 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/ILoginService.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/ILoginService.cs @@ -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 /// JWT-token - Task LoginAsync(LoginRequest request, CancellationToken cancellationToken); + Task LoginAsync(LoginRequest request, CancellationToken cancellationToken); } diff --git a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/LoginService.cs b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/LoginService.cs index c32ba13..85840c5 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/LoginService.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/LoginService/LoginService.cs @@ -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; /// public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService { - async Task ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) + async Task ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) { var user = await users.FindByEmailAsync(request.AuthData.Email, cancellationToken); if (user is null || user.Password != request.AuthData.Password) diff --git a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/NeededServices/ITokenGenerator.cs b/SchengenVisaApi/ApplicationLayer/Services/AuthServices/NeededServices/ITokenGenerator.cs deleted file mode 100644 index a7e6e9f..0000000 --- a/SchengenVisaApi/ApplicationLayer/Services/AuthServices/NeededServices/ITokenGenerator.cs +++ /dev/null @@ -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); -} diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/BlazorWebAssemblyVisaApiClient.csproj b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/BlazorWebAssemblyVisaApiClient.csproj index e7cf21b..a2499f3 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/BlazorWebAssemblyVisaApiClient.csproj +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/BlazorWebAssemblyVisaApiClient.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Pages/Auth.razor b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Pages/Auth.razor new file mode 100644 index 0000000..a32049c --- /dev/null +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Pages/Auth.razor @@ -0,0 +1,35 @@ +@page "/login" +@using VisaApiClient + +Authentication + + + + + + + +

@loginResult

+ +@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 e) + { + loginResult = e.Result.Detail!; + } + } +} diff --git a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Program.cs b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Program.cs index 4832aee..1d6c60f 100644 --- a/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Program.cs +++ b/SchengenVisaApi/BlazorWebAssemblyVisaApiClient/Program.cs @@ -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"); builder.RootComponents.Add("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(sp => + new Client(baseAddress, sp.GetRequiredService())); await builder.Build().RunAsync(); } diff --git a/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs b/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs index e96d625..eeb7c0f 100644 --- a/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs +++ b/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs @@ -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 { /// - public string CreateToken(User user) + public AuthToken CreateToken(User user) { var claims = new List { @@ -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)); } } diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs index 8c6efeb..4522cfc 100644 --- a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs +++ b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs @@ -55,7 +55,7 @@ public class UsersController( /// Returns JWT-token for authentication [HttpGet("login")] - [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task Login(string email, string password, CancellationToken cancellationToken) { diff --git a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs index 8f1b336..26d6fa5 100644 --- a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs +++ b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs @@ -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()) .AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); } diff --git a/SchengenVisaApi/SchengenVisaApi/RequestPipeline.cs b/SchengenVisaApi/SchengenVisaApi/RequestPipeline.cs index 6846a9f..47ac158 100644 --- a/SchengenVisaApi/SchengenVisaApi/RequestPipeline.cs +++ b/SchengenVisaApi/SchengenVisaApi/RequestPipeline.cs @@ -13,6 +13,8 @@ public static class PipelineRequest app.UseStatusCodePages(); + app.UseCors("policy"); + app.UseAuthentication() .UseAuthorization(); diff --git a/SchengenVisaApi/VisaApiClient/Client.cs b/SchengenVisaApi/VisaApiClient/Client.cs index f32ff24..c65b9de 100644 --- a/SchengenVisaApi/VisaApiClient/Client.cs +++ b/SchengenVisaApi/VisaApiClient/Client.cs @@ -413,7 +413,7 @@ namespace VisaApiClient /// /// Success /// A server side error occurred. - public virtual System.Threading.Tasks.Task LoginAsync(string? email, string? password) + public virtual System.Threading.Tasks.Task LoginAsync(string? email, string? password) { return LoginAsync(email, password, System.Threading.CancellationToken.None); } @@ -424,7 +424,7 @@ namespace VisaApiClient /// /// Success /// A server side error occurred. - public virtual async System.Threading.Tasks.Task LoginAsync(string? email, string? password, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task 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(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(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 { diff --git a/SchengenVisaApi/VisaApiClient/ClientBase.cs b/SchengenVisaApi/VisaApiClient/ClientBase.cs index ecc1532..5fcdbe5 100644 --- a/SchengenVisaApi/VisaApiClient/ClientBase.cs +++ b/SchengenVisaApi/VisaApiClient/ClientBase.cs @@ -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 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; } }