From f2e6c72ec907008cad3f013b304a25aebe1d43dd Mon Sep 17 00:00:00 2001 From: Bardin08 Date: Wed, 15 May 2024 14:07:57 +0300 Subject: [PATCH] Add Auth0 integration --- .../Application/Auth0/IAuthService.cs | 9 +++ .../Application/DependencyInjection.cs | 6 +- EduAutomation/Domain/Auth0/LoginDto.cs | 3 + EduAutomation/Domain/Auth0/RegisterDto.cs | 7 ++ EduAutomation/EduAutomation.csproj | 3 + .../Infrastructure/Auth0/Auth0Options.cs | 13 ++++ .../Infrastructure/Auth0/AuthService.cs | 67 +++++++++++++++++++ .../Infrastructure/DependencyInjection.cs | 28 +++++++- EduAutomation/Program.cs | 9 ++- EduAutomation/Rest/Auth0/AuthEndpoints.cs | 35 ++++++++++ .../Rest/Auth0/Mappers/LoginRequestMapper.cs | 12 ++++ .../Auth0/Mappers/RegisterRequestMapper.cs | 12 ++++ .../Rest/Auth0/Models/LoginRequest.cs | 3 + .../Rest/Auth0/Models/RegisterRequest.cs | 7 ++ 14 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 EduAutomation/Application/Auth0/IAuthService.cs create mode 100644 EduAutomation/Domain/Auth0/LoginDto.cs create mode 100644 EduAutomation/Domain/Auth0/RegisterDto.cs create mode 100644 EduAutomation/Infrastructure/Auth0/Auth0Options.cs create mode 100644 EduAutomation/Infrastructure/Auth0/AuthService.cs create mode 100644 EduAutomation/Rest/Auth0/AuthEndpoints.cs create mode 100644 EduAutomation/Rest/Auth0/Mappers/LoginRequestMapper.cs create mode 100644 EduAutomation/Rest/Auth0/Mappers/RegisterRequestMapper.cs create mode 100644 EduAutomation/Rest/Auth0/Models/LoginRequest.cs create mode 100644 EduAutomation/Rest/Auth0/Models/RegisterRequest.cs diff --git a/EduAutomation/Application/Auth0/IAuthService.cs b/EduAutomation/Application/Auth0/IAuthService.cs new file mode 100644 index 0000000..e4c42ac --- /dev/null +++ b/EduAutomation/Application/Auth0/IAuthService.cs @@ -0,0 +1,9 @@ +using EduAutomation.Domain.Auth0; + +namespace EduAutomation.Application.Auth0; + +public interface IAuthService +{ + Task Login(LoginDto dto); + Task Register(RegisterDto dto); +} diff --git a/EduAutomation/Application/DependencyInjection.cs b/EduAutomation/Application/DependencyInjection.cs index 0872e3a..578d57a 100644 --- a/EduAutomation/Application/DependencyInjection.cs +++ b/EduAutomation/Application/DependencyInjection.cs @@ -1,6 +1,8 @@ -using EduAutomation.Application.GitHub.Services; +using EduAutomation.Application.Auth0; +using EduAutomation.Application.GitHub.Services; using EduAutomation.Application.Telegram.Formatters; using EduAutomation.Application.Trello.Formatters; +using EduAutomation.Infrastructure.Auth0; namespace EduAutomation.Application; @@ -8,6 +10,8 @@ public static class DependencyInjection { public static IServiceCollection AddApplication(this IServiceCollection services) { + services.AddScoped(); + services.AddTransient(); services.AddTransient(); diff --git a/EduAutomation/Domain/Auth0/LoginDto.cs b/EduAutomation/Domain/Auth0/LoginDto.cs new file mode 100644 index 0000000..181bb7d --- /dev/null +++ b/EduAutomation/Domain/Auth0/LoginDto.cs @@ -0,0 +1,3 @@ +namespace EduAutomation.Domain.Auth0; + +public record LoginDto(string Login, string Password); diff --git a/EduAutomation/Domain/Auth0/RegisterDto.cs b/EduAutomation/Domain/Auth0/RegisterDto.cs new file mode 100644 index 0000000..e63dd72 --- /dev/null +++ b/EduAutomation/Domain/Auth0/RegisterDto.cs @@ -0,0 +1,7 @@ +namespace EduAutomation.Domain.Auth0; + +public record RegisterDto( + string Login, + string FullName, + string Password, + string Email); diff --git a/EduAutomation/EduAutomation.csproj b/EduAutomation/EduAutomation.csproj index 606b0f1..7e4811b 100644 --- a/EduAutomation/EduAutomation.csproj +++ b/EduAutomation/EduAutomation.csproj @@ -7,8 +7,11 @@ + + + diff --git a/EduAutomation/Infrastructure/Auth0/Auth0Options.cs b/EduAutomation/Infrastructure/Auth0/Auth0Options.cs new file mode 100644 index 0000000..11bdbf9 --- /dev/null +++ b/EduAutomation/Infrastructure/Auth0/Auth0Options.cs @@ -0,0 +1,13 @@ +namespace EduAutomation.Infrastructure.Auth0; + +public class Auth0Options +{ + public const string SectionName = "Auth0"; + + public required string ClientId { get; init; } + public required string ClientSecret { get; init; } + public required string Domain { get; init; } + public required string Authority { get; init; } + public required string Audience { get; init; } + public required string Realm { get; init; } +} diff --git a/EduAutomation/Infrastructure/Auth0/AuthService.cs b/EduAutomation/Infrastructure/Auth0/AuthService.cs new file mode 100644 index 0000000..ce70dc2 --- /dev/null +++ b/EduAutomation/Infrastructure/Auth0/AuthService.cs @@ -0,0 +1,67 @@ +using Auth0.AuthenticationApi; +using Auth0.AuthenticationApi.Models; +using EduAutomation.Application.Auth0; +using EduAutomation.Domain.Auth0; +using Microsoft.Extensions.Options; + +namespace EduAutomation.Infrastructure.Auth0; + +public class AuthService( + IAuthenticationApiClient authenticationApiClient, + IOptions auth0Options) : IAuthService +{ + private readonly Auth0Options _auth0Options = auth0Options.Value; + + public async Task Login(LoginDto dto) + { + try + { + var auth0Response = await authenticationApiClient.GetTokenAsync(new ResourceOwnerTokenRequest + { + Username = dto.Login, + Password = dto.Password, + Realm = _auth0Options.Realm, + + Audience = _auth0Options.Audience, + ClientId = _auth0Options.ClientId, + ClientSecret = _auth0Options.ClientSecret, + Scope = "openid" + }); + + return !string.IsNullOrEmpty(auth0Response?.AccessToken ?? null) + ? auth0Response!.AccessToken : null; + } + catch (Exception e) + { + Console.WriteLine(e); + return null; + } + } + + public async Task Register(RegisterDto dto) + { + try + { + var username = $"{dto.FullName[0]}-{Guid.NewGuid().ToString()[..6]}"; + await authenticationApiClient.SignupUserAsync(new SignupUserRequest + { + Username = username, + Nickname = username, + Name = dto.FullName , + Password = dto.Password, + Email = dto.Email, + + Connection = "edu-automation", + ClientId = _auth0Options.ClientId + }); + + var jwtToken = await Login(new LoginDto(username, dto.Password)); + return jwtToken; + } + catch (Exception e) + { + Console.WriteLine(e); + return null; + } + } +} diff --git a/EduAutomation/Infrastructure/DependencyInjection.cs b/EduAutomation/Infrastructure/DependencyInjection.cs index 4349149..6303d0d 100644 --- a/EduAutomation/Infrastructure/DependencyInjection.cs +++ b/EduAutomation/Infrastructure/DependencyInjection.cs @@ -1,11 +1,14 @@ -using EduAutomation.Application.GitHub; +using Auth0Net.DependencyInjection; +using EduAutomation.Application.GitHub; using EduAutomation.Application.Telegram; using EduAutomation.Application.Trello; +using EduAutomation.Infrastructure.Auth0; using EduAutomation.Infrastructure.GitHub; using EduAutomation.Infrastructure.Telegram; using EduAutomation.Infrastructure.Trello; using GitHubUtils.Core; using Manatee.Trello; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Telegram.Bot; namespace EduAutomation.Infrastructure; @@ -15,6 +18,8 @@ public static class DependencyInjection public static IServiceCollection AddInfrastructure( this IServiceCollection services, IConfiguration configuration) { + services.AddAuth0(configuration); + services.AddTrello(configuration); services.AddGitHub(configuration); services.AddTelegram(configuration); @@ -22,6 +27,27 @@ public static IServiceCollection AddInfrastructure( return services; } + private static void AddAuth0(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration.GetSection(Auth0Options.SectionName)); + services.AddAuth0AuthenticationClient(c => + { + c.Domain = configuration["Auth0:Domain"]!; + c.ClientId = configuration["Auth0:ClientId"]!; + c.ClientSecret = configuration["Auth0:ClientSecret"]!; + }); + + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }).AddJwtBearer(options => + { + options.Authority = configuration["Auth0:Authority"]; + options.Audience = configuration["Auth0:Audience"]; + }); + } + private static void AddTrello(this IServiceCollection services, IConfiguration configuration) { TrelloAuthorization.Default.AppKey = configuration[TrelloOptions.SectionName + ":AppKey"]; diff --git a/EduAutomation/Program.cs b/EduAutomation/Program.cs index 9439540..a84163c 100644 --- a/EduAutomation/Program.cs +++ b/EduAutomation/Program.cs @@ -1,9 +1,12 @@ using EduAutomation.Application; using EduAutomation.Infrastructure; +using EduAutomation.Rest.Auth0; using EduAutomation.Rest.GitHub; var builder = WebApplication.CreateBuilder(args); +builder.Services.AddAuthorization(); + builder.Services.AddInfrastructure(builder.Configuration); builder.Services.AddApplication(); @@ -18,8 +21,12 @@ app.UseSwaggerUI(); } -app.UseHttpsRedirection(); +app.UseAuthorization(); +app.UseAuthentication(); app.MapPost("/api/github-repo-webhook", GitHubEndpoints.RepositoryCreatedWebhook); +app.MapPost("/api/auth/login", AuthEndpoints.Login); +app.MapPost("/api/auth/register", AuthEndpoints.Register); + app.Run(); diff --git a/EduAutomation/Rest/Auth0/AuthEndpoints.cs b/EduAutomation/Rest/Auth0/AuthEndpoints.cs new file mode 100644 index 0000000..034097f --- /dev/null +++ b/EduAutomation/Rest/Auth0/AuthEndpoints.cs @@ -0,0 +1,35 @@ +using EduAutomation.Application.Auth0; +using EduAutomation.Rest.Auth0.Mappers; +using EduAutomation.Rest.Auth0.Models; +using Microsoft.AspNetCore.Mvc; + +namespace EduAutomation.Rest.Auth0; + +public static class AuthEndpoints +{ + public static async Task Login( + LoginRequest payload, + CancellationToken cancellationToken, + [FromServices] IAuthService authService) + { + var loginDto = payload.ToDomainModel(); + var jwtToken = await authService.Login(loginDto); + + return !string.IsNullOrEmpty(jwtToken) + ? Results.Ok(jwtToken) + : Results.Unauthorized(); + } + + public static async Task Register( + RegisterRequest payload, + CancellationToken cancellationToken, + [FromServices] IAuthService authService) + { + var registerDto = payload.ToDomainModel(); + var jwtToken = await authService.Register(registerDto); + + return !string.IsNullOrEmpty(jwtToken) + ? Results.Ok(jwtToken) + : Results.Unauthorized(); + } +} diff --git a/EduAutomation/Rest/Auth0/Mappers/LoginRequestMapper.cs b/EduAutomation/Rest/Auth0/Mappers/LoginRequestMapper.cs new file mode 100644 index 0000000..7860eef --- /dev/null +++ b/EduAutomation/Rest/Auth0/Mappers/LoginRequestMapper.cs @@ -0,0 +1,12 @@ +using EduAutomation.Domain.Auth0; +using EduAutomation.Rest.Auth0.Models; + +namespace EduAutomation.Rest.Auth0.Mappers; + +public static class LoginRequestMapper +{ + public static LoginDto ToDomainModel(this LoginRequest r) + { + return new LoginDto(r.Login, r.Password); + } +} diff --git a/EduAutomation/Rest/Auth0/Mappers/RegisterRequestMapper.cs b/EduAutomation/Rest/Auth0/Mappers/RegisterRequestMapper.cs new file mode 100644 index 0000000..44798f2 --- /dev/null +++ b/EduAutomation/Rest/Auth0/Mappers/RegisterRequestMapper.cs @@ -0,0 +1,12 @@ +using EduAutomation.Domain.Auth0; +using EduAutomation.Rest.Auth0.Models; + +namespace EduAutomation.Rest.Auth0.Mappers; + +public static class RegisterRequestMapper +{ + public static RegisterDto ToDomainModel(this RegisterRequest r) + { + return new RegisterDto(r.Login, r.FullName, r.Password, r.Email); + } +} diff --git a/EduAutomation/Rest/Auth0/Models/LoginRequest.cs b/EduAutomation/Rest/Auth0/Models/LoginRequest.cs new file mode 100644 index 0000000..3d5c8b3 --- /dev/null +++ b/EduAutomation/Rest/Auth0/Models/LoginRequest.cs @@ -0,0 +1,3 @@ +namespace EduAutomation.Rest.Auth0.Models; + +public record LoginRequest(string Login, string Password); diff --git a/EduAutomation/Rest/Auth0/Models/RegisterRequest.cs b/EduAutomation/Rest/Auth0/Models/RegisterRequest.cs new file mode 100644 index 0000000..18cc067 --- /dev/null +++ b/EduAutomation/Rest/Auth0/Models/RegisterRequest.cs @@ -0,0 +1,7 @@ +namespace EduAutomation.Rest.Auth0.Models; + +public record RegisterRequest( + string Login, + string FullName, + string Password, + string Email);