From d3b156a9c42abca90d58c2cea2b089cf297d04e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Sat, 30 Mar 2024 22:33:31 +0100 Subject: [PATCH] Closes #60 Bump dotnet to 8, bump all of the dependencies to matching version. Migrate from "System.IdentityModel.Tokens.Jwt" to "Microsoft.IdentityModel.JsonWebTokens" as it is now the recomended approach. Remove dead code, as agreed We will not be doing any maping of Keycloak id in our db. --- backend/Dockerfile | 4 +- .../Controllers/AuthenticationController.cs | 64 ------------------- .../api/Controllers/GreetingsController.cs | 26 -------- backend/src/api/Data/DAO/UserModel.cs | 12 ---- backend/src/api/Data/DTO/UserDTO.cs | 12 ---- .../20240223084836_Init.Designer.cs | 50 --------------- .../src/api/Migrations/20240223084836_Init.cs | 36 ----------- backend/src/api/Program.cs | 4 -- backend/src/api/RequestExtensions.cs | 20 ++++++ .../src/api/Services/Users/IUserService.cs | 8 --- backend/src/api/Services/Users/UserService.cs | 33 ---------- backend/src/api/api.csproj | 20 +++--- frontend/src/helpers/RequireAuth.tsx | 15 ----- 13 files changed, 34 insertions(+), 270 deletions(-) delete mode 100644 backend/src/api/Controllers/AuthenticationController.cs delete mode 100644 backend/src/api/Controllers/GreetingsController.cs delete mode 100644 backend/src/api/Data/DAO/UserModel.cs delete mode 100644 backend/src/api/Data/DTO/UserDTO.cs delete mode 100644 backend/src/api/Migrations/20240223084836_Init.Designer.cs delete mode 100644 backend/src/api/Migrations/20240223084836_Init.cs create mode 100644 backend/src/api/RequestExtensions.cs delete mode 100644 backend/src/api/Services/Users/IUserService.cs delete mode 100644 backend/src/api/Services/Users/UserService.cs diff --git a/backend/Dockerfile b/backend/Dockerfile index 5990f8d..7a65178 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,11 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/aspnet:7.0.10-alpine3.18 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY src/api/api.csproj src/api/ RUN dotnet restore "src/api/api.csproj" diff --git a/backend/src/api/Controllers/AuthenticationController.cs b/backend/src/api/Controllers/AuthenticationController.cs deleted file mode 100644 index de8b353..0000000 --- a/backend/src/api/Controllers/AuthenticationController.cs +++ /dev/null @@ -1,64 +0,0 @@ -using api.Constants; -using api.Data.DTO; -using api.Services.Users; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.IdentityModel.Tokens.Jwt; - -namespace api.Controllers; - -[ApiController] -[Route("/api/auth")] -public class AuthenticationController : ControllerBase -{ - private readonly IUserService _userService; - private readonly ILogger _logger; - - public AuthenticationController(IUserService userService, ILogger logger) - { - _userService = userService; - _logger = logger; - } - - [HttpGet("create-user")] - [Authorize] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task CreateUser() - { - var accessToken = Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); - var jsonTokenData = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); - var keycloakUuid = jsonTokenData.Subject; - var userEmail = jsonTokenData.Claims.FirstOrDefault(claim => claim.Type == "email")?.Value; - - if (userEmail == null) - { - _logger.LogInformation("User with keycloakUuid={keycloakUuid} tried to log in without email.", keycloakUuid); - return BadRequest("Required data missing"); - } - - var (isSuccess, error) = await _userService.CreateKeycloakUser( - new UserDTO - { - KeycloakUuid = keycloakUuid, - Email = userEmail - } - ); - - if (isSuccess) - { - _logger.LogInformation("The user {email} has been created successfully.", userEmail); - return Ok("User created"); - } - else - { - if (error == ErrorMessages.UserAlreadyExists) - { - _logger.LogInformation("The user {email} has already been created.", userEmail); - return Ok("User is already created"); - } - _logger.LogInformation("Failure during creation of {email} user.", userEmail); - return BadRequest(error); - } - } -} diff --git a/backend/src/api/Controllers/GreetingsController.cs b/backend/src/api/Controllers/GreetingsController.cs deleted file mode 100644 index 665bed0..0000000 --- a/backend/src/api/Controllers/GreetingsController.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace api.Controllers; - -[ApiController] -[Route("/api/greetings")] -public class GreetingsController : ControllerBase -{ - [HttpGet] - [Authorize] - [ProducesResponseType(StatusCodes.Status200OK)] - [Route("user")] - public IActionResult Greet() - { - return Ok($"Hello {HttpContext?.User?.Identity?.Name ?? "World" }"); - } - - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [Route("HelloWorld")] - public IActionResult HelloWorld() - { - return Ok("Hello World!"); - } -} diff --git a/backend/src/api/Data/DAO/UserModel.cs b/backend/src/api/Data/DAO/UserModel.cs deleted file mode 100644 index 12253d3..0000000 --- a/backend/src/api/Data/DAO/UserModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace api.Data.DAO; - -public class UserModel -{ - [Key] - public int Id { get; set; } - public required string KeycloakUuid { get; set; } - [EmailAddress] - public required string Email { get; set; } -} diff --git a/backend/src/api/Data/DTO/UserDTO.cs b/backend/src/api/Data/DTO/UserDTO.cs deleted file mode 100644 index 7079db2..0000000 --- a/backend/src/api/Data/DTO/UserDTO.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace api.Data.DTO; - -public class UserDTO -{ - [Required] - public required string KeycloakUuid { get; set; } - [Required] - [EmailAddress] - public required string Email { get; set; } -} diff --git a/backend/src/api/Migrations/20240223084836_Init.Designer.cs b/backend/src/api/Migrations/20240223084836_Init.Designer.cs deleted file mode 100644 index a363f14..0000000 --- a/backend/src/api/Migrations/20240223084836_Init.Designer.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using api.Data; - -#nullable disable - -namespace api.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240223084836_Init")] - partial class Init - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.2") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("api.Data.DAO.UserModel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("KeycloakUuid") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/backend/src/api/Migrations/20240223084836_Init.cs b/backend/src/api/Migrations/20240223084836_Init.cs deleted file mode 100644 index 9f4ae7a..0000000 --- a/backend/src/api/Migrations/20240223084836_Init.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace api.Migrations -{ - /// - public partial class Init : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - KeycloakUuid = table.Column(type: "text", nullable: false), - Email = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Users"); - } - } -} diff --git a/backend/src/api/Program.cs b/backend/src/api/Program.cs index 35b8666..9f9c12a 100644 --- a/backend/src/api/Program.cs +++ b/backend/src/api/Program.cs @@ -1,5 +1,4 @@ using api.Data; -using api.Services.Users; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; @@ -40,8 +39,6 @@ ValidateIssuer = true, ValidateLifetime = true }; - builder.Services.AddSingleton(tokenValidationParameters); - builder.Services.AddSingleton(keycloakJwtOptions); builder.Services.AddAuthentication(options => { @@ -60,7 +57,6 @@ { c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); }); - builder.Services.AddScoped(); builder.Services.AddCors(options => { diff --git a/backend/src/api/RequestExtensions.cs b/backend/src/api/RequestExtensions.cs new file mode 100644 index 0000000..2d45f01 --- /dev/null +++ b/backend/src/api/RequestExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.IdentityModel.JsonWebTokens; + +namespace api; + +public static class RequestExtensions +{ + public static string? UserId(this HttpRequest request) + { + try + { + var accessToken = request.Headers.Authorization.ToString().Split(' ')[1]; + var jsonTokenData = new JsonWebTokenHandler().ReadJsonWebToken(accessToken); + return jsonTokenData.Subject; + } + catch + { + return null; + } + } +} diff --git a/backend/src/api/Services/Users/IUserService.cs b/backend/src/api/Services/Users/IUserService.cs deleted file mode 100644 index 5b09ba7..0000000 --- a/backend/src/api/Services/Users/IUserService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using api.Data.DTO; - -namespace api.Services.Users; - -public interface IUserService -{ - Task<(bool isSuccess, string? error)> CreateKeycloakUser(UserDTO user); -} diff --git a/backend/src/api/Services/Users/UserService.cs b/backend/src/api/Services/Users/UserService.cs deleted file mode 100644 index 08d440f..0000000 --- a/backend/src/api/Services/Users/UserService.cs +++ /dev/null @@ -1,33 +0,0 @@ -using api.Data; -using api.Data.DTO; -using api.Data.DAO; -using api.Constants; - -namespace api.Services.Users; - -public class UserService : IUserService -{ - private readonly ApplicationDbContext _applicationDbContext; - - public UserService(ApplicationDbContext applicationDbContext) - { - _applicationDbContext = applicationDbContext; - } - - public async Task<(bool isSuccess, string? error)> CreateKeycloakUser(UserDTO user) - { - var userInstance = _applicationDbContext.Users.FirstOrDefault(userInstance => userInstance.KeycloakUuid.Equals(user.KeycloakUuid)); - if (userInstance != null) - { - return (false, ErrorMessages.UserAlreadyExists); - } - var newUser = new UserModel() - { - KeycloakUuid = user.KeycloakUuid, - Email = user.Email - }; - await _applicationDbContext.Users.AddAsync(newUser); - await _applicationDbContext.SaveChangesAsync(); - return (true, null); - } -} diff --git a/backend/src/api/api.csproj b/backend/src/api/api.csproj index 6d537d7..50bdbc8 100644 --- a/backend/src/api/api.csproj +++ b/backend/src/api/api.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 f7cb2e8e-61a4-4a0a-9259-839230fcfe2d Linux enable @@ -9,18 +9,22 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + + + + + diff --git a/frontend/src/helpers/RequireAuth.tsx b/frontend/src/helpers/RequireAuth.tsx index 3014338..8bfa471 100644 --- a/frontend/src/helpers/RequireAuth.tsx +++ b/frontend/src/helpers/RequireAuth.tsx @@ -7,21 +7,6 @@ import Constants from "./Constants"; export default function RequireAuth() { const { isLogin, keycloak } = useAuth(); - - const createUserInBackend = async () => { - const response = await fetch(`${Constants.BASE_URL}/auth/create-user`, { - headers: { Authorization: `Bearer ${keycloak!.token}` }, - }); - if (response.status !== 200) { - const errorMessage = await response.text(); - throw new Error(errorMessage); - } - }; - - if (isLogin) { - createUserInBackend(); - } - return isLogin ? :

Authenticating...

; }