From 0b81cb046af290076050c80a3f1a180925b4780d Mon Sep 17 00:00:00 2001 From: Holden <122419606+midozen@users.noreply.github.com> Date: Fri, 19 Jun 2026 12:57:31 -0500 Subject: [PATCH] Add application services for profiles and neutrino --- .../Profiles/Responses/LoginResponse.cs | 8 +-- API/Controllers/Config/V1/ConfigController.cs | 2 +- .../Neutrino/NeutrinoController.cs | 59 ++++++---------- .../Profiles/V1/ProfilesController.cs | 60 +++++----------- .../Common/Interfaces}/IConfigService.cs | 2 +- .../Common/Interfaces}/ITokenService.cs | 3 +- .../Common}/Tokens/TokenResult.cs | 2 +- .../Common/Tokens/TokenVerifyResult.cs | 12 ++++ RecNet.Application/DependencyInjection.cs | 7 +- .../Neutrino/AuthorizeNeutrinoCommand.cs | 5 ++ .../Neutrino/INeutrinoAuthorizationService.cs | 8 +++ .../Neutrino/NeutrinoAuthorizationFailure.cs | 7 ++ .../Neutrino/NeutrinoAuthorizationResult.cs | 17 +++++ .../Neutrino/NeutrinoAuthorizationService.cs | 30 ++++++++ .../Profiles/IProfileService.cs | 8 +++ .../Profiles/LoginProfileCommand.cs | 9 +++ .../Profiles/LoginProfileResult.cs | 33 +++++++++ RecNet.Application/Profiles/ProfileDto.cs | 8 +-- RecNet.Application/Profiles/ProfileService.cs | 69 +++++++++++++++++++ RecNet.Domain/Entities/Profiles/Profile.cs | 12 +++- .../Services/Tokens/TokenVerifyResult.cs | 21 ------ RecNet.Infrastructure/DependencyInjection.cs | 3 +- .../RecNet.Infrastructure.csproj | 1 + .../Services/Configuration/ConfigService.cs | 2 +- .../Services/Tokens/TokenService.cs | 11 ++- 25 files changed, 274 insertions(+), 125 deletions(-) rename {RecNet.Domain/Services/Configuration => RecNet.Application/Common/Interfaces}/IConfigService.cs (81%) rename {RecNet.Domain/Services/Tokens => RecNet.Application/Common/Interfaces}/ITokenService.cs (67%) rename {RecNet.Domain/Services => RecNet.Application/Common}/Tokens/TokenResult.cs (64%) create mode 100644 RecNet.Application/Common/Tokens/TokenVerifyResult.cs create mode 100644 RecNet.Application/Neutrino/AuthorizeNeutrinoCommand.cs create mode 100644 RecNet.Application/Neutrino/INeutrinoAuthorizationService.cs create mode 100644 RecNet.Application/Neutrino/NeutrinoAuthorizationFailure.cs create mode 100644 RecNet.Application/Neutrino/NeutrinoAuthorizationResult.cs create mode 100644 RecNet.Application/Neutrino/NeutrinoAuthorizationService.cs create mode 100644 RecNet.Application/Profiles/IProfileService.cs create mode 100644 RecNet.Application/Profiles/LoginProfileCommand.cs create mode 100644 RecNet.Application/Profiles/LoginProfileResult.cs create mode 100644 RecNet.Application/Profiles/ProfileService.cs delete mode 100644 RecNet.Domain/Services/Tokens/TokenVerifyResult.cs diff --git a/API/Contracts/Profiles/Responses/LoginResponse.cs b/API/Contracts/Profiles/Responses/LoginResponse.cs index 76d0d3b..a07529b 100644 --- a/API/Contracts/Profiles/Responses/LoginResponse.cs +++ b/API/Contracts/Profiles/Responses/LoginResponse.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; using RecNet.Application.Profiles; namespace API.Contracts.Profiles.Responses; @@ -7,10 +7,10 @@ public class LoginResponse { [JsonPropertyName("Profile")] public required ProfileDTO Profile { get; set; } - + [JsonPropertyName("AccessToken")] public required string AccessToken { get; set; } - + [JsonPropertyName("ExpiresIn")] public required int ExpiresIn { get; set; } -} \ No newline at end of file +} diff --git a/API/Controllers/Config/V1/ConfigController.cs b/API/Controllers/Config/V1/ConfigController.cs index dfdc90b..3093725 100644 --- a/API/Controllers/Config/V1/ConfigController.cs +++ b/API/Controllers/Config/V1/ConfigController.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using RecNet.Domain.Services.Configuration; +using RecNet.Application.Common.Interfaces; namespace API.Controllers.Config.V1; diff --git a/API/Controllers/Neutrino/NeutrinoController.cs b/API/Controllers/Neutrino/NeutrinoController.cs index 111148f..b8e2965 100644 --- a/API/Controllers/Neutrino/NeutrinoController.cs +++ b/API/Controllers/Neutrino/NeutrinoController.cs @@ -1,28 +1,23 @@ -using System.Security.Claims; using System.Text.Json; using API.Contracts.Neutrino.Enums; using API.Contracts.Neutrino.Requests; using API.Contracts.Neutrino.Responses; using Microsoft.AspNetCore.Mvc; -using RecNet.Domain.Repositories; -using RecNet.Domain.Services.Tokens; +using RecNet.Application.Neutrino; namespace API.Controllers.Neutrino; [Route("[controller]")] [ApiController] -public class NeutrinoController( - IProfileRepository profileRepository, - ITokenService tokenService) : ControllerBase +public class NeutrinoController(INeutrinoAuthorizationService authorizationService) : ControllerBase { - // This route will be kind of abysmal so bare with it. + // Photon sends the request with Content-Type: text/plain instead of application/json. [Route("authorize")] - public async Task> AuthorizeNeutrinoAsync() + public async Task> AuthorizeNeutrinoAsync( + CancellationToken ct) { - // Photon sends the request with Content-Type: text/plain instead of application/json - // This is a really janky workaround since we can't use [FromBody] to automatically parse the data. using var reader = new StreamReader(Request.Body); - string rawText = await reader.ReadToEndAsync(); + string rawText = await reader.ReadToEndAsync(ct); NeutrinoAuthenticateRequest? request; @@ -41,42 +36,30 @@ public class NeutrinoController( AuthenticationResultCode.InvalidParameters)); } - // 1. Check if request parameters aren't empty - if (request is null || - request.ProfileId == Guid.Empty || - string.IsNullOrWhiteSpace(request.AccessToken)) + if (request is null) return Ok( NeutrinoAuthenticateResponse.Failure( AuthenticationResultCode.InvalidParameters)); - // 2. Verify accessToken is valid - var tokenVerifyResult = tokenService.VerifyToken(request.AccessToken); - if (tokenVerifyResult.IsError || !TryGetProfileId(tokenVerifyResult.Claims, out var tokenAccountId)) - return Ok( - NeutrinoAuthenticateResponse.Failure( - AuthenticationResultCode.AuthenticationFailedWrongCredentials)); + var result = await authorizationService.AuthorizeAsync( + new AuthorizeNeutrinoCommand( + request.ProfileId, + request.AccessToken), + ct); - // 3. Verify that the accountId from the request matches the one from the token - if (tokenAccountId != request.ProfileId) + if (!result.Succeeded) return Ok( NeutrinoAuthenticateResponse.Failure( - AuthenticationResultCode.AuthenticationFailedWrongCredentials)); - - // 4. Get the profile to check if it exists, and for the name - var profile = await profileRepository.GetByIdAsync(request.ProfileId); - if (profile is null) - return Ok( - NeutrinoAuthenticateResponse.Failure( - AuthenticationResultCode.AuthenticationFailedWrongCredentials)); + MapFailure(result.Failure))); return Ok( NeutrinoAuthenticateResponse.Success( - userId: request.ProfileId.ToString(), - nickname: profile.Name - ) - ); + result.UserId!, + result.Nickname!)); } - private static bool TryGetProfileId(IEnumerable claims, out Guid accountId) - => Guid.TryParse(claims.FirstOrDefault(c => c.Type == "sub")?.Value, out accountId); -} \ No newline at end of file + private static AuthenticationResultCode MapFailure(NeutrinoAuthorizationFailure? failure) + => failure == NeutrinoAuthorizationFailure.InvalidParameters + ? AuthenticationResultCode.InvalidParameters + : AuthenticationResultCode.AuthenticationFailedWrongCredentials; +} diff --git a/API/Controllers/Profiles/V1/ProfilesController.cs b/API/Controllers/Profiles/V1/ProfilesController.cs index 39e3a22..ca5f46f 100644 --- a/API/Controllers/Profiles/V1/ProfilesController.cs +++ b/API/Controllers/Profiles/V1/ProfilesController.cs @@ -1,79 +1,53 @@ using API.Contracts.Profiles.Responses; -using AutoMapper; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using RecNet.Application.Profiles; -using RecNet.Domain.Repositories; -using RecNet.Domain.Services.Tokens; using LoginRequest = API.Contracts.Profiles.Requests.LoginRequest; -using Profile = RecNet.Domain.Entities.Profiles.Profile; namespace API.Controllers.Profiles.V1; [Route("api/[controller]/v1")] [ApiController] -[Authorize] -public class ProfilesController( - IProfileRepository profileRepository, - IMapper mapper, - ITokenService tokenService) : ControllerBase +public class ProfilesController(IProfileService profileService) : ControllerBase { [HttpGet("{id:guid}")] public async Task> GetProfile( Guid id, CancellationToken ct) { - var profile = await profileRepository.GetByIdAsync(id, ct); + var profile = await profileService.GetProfileAsync(id, ct); if (profile == null) return NotFound(); - return mapper.Map(profile); + return profile; } [HttpGet("bulk")] public async Task> GetBulkProfiles( [FromQuery(Name = "id")] List ids, CancellationToken ct) - { - var profiles = await profileRepository.GetByIdsAsync(ids, ct); + => await profileService.GetProfilesAsync(ids, ct); - return mapper.Map>(profiles); - } - - // TODO: Implement - [AllowAnonymous] [HttpPost("login")] public async Task> Login( [FromBody] LoginRequest request, CancellationToken ct) { - var name = GenerateRandomName(); - - var profile = await profileRepository.GetByPlatform(request.PlatformType, request.PlatformId, ct); - if (profile is null) - { - profile = Profile.Create(name, request.PlatformType, request.PlatformId); - await profileRepository.AddAsync(profile, ct); - } + var result = await profileService.LoginAsync( + new LoginProfileCommand( + request.DeviceId, + request.PlatformType, + request.PlatformId, + request.PlatformAuthentication), + ct); - profile.AddDeviceId(request.DeviceId); - - await profileRepository.SaveChangesAsync(ct); - - if (profile.IsBanned) - return BadRequest(); - - var token = tokenService.GenerateProfileToken(profile); + if (!result.Succeeded) + return BadRequest(result.Message); return new LoginResponse { - Profile = mapper.Map(profile), - AccessToken = token.AccessToken, - ExpiresIn = token.ExpiresIn + Profile = result.Profile!, + AccessToken = result.AccessToken!, + ExpiresIn = result.ExpiresIn }; } - - // TODO: REPLACE WITH BETTER GENERATOR, THIS IS TEMP. - private static string GenerateRandomName() - => string.Concat("rr", Guid.NewGuid().ToString("N").AsSpan(0, 18)); -} +} \ No newline at end of file diff --git a/RecNet.Domain/Services/Configuration/IConfigService.cs b/RecNet.Application/Common/Interfaces/IConfigService.cs similarity index 81% rename from RecNet.Domain/Services/Configuration/IConfigService.cs rename to RecNet.Application/Common/Interfaces/IConfigService.cs index df62373..998cefa 100644 --- a/RecNet.Domain/Services/Configuration/IConfigService.cs +++ b/RecNet.Application/Common/Interfaces/IConfigService.cs @@ -1,4 +1,4 @@ -namespace RecNet.Domain.Services.Configuration; +namespace RecNet.Application.Common.Interfaces; public interface IConfigService { diff --git a/RecNet.Domain/Services/Tokens/ITokenService.cs b/RecNet.Application/Common/Interfaces/ITokenService.cs similarity index 67% rename from RecNet.Domain/Services/Tokens/ITokenService.cs rename to RecNet.Application/Common/Interfaces/ITokenService.cs index 43f7834..b47859b 100644 --- a/RecNet.Domain/Services/Tokens/ITokenService.cs +++ b/RecNet.Application/Common/Interfaces/ITokenService.cs @@ -1,6 +1,7 @@ +using RecNet.Application.Common.Tokens; using RecNet.Domain.Entities.Profiles; -namespace RecNet.Domain.Services.Tokens; +namespace RecNet.Application.Common.Interfaces; public interface ITokenService { diff --git a/RecNet.Domain/Services/Tokens/TokenResult.cs b/RecNet.Application/Common/Tokens/TokenResult.cs similarity index 64% rename from RecNet.Domain/Services/Tokens/TokenResult.cs rename to RecNet.Application/Common/Tokens/TokenResult.cs index 47a40e2..5f4b8a0 100644 --- a/RecNet.Domain/Services/Tokens/TokenResult.cs +++ b/RecNet.Application/Common/Tokens/TokenResult.cs @@ -1,4 +1,4 @@ -namespace RecNet.Domain.Services.Tokens; +namespace RecNet.Application.Common.Tokens; public sealed record TokenResult( string AccessToken, diff --git a/RecNet.Application/Common/Tokens/TokenVerifyResult.cs b/RecNet.Application/Common/Tokens/TokenVerifyResult.cs new file mode 100644 index 0000000..ed3ff82 --- /dev/null +++ b/RecNet.Application/Common/Tokens/TokenVerifyResult.cs @@ -0,0 +1,12 @@ +namespace RecNet.Application.Common.Tokens; + +public sealed record TokenVerifyResult( + bool Succeeded, + Guid? ProfileId) +{ + public static TokenVerifyResult Success(Guid profileId) + => new(true, profileId); + + public static TokenVerifyResult Failure() + => new(false, null); +} diff --git a/RecNet.Application/DependencyInjection.cs b/RecNet.Application/DependencyInjection.cs index 5b7ee97..d4cbfdc 100644 --- a/RecNet.Application/DependencyInjection.cs +++ b/RecNet.Application/DependencyInjection.cs @@ -1,4 +1,6 @@ using Microsoft.Extensions.DependencyInjection; +using RecNet.Application.Neutrino; +using RecNet.Application.Profiles; namespace RecNet.Application; @@ -7,9 +9,8 @@ public static class DependencyInjection public static IServiceCollection AddApplication(this IServiceCollection services) { services.AddAutoMapper(_ => { }, typeof(DependencyInjection).Assembly); - - // Usually there would be CQRS, but I'm rushing out this server, so I didn't feel like adding it. - // Enjoy a VERY barebones application layer :D + services.AddScoped(); + services.AddScoped(); return services; } diff --git a/RecNet.Application/Neutrino/AuthorizeNeutrinoCommand.cs b/RecNet.Application/Neutrino/AuthorizeNeutrinoCommand.cs new file mode 100644 index 0000000..46d31cb --- /dev/null +++ b/RecNet.Application/Neutrino/AuthorizeNeutrinoCommand.cs @@ -0,0 +1,5 @@ +namespace RecNet.Application.Neutrino; + +public sealed record AuthorizeNeutrinoCommand( + Guid ProfileId, + string AccessToken); diff --git a/RecNet.Application/Neutrino/INeutrinoAuthorizationService.cs b/RecNet.Application/Neutrino/INeutrinoAuthorizationService.cs new file mode 100644 index 0000000..adca19b --- /dev/null +++ b/RecNet.Application/Neutrino/INeutrinoAuthorizationService.cs @@ -0,0 +1,8 @@ +namespace RecNet.Application.Neutrino; + +public interface INeutrinoAuthorizationService +{ + Task AuthorizeAsync( + AuthorizeNeutrinoCommand command, + CancellationToken ct = default); +} diff --git a/RecNet.Application/Neutrino/NeutrinoAuthorizationFailure.cs b/RecNet.Application/Neutrino/NeutrinoAuthorizationFailure.cs new file mode 100644 index 0000000..447c545 --- /dev/null +++ b/RecNet.Application/Neutrino/NeutrinoAuthorizationFailure.cs @@ -0,0 +1,7 @@ +namespace RecNet.Application.Neutrino; + +public enum NeutrinoAuthorizationFailure +{ + InvalidParameters, + AuthenticationFailed +} diff --git a/RecNet.Application/Neutrino/NeutrinoAuthorizationResult.cs b/RecNet.Application/Neutrino/NeutrinoAuthorizationResult.cs new file mode 100644 index 0000000..ea3bf59 --- /dev/null +++ b/RecNet.Application/Neutrino/NeutrinoAuthorizationResult.cs @@ -0,0 +1,17 @@ +namespace RecNet.Application.Neutrino; + +public sealed record NeutrinoAuthorizationResult( + bool Succeeded, + NeutrinoAuthorizationFailure? Failure, + string? UserId, + string? Nickname) +{ + public static NeutrinoAuthorizationResult Success(Guid userId, string nickname) + => new(true, null, userId.ToString(), nickname); + + public static NeutrinoAuthorizationResult InvalidParameters() + => new(false, NeutrinoAuthorizationFailure.InvalidParameters, null, null); + + public static NeutrinoAuthorizationResult AuthenticationFailed() + => new(false, NeutrinoAuthorizationFailure.AuthenticationFailed, null, null); +} diff --git a/RecNet.Application/Neutrino/NeutrinoAuthorizationService.cs b/RecNet.Application/Neutrino/NeutrinoAuthorizationService.cs new file mode 100644 index 0000000..7149954 --- /dev/null +++ b/RecNet.Application/Neutrino/NeutrinoAuthorizationService.cs @@ -0,0 +1,30 @@ +using RecNet.Application.Common.Interfaces; +using RecNet.Domain.Repositories; + +namespace RecNet.Application.Neutrino; + +public class NeutrinoAuthorizationService( + IProfileRepository profileRepository, + ITokenService tokenService) : INeutrinoAuthorizationService +{ + public async Task AuthorizeAsync( + AuthorizeNeutrinoCommand command, + CancellationToken ct = default) + { + if (command.ProfileId == Guid.Empty || string.IsNullOrWhiteSpace(command.AccessToken)) + return NeutrinoAuthorizationResult.InvalidParameters(); + + var tokenVerifyResult = tokenService.VerifyToken(command.AccessToken); + if (!tokenVerifyResult.Succeeded || tokenVerifyResult.ProfileId is null) + return NeutrinoAuthorizationResult.AuthenticationFailed(); + + if (tokenVerifyResult.ProfileId.Value != command.ProfileId) + return NeutrinoAuthorizationResult.AuthenticationFailed(); + + var profile = await profileRepository.GetByIdAsync(command.ProfileId, ct); + if (profile is null) + return NeutrinoAuthorizationResult.AuthenticationFailed(); + + return NeutrinoAuthorizationResult.Success(profile.ProfileId, profile.Name); + } +} diff --git a/RecNet.Application/Profiles/IProfileService.cs b/RecNet.Application/Profiles/IProfileService.cs new file mode 100644 index 0000000..2c1b91b --- /dev/null +++ b/RecNet.Application/Profiles/IProfileService.cs @@ -0,0 +1,8 @@ +namespace RecNet.Application.Profiles; + +public interface IProfileService +{ + Task GetProfileAsync(Guid profileId, CancellationToken ct = default); + Task> GetProfilesAsync(List profileIds, CancellationToken ct = default); + Task LoginAsync(LoginProfileCommand command, CancellationToken ct = default); +} diff --git a/RecNet.Application/Profiles/LoginProfileCommand.cs b/RecNet.Application/Profiles/LoginProfileCommand.cs new file mode 100644 index 0000000..7359958 --- /dev/null +++ b/RecNet.Application/Profiles/LoginProfileCommand.cs @@ -0,0 +1,9 @@ +using RecNet.Domain.Common; + +namespace RecNet.Application.Profiles; + +public sealed record LoginProfileCommand( + string DeviceId, + PlatformType PlatformType, + string PlatformId, + string PlatformAuthentication); diff --git a/RecNet.Application/Profiles/LoginProfileResult.cs b/RecNet.Application/Profiles/LoginProfileResult.cs new file mode 100644 index 0000000..0814c02 --- /dev/null +++ b/RecNet.Application/Profiles/LoginProfileResult.cs @@ -0,0 +1,33 @@ +namespace RecNet.Application.Profiles; + + + +public class LoginProfileResult +{ + public bool Succeeded { get; set; } + public string? Message { get; set; } + + public ProfileDTO? Profile { get; set; } + public string? AccessToken { get; set; } + public int ExpiresIn { get; set; } + + public static LoginProfileResult Success(ProfileDTO profile, string accessToken, int expiresIn) + { + return new LoginProfileResult + { + Succeeded = true, + Profile = profile, + AccessToken = accessToken, + ExpiresIn = expiresIn + }; + } + + public static LoginProfileResult Fail(string? message = null) + { + return new LoginProfileResult + { + Succeeded = false, + Message = message + }; + } +} diff --git a/RecNet.Application/Profiles/ProfileDto.cs b/RecNet.Application/Profiles/ProfileDto.cs index 30b276e..7da37f7 100644 --- a/RecNet.Application/Profiles/ProfileDto.cs +++ b/RecNet.Application/Profiles/ProfileDto.cs @@ -1,12 +1,8 @@ -using System.Text.Json.Serialization; - namespace RecNet.Application.Profiles; public class ProfileDTO { - [JsonPropertyName("ProfileId")] public Guid ProfileId { get; init; } - - [JsonPropertyName("Name")] + public required string Name { get; init; } -} \ No newline at end of file +} diff --git a/RecNet.Application/Profiles/ProfileService.cs b/RecNet.Application/Profiles/ProfileService.cs new file mode 100644 index 0000000..5bf93d3 --- /dev/null +++ b/RecNet.Application/Profiles/ProfileService.cs @@ -0,0 +1,69 @@ +using AutoMapper; +using RecNet.Application.Common.Interfaces; +using RecNet.Domain.Repositories; +using ProfileEntity = RecNet.Domain.Entities.Profiles.Profile; + +namespace RecNet.Application.Profiles; + +public class ProfileService( + IProfileRepository profileRepository, + ITokenService tokenService, + IMapper mapper) : IProfileService +{ + public async Task GetProfileAsync(Guid profileId, CancellationToken ct = default) + { + var profile = await profileRepository.GetByIdAsync(profileId, ct); + + return profile is null + ? null + : mapper.Map(profile); + } + + public async Task> GetProfilesAsync( + List profileIds, + CancellationToken ct = default) + { + var profiles = await profileRepository.GetByIdsAsync(profileIds, ct); + + return mapper.Map>(profiles); + } + + public async Task LoginAsync( + LoginProfileCommand command, + CancellationToken ct = default) + { + var profile = await profileRepository.GetByPlatform( + command.PlatformType, + command.PlatformId, + ct); + + if (profile is null) + { + profile = ProfileEntity.Create( + GenerateRandomName(), + command.PlatformType, + command.PlatformId); + + await profileRepository.AddAsync(profile, ct); + } + + profile.AddDeviceId(command.DeviceId); + + await profileRepository.SaveChangesAsync(ct); + + if (profile.IsBanned) + return LoginProfileResult.Fail("Profile is banned"); + + var token = tokenService.GenerateProfileToken(profile); + + return LoginProfileResult.Success( + profile: mapper.Map(profile), + accessToken: token.AccessToken, + expiresIn: token.ExpiresIn + ); + } + + // Temporary until platform/user-name verification becomes a real use case. + private static string GenerateRandomName() + => string.Concat("rr", Guid.NewGuid().ToString("N").AsSpan(0, 18)); +} \ No newline at end of file diff --git a/RecNet.Domain/Entities/Profiles/Profile.cs b/RecNet.Domain/Entities/Profiles/Profile.cs index d3a758d..6b60705 100644 --- a/RecNet.Domain/Entities/Profiles/Profile.cs +++ b/RecNet.Domain/Entities/Profiles/Profile.cs @@ -61,7 +61,17 @@ public class Profile DeviceIds.Add(deviceId); } - + public void Ban() + => IsBanned = true; + + public void Unban() + => IsBanned = false; + + public void GrantModerator() + => IsModerator = true; + + public void RevokeModerator() + => IsModerator = false; private static string RequireValue(string value, string parameterName) => !string.IsNullOrWhiteSpace(value) ? value : throw new DomainException($"{parameterName} is required."); diff --git a/RecNet.Domain/Services/Tokens/TokenVerifyResult.cs b/RecNet.Domain/Services/Tokens/TokenVerifyResult.cs deleted file mode 100644 index 0013b8e..0000000 --- a/RecNet.Domain/Services/Tokens/TokenVerifyResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Security.Claims; - -namespace RecNet.Domain.Services.Tokens; - -public class TokenVerifyResult -{ - public bool IsError { get; set; } - public IEnumerable Claims { get; set; } - - private TokenVerifyResult(bool isError, IEnumerable? claims) - { - IsError = isError; - Claims = claims ?? []; - } - - public static TokenVerifyResult Success(IEnumerable claims) - => new(false, claims); - - public static TokenVerifyResult Failure() - => new(true, null); -} \ No newline at end of file diff --git a/RecNet.Infrastructure/DependencyInjection.cs b/RecNet.Infrastructure/DependencyInjection.cs index 7d0aaf0..07f260a 100644 --- a/RecNet.Infrastructure/DependencyInjection.cs +++ b/RecNet.Infrastructure/DependencyInjection.cs @@ -1,8 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using RecNet.Application.Common.Interfaces; using RecNet.Domain.Repositories; -using RecNet.Domain.Services.Configuration; -using RecNet.Domain.Services.Tokens; using RecNet.Infrastructure.Persistence; using RecNet.Infrastructure.Persistence.Repositories; using RecNet.Infrastructure.Services.Configuration; diff --git a/RecNet.Infrastructure/RecNet.Infrastructure.csproj b/RecNet.Infrastructure/RecNet.Infrastructure.csproj index f717ea8..65415a4 100644 --- a/RecNet.Infrastructure/RecNet.Infrastructure.csproj +++ b/RecNet.Infrastructure/RecNet.Infrastructure.csproj @@ -12,6 +12,7 @@ + diff --git a/RecNet.Infrastructure/Services/Configuration/ConfigService.cs b/RecNet.Infrastructure/Services/Configuration/ConfigService.cs index 383d0c4..f2536e8 100644 --- a/RecNet.Infrastructure/Services/Configuration/ConfigService.cs +++ b/RecNet.Infrastructure/Services/Configuration/ConfigService.cs @@ -1,6 +1,6 @@ using System.Text.Json; +using RecNet.Application.Common.Interfaces; using RecNet.Domain.Repositories; -using RecNet.Domain.Services.Configuration; namespace RecNet.Infrastructure.Services.Configuration; diff --git a/RecNet.Infrastructure/Services/Tokens/TokenService.cs b/RecNet.Infrastructure/Services/Tokens/TokenService.cs index 2a7d66c..5aa1717 100644 --- a/RecNet.Infrastructure/Services/Tokens/TokenService.cs +++ b/RecNet.Infrastructure/Services/Tokens/TokenService.cs @@ -3,8 +3,9 @@ using System.Security.Claims; using System.Text; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; +using RecNet.Application.Common.Interfaces; +using RecNet.Application.Common.Tokens; using RecNet.Domain.Entities.Profiles; -using RecNet.Domain.Services.Tokens; namespace RecNet.Infrastructure.Services.Tokens; @@ -71,8 +72,14 @@ public class TokenService(IOptions options) : ITokenService try { var principal = tokenHandler.ValidateToken(token, validationParameters, out _); + + var profileIdClaim = + principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value ?? + principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; - return TokenVerifyResult.Success(principal.Claims); + return Guid.TryParse(profileIdClaim, out var profileId) + ? TokenVerifyResult.Success(profileId) + : TokenVerifyResult.Failure(); } catch (SecurityTokenException) {