From 7d7bc9e82f8609e86543d41f017575c50d936d1e Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Tue, 28 Mar 2023 17:01:50 +0000 Subject: [PATCH] Merged PR 77: Added "user-info" method in API - Added "user-info" method in API - removed ProfilePictureUrl property from token - contact options - Added user contact options - mapping fix --- Directory.Build.props | 2 +- ReleaseNotes.xml | 16 +++- dependencies.props | 2 +- .../Abstractions/IHttpContextService.cs | 7 ++ .../Mappings/MappingProfile.cs | 9 ++ src/Tuitio.Application/Queries/GetUserInfo.cs | 64 +++++++++++++ .../DbContexts/TuitioDbContext.cs | 2 + .../AppUserConfiguration.cs | 1 + .../ContactOptionConfiguration.cs | 18 ++++ .../ContactTypeConfiguration.cs | 17 ++++ .../Repositories/UserRepository.cs | 9 ++ .../Scripts/2.3.0/01.ContactType table.sql | 24 +++++ .../Scripts/2.3.0/02.ContactOption table.sql | 10 ++ .../Tuitio.Domain.Data.csproj | 6 ++ src/Tuitio.Domain/Entities/AppUser.cs | 1 + src/Tuitio.Domain/Entities/ContactInfo.cs | 13 +++ src/Tuitio.Domain/Entities/ContactType.cs | 11 +++ src/Tuitio.Domain/Models/Token.cs | 1 - .../Repositories/IUserRepository.cs | 1 + .../Dto/ResultRecords.cs | 1 - .../AuthenticationExtensions.cs | 18 ++++ .../Authentication/AuthenticationHandler.cs | 91 +++++++++++++++++++ .../Authentication/HttpContextService.cs | 31 +++++++ src/Tuitio/Controllers/ConnectController.cs | 12 +++ src/Tuitio/Extensions/StartupExtensions.cs | 6 +- 25 files changed, 365 insertions(+), 8 deletions(-) create mode 100644 src/Tuitio.Application/Abstractions/IHttpContextService.cs create mode 100644 src/Tuitio.Application/Queries/GetUserInfo.cs create mode 100644 src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactOptionConfiguration.cs create mode 100644 src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactTypeConfiguration.cs create mode 100644 src/Tuitio.Domain.Data/Scripts/2.3.0/01.ContactType table.sql create mode 100644 src/Tuitio.Domain.Data/Scripts/2.3.0/02.ContactOption table.sql create mode 100644 src/Tuitio.Domain/Entities/ContactInfo.cs create mode 100644 src/Tuitio.Domain/Entities/ContactType.cs create mode 100644 src/Tuitio/Authentication/AuthenticationExtensions.cs create mode 100644 src/Tuitio/Authentication/AuthenticationHandler.cs create mode 100644 src/Tuitio/Authentication/HttpContextService.cs diff --git a/Directory.Build.props b/Directory.Build.props index 2fc3dd9..b4b2f3d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 2.2.0 + 2.3.0 Tudor Stanciu STA Tuitio diff --git a/ReleaseNotes.xml b/ReleaseNotes.xml index c7a8b0c..77af9bd 100644 --- a/ReleaseNotes.xml +++ b/ReleaseNotes.xml @@ -53,11 +53,11 @@ 2.0.0 ◾ Tuitio rebranding - ◾ .NET 6 upgrade - ◾ Nuget packages upgrade + ◾ .NET 6 upgrade + ◾ Nuget packages upgrade ◾ Added Seq logging ◾ Refactoring and code cleanup - ◾ Added README.md file + ◾ Added README.md file @@ -75,4 +75,14 @@ ◾ Added some tests + + 2.3.0 + 2023-03-27 19:20 + + Added "user-info" method in API + ◾ The "user-info" method returns the data of the authenticated user. + ◾ Added http context accessor and authentication handler + ◾ Added user contact options + + \ No newline at end of file diff --git a/dependencies.props b/dependencies.props index 4cafce3..db91843 100644 --- a/dependencies.props +++ b/dependencies.props @@ -9,7 +9,7 @@ 9.0.0 6.0.1 13.0.1 - 1.0.6 + 1.0.7 1.2.0 1.0.0 diff --git a/src/Tuitio.Application/Abstractions/IHttpContextService.cs b/src/Tuitio.Application/Abstractions/IHttpContextService.cs new file mode 100644 index 0000000..bfbcacd --- /dev/null +++ b/src/Tuitio.Application/Abstractions/IHttpContextService.cs @@ -0,0 +1,7 @@ +namespace Tuitio.Application.Abstractions +{ + public interface IHttpContextService + { + int GetUserId(); + } +} diff --git a/src/Tuitio.Application/Mappings/MappingProfile.cs b/src/Tuitio.Application/Mappings/MappingProfile.cs index d1dfbf0..9b031f5 100644 --- a/src/Tuitio.Application/Mappings/MappingProfile.cs +++ b/src/Tuitio.Application/Mappings/MappingProfile.cs @@ -5,6 +5,7 @@ using Tuitio.Domain.Entities; using System.Collections.Generic; using dto = Tuitio.PublishedLanguage.Dto; using models = Tuitio.Domain.Models; +using Tuitio.Application.Queries; namespace Tuitio.Application.Mappings { @@ -13,6 +14,14 @@ namespace Tuitio.Application.Mappings public MappingProfile() { CreateMap(); + CreateMap() + .ForMember(z => z.Claims, src => src.MapFrom(z => ComposeClaims(z.Claims))); + + CreateMap() + .ForMember(z => z.Id, src => src.MapFrom(z => z.ContactOptionId)) + .ForMember(z => z.ContactTypeCode, src => src.MapFrom(z => z.ContactType.ContactTypeCode)) + .ForMember(z => z.ContactTypeName, src => src.MapFrom(z => z.ContactType.ContactTypeName)); + CreateMap() .ForMember(z => z.Claims, src => src.MapFrom(z => ComposeClaims(z.Claims))); diff --git a/src/Tuitio.Application/Queries/GetUserInfo.cs b/src/Tuitio.Application/Queries/GetUserInfo.cs new file mode 100644 index 0000000..89d1cfb --- /dev/null +++ b/src/Tuitio.Application/Queries/GetUserInfo.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Tudor Stanciu + +using AutoMapper; +using MediatR; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tuitio.Application.Abstractions; +using Tuitio.Domain.Repositories; + +namespace Tuitio.Application.Queries +{ + public class GetUserInfo + { + public class Query : IRequest { } + + public record Model + { + public int UserId { get; set; } + public string UserName { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + public string ProfilePictureUrl { get; set; } + public string SecurityStamp { get; set; } + public DateTime CreationDate { get; set; } + public int? FailedLoginAttempts { get; set; } + public DateTime? LastLoginDate { get; set; } + public Dictionary Claims { get; init; } + public ContactOption[] ContactOptions { get; set; } + } + + public record ContactOption + { + public int Id { get; set; } + public string ContactTypeCode { get; set; } + public string ContactTypeName { get; set; } + public string ContactValue { get; set; } + } + + public class QueryHandler : IRequestHandler + { + private readonly IUserRepository _userRepository; + private readonly IHttpContextService _httpContextService; + private readonly IMapper _mapper; + + public QueryHandler(IUserRepository userRepository, IHttpContextService httpContextService, IMapper mapper) + { + _userRepository=userRepository; + _httpContextService=httpContextService; + _mapper=mapper; + } + + public async Task Handle(Query request, CancellationToken cancellationToken) + { + var userId = _httpContextService.GetUserId(); + var user = await _userRepository.GetFullUser(userId); + var info = _mapper.Map(user); + return info; + } + } + } +} diff --git a/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs b/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs index 497966b..26b4da0 100644 --- a/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs +++ b/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs @@ -27,6 +27,8 @@ namespace Tuitio.Domain.Data.DbContexts modelBuilder.ApplyConfiguration(new AppUserConfiguration()); modelBuilder.ApplyConfiguration(new UserClaimConfiguration()); modelBuilder.ApplyConfiguration(new UserTokenConfiguration()); + modelBuilder.ApplyConfiguration(new ContactTypeConfiguration()); + modelBuilder.ApplyConfiguration(new ContactOptionConfiguration()); } } } diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs index 95aaf93..771d0d0 100644 --- a/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs +++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs @@ -14,6 +14,7 @@ namespace Tuitio.Domain.Data.EntityTypeConfiguration builder.Property(z => z.UserId).ValueGeneratedOnAdd(); builder.HasOne(z => z.Status).WithMany().HasForeignKey(z => z.StatusId); builder.HasMany(z => z.Claims).WithOne().HasForeignKey(z => z.UserId); + builder.HasMany(z => z.ContactOptions).WithOne().HasForeignKey(z => z.UserId); } } } diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactOptionConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactOptionConfiguration.cs new file mode 100644 index 0000000..e934b2a --- /dev/null +++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactOptionConfiguration.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2020 Tudor Stanciu + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Tuitio.Domain.Entities; + +namespace Tuitio.Domain.Data.EntityTypeConfiguration +{ + class ContactOptionConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ContactOption").HasKey(key => key.ContactOptionId); + builder.Property(z => z.ContactOptionId).ValueGeneratedOnAdd(); + builder.HasOne(z => z.ContactType).WithMany().HasForeignKey(z => z.ContactTypeId); + } + } +} diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactTypeConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactTypeConfiguration.cs new file mode 100644 index 0000000..e92e4d6 --- /dev/null +++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/ContactTypeConfiguration.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2020 Tudor Stanciu + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Tuitio.Domain.Entities; + +namespace Tuitio.Domain.Data.EntityTypeConfiguration +{ + class ContactTypeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ContactType").HasKey(z => z.ContactTypeId); + builder.Property(z => z.ContactTypeId).ValueGeneratedOnAdd(); + } + } +} diff --git a/src/Tuitio.Domain.Data/Repositories/UserRepository.cs b/src/Tuitio.Domain.Data/Repositories/UserRepository.cs index e11b6f6..f894100 100644 --- a/src/Tuitio.Domain.Data/Repositories/UserRepository.cs +++ b/src/Tuitio.Domain.Data/Repositories/UserRepository.cs @@ -28,6 +28,15 @@ namespace Tuitio.Domain.Data.Repositories .FirstOrDefaultAsync(z => z.UserName == userName && z.Password == password); } + public Task GetFullUser(int userId) + { + return _dbContext.Users + .Include(z => z.Status) + .Include(z => z.Claims) + .Include(z => z.ContactOptions).ThenInclude(z => z.ContactType) + .FirstOrDefaultAsync(z => z.UserId == userId); + } + public async Task UpdateUserAfterLogin(AppUser user, Token token, string tokenRaw) { var userToken = new UserToken() diff --git a/src/Tuitio.Domain.Data/Scripts/2.3.0/01.ContactType table.sql b/src/Tuitio.Domain.Data/Scripts/2.3.0/01.ContactType table.sql new file mode 100644 index 0000000..0172d6d --- /dev/null +++ b/src/Tuitio.Domain.Data/Scripts/2.3.0/01.ContactType table.sql @@ -0,0 +1,24 @@ +if not exists (select top 1 1 from sys.objects where name = 'ContactType' and type = 'U') +begin + create table ContactType + ( + ContactTypeId int identity(1, 1) constraint PK_ContactType primary key, + ContactTypeCode varchar(30) not null, + ContactTypeName varchar(50) not null + ) +end + +if not exists (select top 1 1 from ContactType) +begin + insert into ContactType(ContactTypeCode, ContactTypeName) + values ('EMAIL', 'Email'), + ('PHONE', 'Phone'), + ('WEBSITE', 'Website'), + ('LINKEDIN', 'LinkedIn'), + ('GITHUB', 'GitHub'), + ('GITEA', 'Gitea'), + ('PORTFOLIO', 'Portfolio'), + ('CURRICULUM_VITAE', 'Curriculum vitae'), + ('BLOG', 'Blog'), + ('REDDIT', 'Reddit') +end \ No newline at end of file diff --git a/src/Tuitio.Domain.Data/Scripts/2.3.0/02.ContactOption table.sql b/src/Tuitio.Domain.Data/Scripts/2.3.0/02.ContactOption table.sql new file mode 100644 index 0000000..9e0c394 --- /dev/null +++ b/src/Tuitio.Domain.Data/Scripts/2.3.0/02.ContactOption table.sql @@ -0,0 +1,10 @@ +if not exists (select top 1 1 from sys.objects where name = 'ContactOption' and type = 'U') +begin + create table ContactOption + ( + ContactOptionId int identity(1, 1) constraint PK_ContactOption primary key, + UserId int constraint FK_ContactOption_AppUser foreign key references AppUser(UserId), + ContactTypeId int constraint FK_ContactOption_ContactType foreign key references ContactType(ContactTypeId), + ContactValue varchar(150) not null + ) +end \ No newline at end of file diff --git a/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj b/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj index edd04a6..cd5c1bf 100644 --- a/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj +++ b/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj @@ -28,6 +28,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/src/Tuitio.Domain/Entities/AppUser.cs b/src/Tuitio.Domain/Entities/AppUser.cs index f9289d2..f7933bf 100644 --- a/src/Tuitio.Domain/Entities/AppUser.cs +++ b/src/Tuitio.Domain/Entities/AppUser.cs @@ -22,5 +22,6 @@ namespace Tuitio.Domain.Entities public DateTime? PasswordChangeDate { get; set; } public UserStatus Status { get; set; } public ICollection Claims { get; set; } + public ICollection ContactOptions { get; set; } } } diff --git a/src/Tuitio.Domain/Entities/ContactInfo.cs b/src/Tuitio.Domain/Entities/ContactInfo.cs new file mode 100644 index 0000000..9348217 --- /dev/null +++ b/src/Tuitio.Domain/Entities/ContactInfo.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2020 Tudor Stanciu + +namespace Tuitio.Domain.Entities +{ + public class ContactOption + { + public int ContactOptionId { get; set; } + public int UserId { get; set; } + public int ContactTypeId { get; set; } + public string ContactValue { get; set; } + public ContactType ContactType { get; set; } + } +} diff --git a/src/Tuitio.Domain/Entities/ContactType.cs b/src/Tuitio.Domain/Entities/ContactType.cs new file mode 100644 index 0000000..98fcf1c --- /dev/null +++ b/src/Tuitio.Domain/Entities/ContactType.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2020 Tudor Stanciu + +namespace Tuitio.Domain.Entities +{ + public class ContactType + { + public int ContactTypeId { get; set; } + public string ContactTypeCode { get; set; } + public string ContactTypeName { get; set; } + } +} diff --git a/src/Tuitio.Domain/Models/Token.cs b/src/Tuitio.Domain/Models/Token.cs index 9877562..f2621d3 100644 --- a/src/Tuitio.Domain/Models/Token.cs +++ b/src/Tuitio.Domain/Models/Token.cs @@ -13,7 +13,6 @@ namespace Tuitio.Domain.Models public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } - public string ProfilePictureUrl { get; set; } public string SecurityStamp { get; set; } public string LockStamp { get; set; } public DateTime CreatedAt { get; set; } diff --git a/src/Tuitio.Domain/Repositories/IUserRepository.cs b/src/Tuitio.Domain/Repositories/IUserRepository.cs index 158f629..4122059 100644 --- a/src/Tuitio.Domain/Repositories/IUserRepository.cs +++ b/src/Tuitio.Domain/Repositories/IUserRepository.cs @@ -10,6 +10,7 @@ namespace Tuitio.Domain.Repositories public interface IUserRepository { Task GetUser(string userName, string password); + Task GetFullUser(int userId); Task UpdateUserAfterLogin(AppUser user, Token token, string tokenRaw); Task GetActiveTokens(); Task RemoveToken(Guid tokenId); diff --git a/src/Tuitio.PublishedLanguage/Dto/ResultRecords.cs b/src/Tuitio.PublishedLanguage/Dto/ResultRecords.cs index 21bcbf2..1cdf55d 100644 --- a/src/Tuitio.PublishedLanguage/Dto/ResultRecords.cs +++ b/src/Tuitio.PublishedLanguage/Dto/ResultRecords.cs @@ -15,7 +15,6 @@ namespace Tuitio.PublishedLanguage.Dto public string FirstName { get; init; } public string LastName { get; init; } public string Email { get; init; } - public string ProfilePictureUrl { get; init; } public string SecurityStamp { get; init; } public string LockStamp { get; init; } public DateTime CreatedAt { get; init; } diff --git a/src/Tuitio/Authentication/AuthenticationExtensions.cs b/src/Tuitio/Authentication/AuthenticationExtensions.cs new file mode 100644 index 0000000..66eabb1 --- /dev/null +++ b/src/Tuitio/Authentication/AuthenticationExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.DependencyInjection; +using Tuitio.Application.Abstractions; + +namespace Tuitio.Authentication +{ + internal static class AuthenticationExtensions + { + public static IServiceCollection AddLocalAuthentication(this IServiceCollection services) + { + var scheme = "TuitioAuthentication"; + services.AddAuthentication(scheme) + .AddScheme(scheme, null); + services.AddScoped(); + return services; + } + } +} diff --git a/src/Tuitio/Authentication/AuthenticationHandler.cs b/src/Tuitio/Authentication/AuthenticationHandler.cs new file mode 100644 index 0000000..8c1a0da --- /dev/null +++ b/src/Tuitio/Authentication/AuthenticationHandler.cs @@ -0,0 +1,91 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Tuitio.Application.Services.Abstractions; +using Tuitio.Domain.Models; + +namespace Tuitio.Authentication +{ + public class AuthenticationHandler : AuthenticationHandler + { + private readonly IUserService _userService; + private readonly ILogger _logger; + + public AuthenticationHandler(IOptionsMonitor options, ILoggerFactory loggerFactory, UrlEncoder encoder, ISystemClock clock, IUserService userService, ILogger logger) : base(options, loggerFactory, encoder, clock) + { + _userService=userService; + _logger=logger; + } + + protected override async Task HandleAuthenticateAsync() + { + var token = GetAuthorizationToken(); + if (token == null) + return AuthenticateResult.Fail("AUTHORIZATION_HEADER_IS_MISSING"); + + var result = await Task.Run(() => _userService.Authorize(token)); + if (result == null) + return AuthenticateResult.Fail("UNAUTHORIZED"); + + var ticket = GetAuthenticationTicket(result); + return AuthenticateResult.Success(ticket); + } + + private string GetAuthorizationToken() + { + if (Request.Headers.ContainsKey("Authorization")) + { + var authorizationHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); + var token = authorizationHeader.Parameter; + return token; + } + + return null; + } + + private AuthenticationTicket GetAuthenticationTicket(Token result) + { + var claimCollection = new Dictionary() + { + { ClaimTypes.NameIdentifier, result.UserId.ToString() }, + { ClaimTypes.Name, result.UserName }, + }; + + if (result.FirstName != null) + claimCollection.Add(ClaimTypes.GivenName, result.FirstName); + + if (result.LastName != null) + claimCollection.Add(ClaimTypes.Surname, result.FirstName); + + if (result.Email != null) + claimCollection.Add(ClaimTypes.Email, result.Email); + + if (result.Claims != null && result.Claims.Any()) + { + foreach (var claim in result.Claims) + { + if (claimCollection.ContainsKey(claim.Key)) + { + _logger.LogWarning($"There is already a claim with key {claim.Key} in the collection. The combination {claim.Key}:{claim.Value} will be ignored."); + continue; + } + + claimCollection.Add(claim.Key, claim.Value); + } + } + + var claims = claimCollection.Select(z => new Claim(z.Key, z.Value)).ToArray(); + var identity = new ClaimsIdentity(claims, Scheme.Name); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, Scheme.Name); + + return ticket; + } + } +} diff --git a/src/Tuitio/Authentication/HttpContextService.cs b/src/Tuitio/Authentication/HttpContextService.cs new file mode 100644 index 0000000..682c7b5 --- /dev/null +++ b/src/Tuitio/Authentication/HttpContextService.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Http; +using System.Linq; +using System; +using Tuitio.Application.Abstractions; +using System.Security.Claims; + +namespace Tuitio.Authentication +{ + public class HttpContextService : IHttpContextService + { + private readonly IHttpContextAccessor _httpAccessor; + + public HttpContextService(IHttpContextAccessor httpAccessor) + { + _httpAccessor=httpAccessor; + } + + public int GetUserId() + { + var userIdString = _httpAccessor.HttpContext.User?.Claims.FirstOrDefault(z => z.Type == ClaimTypes.NameIdentifier)?.Value; + + if (string.IsNullOrEmpty(userIdString)) + throw new Exception("User id could not be retrieved from claims."); + + if (!int.TryParse(userIdString, out int userId)) + throw new Exception("User id is not a valid integer."); + + return userId; + } + } +} diff --git a/src/Tuitio/Controllers/ConnectController.cs b/src/Tuitio/Controllers/ConnectController.cs index e94b151..31d1d50 100644 --- a/src/Tuitio/Controllers/ConnectController.cs +++ b/src/Tuitio/Controllers/ConnectController.cs @@ -1,12 +1,15 @@ // Copyright (c) 2020 Tudor Stanciu using MediatR; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Tuitio.Application.CommandHandlers; +using Tuitio.Application.Queries; namespace Tuitio.Api.Controllers { + [Authorize] [ApiController] [Route("connect")] public class ConnectController : ControllerBase @@ -18,6 +21,7 @@ namespace Tuitio.Api.Controllers _mediator = mediator; } + [AllowAnonymous] [HttpPost("authorize")] public async Task AuthorizeToken([FromQuery] string token) { @@ -25,5 +29,13 @@ namespace Tuitio.Api.Controllers var result = await _mediator.Send(command); return Ok(result); } + + [HttpGet("user-info")] + public async Task GetUserInfo() + { + var command = new GetUserInfo.Query(); + var result = await _mediator.Send(command); + return Ok(result); + } } } diff --git a/src/Tuitio/Extensions/StartupExtensions.cs b/src/Tuitio/Extensions/StartupExtensions.cs index 03e9e76..b2c57f8 100644 --- a/src/Tuitio/Extensions/StartupExtensions.cs +++ b/src/Tuitio/Extensions/StartupExtensions.cs @@ -11,6 +11,7 @@ using Netmash.Infrastructure.DatabaseMigration; using Netmash.Infrastructure.DatabaseMigration.Constants; using Tuitio.Application; using Tuitio.Application.Services.Abstractions; +using Tuitio.Authentication; using Tuitio.Domain.Data; namespace Tuitio.Extensions @@ -20,6 +21,8 @@ namespace Tuitio.Extensions public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration) { services.AddControllers(); + services.AddLocalAuthentication(); + services.AddHttpContextAccessor(); // MediatR services.AddMediatR(typeof(Application.CommandHandlers.AccountLoginHandler).Assembly); @@ -30,7 +33,7 @@ namespace Tuitio.Extensions services.AddAutoMapper(typeof(Application.Mappings.MappingProfile).Assembly); // Swagger - services.AddSwagger("Tuitio API", AuthorizationType.None); + services.AddSwagger("Tuitio API", AuthorizationType.Tuitio); // Data access services.AddMigration(DatabaseType.SQLServer, MetadataLocation.Database); @@ -47,6 +50,7 @@ namespace Tuitio.Extensions app.UseCors(z => z.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); app.UseRouting(); + app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => {