diff --git a/.github/workflows/compose-build.yml b/.github/workflows/compose-build.yml index 6eb6b5a..2dd5721 100644 --- a/.github/workflows/compose-build.yml +++ b/.github/workflows/compose-build.yml @@ -26,14 +26,14 @@ jobs: run: | echo "Checking health..." for i in {1..10}; do - curl --fail http://0.0.0.0:8082/health && break || sleep 5 + curl --fail http://0.0.0.0:8000/auth/health && break || sleep 5 done - curl --fail http://0.0.0.0:8082/health || exit 1 + curl --fail http://0.0.0.0:8000/auth/health || exit 1 - name: Health Check PostService run: | echo "Checking health..." for i in {1..10}; do - curl --fail http://0.0.0.0:8081/health && break || sleep 5 + curl --fail http://0.0.0.0:8000/post/health && break || sleep 5 done - curl --fail http://0.0.0.0:8081/health || exit 1 \ No newline at end of file + curl --fail http://0.0.0.0:8000/post/health || exit 1 \ No newline at end of file diff --git a/FitnessApp.sln b/FitnessApp.sln index d1498e5..457ef88 100644 --- a/FitnessApp.sln +++ b/FitnessApp.sln @@ -76,6 +76,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build-and-test", "build-and EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.DTO", "backend\src\Shared\Shared.DTO\Shared.DTO.csproj", "{334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateway", "backend\src\Gateway", "{DC9B9F00-24BF-4AD3-B45A-38BFCFC309D7}" + ProjectSection(SolutionItems) = preProject + backend\src\Gateway\Dockerfile = backend\src\Gateway\Dockerfile + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gateway", "backend\src\Gateway\Gateway\Gateway.csproj", "{830B4F05-7C89-4C0F-B27D-B1008B605E79}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -142,6 +149,10 @@ Global {334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}.Release|Any CPU.Build.0 = Release|Any CPU + {830B4F05-7C89-4C0F-B27D-B1008B605E79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {830B4F05-7C89-4C0F-B27D-B1008B605E79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {830B4F05-7C89-4C0F-B27D-B1008B605E79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {830B4F05-7C89-4C0F-B27D-B1008B605E79}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {2E44C019-ED30-4671-A878-D2F7B6071024} = {84AE3F00-9A59-4A8D-A737-67AB74E5D1FD} @@ -167,5 +178,7 @@ Global {499BA3B4-A81F-4079-B256-CC04927BB06D} = {D5A12D19-86E3-4E83-8BF0-D9AF8C2477F3} {BC8EDBE4-B8BD-4934-B096-0C2E1C1E84C7} = {499BA3B4-A81F-4079-B256-CC04927BB06D} {334DF17C-BD69-47B9-B4D0-ACB5AD6223CF} = {112E9C63-BF62-49C1-AD19-03855535225B} + {DC9B9F00-24BF-4AD3-B45A-38BFCFC309D7} = {B7E4E4CF-E7A9-4DAC-807E-DFF17553BAE8} + {830B4F05-7C89-4C0F-B27D-B1008B605E79} = {DC9B9F00-24BF-4AD3-B45A-38BFCFC309D7} EndGlobalSection EndGlobal diff --git a/backend/src/AuthService/AuthService.Api/GraphQL/Mutation.cs b/backend/src/AuthService/AuthService.Api/GraphQL/Mutation.cs index fadd74e..acd50d4 100644 --- a/backend/src/AuthService/AuthService.Api/GraphQL/Mutation.cs +++ b/backend/src/AuthService/AuthService.Api/GraphQL/Mutation.cs @@ -20,6 +20,17 @@ public Mutation(IHttpContextAccessor httpContextAccessor) _httpContextAccessor = httpContextAccessor; } + + /* ToDo: When all the services are running in the docker that's it + System.Net.Http.HttpClient.KeycloakClient.LogicalHandler[100] + 2025-01-05 22:30:39 Start processing HTTP request POST http://keycloak:8080/realms/fitness-app-realm/protocol/openid-connect/token + 2025-01-05 22:30:39 info: System.Net.Http.HttpClient.KeycloakClient.ClientHandler[100] + 2025-01-05 22:30:39 Sending HTTP request POST http://keycloak:8080/realms/fitness-app-realm/protocol/openid-connect/token + 2025-01-05 22:30:39 info: System.Net.Http.HttpClient.KeycloakClient.ClientHandler[101] + 2025-01-05 22:30:39 Received HTTP response headers after 91.8503ms - 404 + 2025-01-05 22:30:39 info: System.Net.Http.HttpClient.KeycloakClient.LogicalHandler[101] + 2025-01-05 22:30:39 End processing HTTP request after 100.8284ms - 404 + */ public async Task Register(RegisterDto input, [Service] RegisterCommandHandler registerCommandHandler) { var command = new RegisterCommand(input); diff --git a/backend/src/AuthService/AuthService.Api/Properties/launchSettings.json b/backend/src/AuthService/AuthService.Api/Properties/launchSettings.json index 1a0ab26..a626a88 100644 --- a/backend/src/AuthService/AuthService.Api/Properties/launchSettings.json +++ b/backend/src/AuthService/AuthService.Api/Properties/launchSettings.json @@ -14,7 +14,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "graphql", - "applicationUrl": "http://localhost:8082", + "applicationUrl": "http://localhost:8001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -24,7 +24,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "graphql", - "applicationUrl": "https://localhost:7165;http://localhost:8082", + "applicationUrl": "https://localhost:7165;http://localhost:8001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/backend/src/AuthService/AuthService.Api/appsettings.json b/backend/src/AuthService/AuthService.Api/appsettings.json index 398010b..37c1c7f 100644 --- a/backend/src/AuthService/AuthService.Api/appsettings.json +++ b/backend/src/AuthService/AuthService.Api/appsettings.json @@ -8,15 +8,15 @@ "AllowedHosts": "*", "AllowedOrigins": [ "http://localhost:4000", "http://localhost:4200", "http://localhost:8080" ], "DatabaseConnectionString": "Host=localhost;Port=5432;Username=postgres;Password=mysecretpasswordfordevelopment;Database=AuthDb", - "WebHostUrl": "http://0.0.0.0:8082", + "WebHostUrl": "http://0.0.0.0:8001", "Keycloak": { "Url": "http://localhost:8080", "Realm": "fitness-app-realm", "ClientId": "fitness-app-client", - "ClientSecret": "4mTuelQIWNfLw3HGfE3xAD3Bmyb5OHWN", + "ClientSecret": "7lGtugT2Dmwgi6gYJQnGZf5aO0Y9tzlh", "AdminUsername": "admin", "AdminPassword": "admin", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Q/E+u/w56fl1C67jv2glu/wjy50re4UbVqJHX+T84hN8lGjiHRxqmEgdsyArhiXK0XBbbolPJ+jLTXY7KAKXXtsCjNOV/WLCKFi9Gdn2vgrmD+g2BatpRvO33nytwlDZSkZSnFpTY3io6ZcMCB+YQK5i+2QOz1gahJt5Dac+bwZ96d3x+dEP+0lZ3+3VTe0bKWRMDked2f8E4K9mvywyUEf3Ihe/2YVhXZyUnshMrRmn6ZZ4DvpQcrHMtGMCBiB5N6pllxBu5XAcxYPRxZtx+q4GSU1bCA9es4RNgLTBGP8GQXVVqlq80j8oxR1SSigqLNbA7qcGi4+rmWg/ohdkQIDAQAB" + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuQXYfCuREUGjALqZ/iPLvqPAEBcCP4Knbwixdbe+jwhrMjX7n/azi+htI3egSAjlSlVvc5olSU2ykydCD2BqBgxMpNGoTa8dp96vDdTeMBD/semyOiSmJq4tqeKw2n7cjpI7jvoDP/HvH5b90SIsmmmUEHnjie3fakb0VccQxJdQXolNlCZfBytD0L8bBmuvPlQdgSoOxCuRakVz2NBErjx0SRxn0EltHdu1dEA0Ya+I7jeX3ZgbKe+R/oIuggYnnlh//wNXsfHGKKYsxxp/FQjItS2oq29aGPjTA6t3nt0Rysg66IbNxdMFg1nfREFKV7Qpb2IsC01k0s/r/+2BlwIDAQAB" }, "RabbitMq": { "Host": "localhost", diff --git a/backend/src/AuthService/Dockerfile b/backend/src/AuthService/Dockerfile index 89ad510..d012ee2 100644 --- a/backend/src/AuthService/Dockerfile +++ b/backend/src/AuthService/Dockerfile @@ -1,6 +1,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app -EXPOSE 8081 +EXPOSE 8001 RUN apt-get update && apt-get install -y curl && apt-get clean diff --git a/backend/src/Gateway/Dockerfile b/backend/src/Gateway/Dockerfile new file mode 100644 index 0000000..724a972 --- /dev/null +++ b/backend/src/Gateway/Dockerfile @@ -0,0 +1,28 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +EXPOSE 8000 + +RUN apt-get update && apt-get install -y curl && apt-get clean + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build + +WORKDIR /src + +COPY Gateway/Gateway/Gateway.csproj Gateway/Gateway/ + +RUN dotnet restore Gateway/Gateway/Gateway.csproj + +COPY . . + +WORKDIR /src/Gateway/Gateway + +RUN dotnet build Gateway.csproj -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish Gateway.csproj -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . + +ENTRYPOINT ["dotnet", "Gateway.dll"] \ No newline at end of file diff --git a/backend/src/Gateway/Gateway/Constants/AppSettingsConstants.cs b/backend/src/Gateway/Gateway/Constants/AppSettingsConstants.cs new file mode 100644 index 0000000..cbe2b14 --- /dev/null +++ b/backend/src/Gateway/Gateway/Constants/AppSettingsConstants.cs @@ -0,0 +1,8 @@ +namespace Gateway.Constants; + +public class AppSettingsConstants +{ + public const string WebHostUrl = "WebHostUrl"; + public const string AllowedOrigins = "AllowedOrigins"; + public const string ReverseProxy = "ReverseProxy"; +} \ No newline at end of file diff --git a/backend/src/Gateway/Gateway/Constants/CorsConstants.cs b/backend/src/Gateway/Gateway/Constants/CorsConstants.cs new file mode 100644 index 0000000..2a9aad7 --- /dev/null +++ b/backend/src/Gateway/Gateway/Constants/CorsConstants.cs @@ -0,0 +1,6 @@ +namespace Gateway.Constants; + +public class CorsConstants +{ + public const string CorsPolicy = "CorsPolicy"; +} \ No newline at end of file diff --git a/backend/src/Gateway/Gateway/Gateway.csproj b/backend/src/Gateway/Gateway/Gateway.csproj new file mode 100644 index 0000000..c95b170 --- /dev/null +++ b/backend/src/Gateway/Gateway/Gateway.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/backend/src/Gateway/Gateway/Program.cs b/backend/src/Gateway/Gateway/Program.cs new file mode 100644 index 0000000..d9a1fd7 --- /dev/null +++ b/backend/src/Gateway/Gateway/Program.cs @@ -0,0 +1,42 @@ +using Gateway.Constants; + +var builder = WebApplication.CreateBuilder(args); + +var allowedOrigins = builder.Configuration.GetSection(AppSettingsConstants.AllowedOrigins).Get(); +var hostingUrl = builder.Configuration[AppSettingsConstants.WebHostUrl]; +var reverseProxyConfig = builder.Configuration.GetSection(AppSettingsConstants.ReverseProxy); + +builder.WebHost.UseUrls(hostingUrl ?? throw new ArgumentNullException(nameof(hostingUrl), "Hosting URL is not configured.")); + +builder.Services + .AddCors(options => + { + options.AddPolicy(CorsConstants.CorsPolicy, policyBuilder => + { + policyBuilder + .WithOrigins(allowedOrigins ?? throw new ArgumentNullException(nameof(allowedOrigins), + "Allowed Origin URLs are not configured.")) + .AllowCredentials() + .AllowAnyHeader() + .AllowAnyMethod(); + }); + }); + +builder.Services + .AddReverseProxy() + .LoadFromConfig(reverseProxyConfig); + +var app = builder.Build(); + +if (!app.Environment.IsDevelopment()) +{ + app.UseHttpsRedirection(); +} + +app.UseCors(CorsConstants.CorsPolicy); + +app.MapReverseProxy(); + +app.MapGet("/health" ,() => Results.Ok("Healthy")); + +app.Run(); \ No newline at end of file diff --git a/backend/src/Gateway/Gateway/Properties/launchSettings.json b/backend/src/Gateway/Gateway/Properties/launchSettings.json new file mode 100644 index 0000000..5947b26 --- /dev/null +++ b/backend/src/Gateway/Gateway/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:37518", + "sslPort": 44340 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "", + "applicationUrl": "http://localhost:8000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "", + "applicationUrl": "https://localhost:7204;http://localhost:8000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/backend/src/Gateway/Gateway/appsettings.json b/backend/src/Gateway/Gateway/appsettings.json new file mode 100644 index 0000000..f51fe42 --- /dev/null +++ b/backend/src/Gateway/Gateway/appsettings.json @@ -0,0 +1,54 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "WebHostUrl": "http://0.0.0.0:8000", + "AllowedOrigins": [ "http://localhost:4200", "http://localhost:8080" ], + "ReverseProxy": { + "Routes": { + "AuthService": { + "ClusterId": "auth-service", + "Match": { + "Path": "/auth/{endpoint:regex(^graphql$|^health$)}" + }, + "Transforms": [ + { + "PathRemovePrefix": "/auth" + } + ] + }, + "PostService": { + "ClusterId": "post-service", + "Match": { + "Path": "/post/{endpoint:regex(^graphql$|^health$)}" + }, + "Transforms": [ + { + "PathRemovePrefix": "/post" + } + ], + "WebSocket": true + } + }, + "Clusters": { + "auth-service": { + "Destinations": { + "destination1": { + "Address": "http://localhost:8001" + } + } + }, + "post-service": { + "Destinations": { + "destination1": { + "Address": "http://localhost:8002" + } + } + } + } + } +} diff --git a/backend/src/PostService/Dockerfile b/backend/src/PostService/Dockerfile index b5de60d..1b4b50d 100644 --- a/backend/src/PostService/Dockerfile +++ b/backend/src/PostService/Dockerfile @@ -1,6 +1,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app -EXPOSE 8081 +EXPOSE 8002 RUN apt-get update && apt-get install -y curl && apt-get clean diff --git a/backend/src/PostService/PostService.Api/GraphQL/Mutation.cs b/backend/src/PostService/PostService.Api/GraphQL/Mutation.cs new file mode 100644 index 0000000..70c80f8 --- /dev/null +++ b/backend/src/PostService/PostService.Api/GraphQL/Mutation.cs @@ -0,0 +1,126 @@ +using System.Security.Claims; +using PostService.Application.Commands.AddComment; +using PostService.Application.Commands.AddLike; +using PostService.Application.Commands.AddPost; +using PostService.Application.Commands.DeleteComment; +using PostService.Application.Commands.DeleteLike; +using PostService.Application.Commands.DeletePost; +using PostService.Application.Commands.UpdatePost; +using PostService.Application.DTOs; + +namespace PostService.Api.GraphQL; + +public class Mutation +{ + private readonly IHttpContextAccessor _httpContextAccessor; + + public Mutation(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public async Task CreatePost(CreatePostDto input, [Service] AddPostCommandHandler addPostCommandHandler) + { + var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var command = new AddPostCommand(input, userId); + + var result = await addPostCommandHandler.HandleAsync(command); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task UpdatePost(UpdatePostDto input, [Service] UpdatePostCommandHandler updatePostCommandHandler) + { + var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var command = new UpdatePostCommand(input, userId); + + var result = await updatePostCommandHandler.HandleAsync(command); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task DeletePost(Guid id, [Service] DeletePostCommandHandler deletePostCommandHandler) + { + var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var command = new DeletePostCommand(id, userId); + + var result = await deletePostCommandHandler.HandleAsync(command); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task CreateComment(CreateCommentDto input, [Service] AddCommentCommandHandler addCommentCommandHandler) + { + var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var command = new AddCommentCommand(input, userId); + + var result = await addCommentCommandHandler.HandleAsync(command); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task DeleteComment(Guid id, Guid postId, [Service] DeleteCommentCommandHandler deleteCommentCommandHandler) + { + var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var command = new DeleteCommentCommand(id, postId, userId); + + var result = await deleteCommentCommandHandler.HandleAsync(command); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task AddLike(Guid postId, [Service] AddLikeCommandHandler addLikeCommandHandler) + { + var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var command = new AddLikeCommand(postId, userId); + + var result = await addLikeCommandHandler.HandleAsync(command); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task DeleteLike(Guid id, Guid postId, [Service] DeleteLikeCommandHandler deleteLikeCommandHandler) + { + var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var command = new DeleteLikeCommand(id, postId, userId); + + var result = await deleteLikeCommandHandler.HandleAsync(command); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } +} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/GraphQL/Mutations/CommentMutation.cs b/backend/src/PostService/PostService.Api/GraphQL/Mutations/CommentMutation.cs deleted file mode 100644 index 32f3e71..0000000 --- a/backend/src/PostService/PostService.Api/GraphQL/Mutations/CommentMutation.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Security.Claims; -using PostService.Application.Commands.AddComment; -using PostService.Application.Commands.DeleteComment; -using PostService.Application.DTOs; - -namespace PostService.Api.GraphQL.Mutations; - -public class CommentMutation -{ - private readonly IHttpContextAccessor _httpContextAccessor; - - public CommentMutation(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - public async Task CreateComment(CreateCommentDto input, [Service] AddCommentCommandHandler addCommentCommandHandler) - { - var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var command = new AddCommentCommand(input, userId); - - var result = await addCommentCommandHandler.HandleAsync(command); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } - - public async Task DeleteComment(Guid id, Guid postId, [Service] DeleteCommentCommandHandler deleteCommentCommandHandler) - { - var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var command = new DeleteCommentCommand(id, postId, userId); - - var result = await deleteCommentCommandHandler.HandleAsync(command); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } -} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/GraphQL/Mutations/LikeMutation.cs b/backend/src/PostService/PostService.Api/GraphQL/Mutations/LikeMutation.cs deleted file mode 100644 index f1ca6b1..0000000 --- a/backend/src/PostService/PostService.Api/GraphQL/Mutations/LikeMutation.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Security.Claims; -using PostService.Application.Commands.AddLike; -using PostService.Application.Commands.DeleteLike; -using PostService.Application.DTOs; - -namespace PostService.Api.GraphQL.Mutations; - -public class LikeMutation -{ - private readonly IHttpContextAccessor _httpContextAccessor; - - public LikeMutation(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - public async Task AddLike(Guid postId, [Service] AddLikeCommandHandler addLikeCommandHandler) - { - var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var command = new AddLikeCommand(postId, userId); - - var result = await addLikeCommandHandler.HandleAsync(command); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } - - public async Task DeleteLike(Guid id, Guid postId, [Service] DeleteLikeCommandHandler deleteLikeCommandHandler) - { - var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var command = new DeleteLikeCommand(id, postId, userId); - - var result = await deleteLikeCommandHandler.HandleAsync(command); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } -} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/GraphQL/Mutations/PostMutation.cs b/backend/src/PostService/PostService.Api/GraphQL/Mutations/PostMutation.cs deleted file mode 100644 index 9617dec..0000000 --- a/backend/src/PostService/PostService.Api/GraphQL/Mutations/PostMutation.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Security.Claims; -using PostService.Application.Commands.AddPost; -using PostService.Application.Commands.DeletePost; -using PostService.Application.Commands.UpdatePost; -using PostService.Application.DTOs; - -namespace PostService.Api.GraphQL.Mutations; - -public class PostMutation -{ - private readonly IHttpContextAccessor _httpContextAccessor; - - public PostMutation(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - public async Task CreatePost(CreatePostDto input, [Service] AddPostCommandHandler addPostCommandHandler) - { - var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var command = new AddPostCommand(input, userId); - - var result = await addPostCommandHandler.HandleAsync(command); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } - - public async Task UpdatePost(UpdatePostDto input, [Service] UpdatePostCommandHandler updatePostCommandHandler) - { - var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var command = new UpdatePostCommand(input, userId); - - var result = await updatePostCommandHandler.HandleAsync(command); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } - - public async Task DeletePost(Guid id, [Service] DeletePostCommandHandler deletePostCommandHandler) - { - var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var command = new DeletePostCommand(id, userId); - - var result = await deletePostCommandHandler.HandleAsync(command); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } -} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/GraphQL/Queries/CommentQuery.cs b/backend/src/PostService/PostService.Api/GraphQL/Queries/CommentQuery.cs deleted file mode 100644 index e7fd0b0..0000000 --- a/backend/src/PostService/PostService.Api/GraphQL/Queries/CommentQuery.cs +++ /dev/null @@ -1,21 +0,0 @@ -using PostService.Application.Queries.GetAllComments; -using PostService.Domain.Entities; - -namespace PostService.Api.GraphQL.Queries; - -public class CommentQuery -{ - public async Task> GetAllComments(Guid postId, int first, [Service] GetAllCommentsQueryHandler getAllCommentsQueryHandler) - { - var query = new GetAllCommentsQuery(postId, first); - - var result = await getAllCommentsQueryHandler.HandleAsync(query); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } -} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/GraphQL/Queries/LikeQuery.cs b/backend/src/PostService/PostService.Api/GraphQL/Queries/LikeQuery.cs deleted file mode 100644 index 3aeb908..0000000 --- a/backend/src/PostService/PostService.Api/GraphQL/Queries/LikeQuery.cs +++ /dev/null @@ -1,21 +0,0 @@ -using PostService.Application.Queries.GetAllLikes; -using PostService.Domain.Entities; - -namespace PostService.Api.GraphQL.Queries; - -public class LikeQuery -{ - public async Task> GetAllLikes(Guid postId, int first, [Service] GetAllLikesQueryHandler getAllLikesQueryHandler) - { - var query = new GetAllLikesQuery(postId, first); - - var result = await getAllLikesQueryHandler.HandleAsync(query); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } -} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/GraphQL/Queries/PostQuery.cs b/backend/src/PostService/PostService.Api/GraphQL/Queries/PostQuery.cs deleted file mode 100644 index 7c3f1d8..0000000 --- a/backend/src/PostService/PostService.Api/GraphQL/Queries/PostQuery.cs +++ /dev/null @@ -1,37 +0,0 @@ -using PostService.Application.DTOs; -using PostService.Application.Queries.GetAllPosts; -using PostService.Application.Queries.GetPost; -using PostService.Domain.Entities; - -namespace PostService.Api.GraphQL.Queries; - -public class PostQuery -{ - public async Task GetPost(Guid id, [Service] GetPostQueryHandler getPostQueryHandler) - { - var query = new GetPostQuery(id); - - var result = await getPostQueryHandler.HandleAsync(query); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } - - public async Task> GetAllPost(int first, Guid lastPostId, [Service] GetAllPostsQueryHandler getAllPostsQueryHandler) - { - var query = new GetAllPostsQuery(first, lastPostId); - - var result = await getAllPostsQueryHandler.HandleAsync(query); - - if (!result.IsSuccess) - { - throw new GraphQLException(new Error(result.Error.Message)); - } - - return result.Response; - } -} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/GraphQL/Query.cs b/backend/src/PostService/PostService.Api/GraphQL/Query.cs new file mode 100644 index 0000000..f9d2e7d --- /dev/null +++ b/backend/src/PostService/PostService.Api/GraphQL/Query.cs @@ -0,0 +1,69 @@ +using PostService.Application.DTOs; +using PostService.Application.Queries.GetAllComments; +using PostService.Application.Queries.GetAllLikes; +using PostService.Application.Queries.GetAllPosts; +using PostService.Application.Queries.GetPost; +using PostService.Domain.Entities; + +namespace PostService.Api.GraphQL; + +public class Query +{ + public async Task GetPost(Guid id, [Service] GetPostQueryHandler getPostQueryHandler) + { + var query = new GetPostQuery(id); + + var result = await getPostQueryHandler.HandleAsync(query); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task> GetAllPost(int first, Guid lastPostId, [Service] GetAllPostsQueryHandler getAllPostsQueryHandler) + { + var query = new GetAllPostsQuery(first, lastPostId); + + var result = await getAllPostsQueryHandler.HandleAsync(query); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task> GetAllComments(Guid postId, int first, [Service] GetAllCommentsQueryHandler getAllCommentsQueryHandler) + { + var query = new GetAllCommentsQuery(postId, first); + + var result = await getAllCommentsQueryHandler.HandleAsync(query); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public async Task> GetAllLikes(Guid postId, int first, [Service] GetAllLikesQueryHandler getAllLikesQueryHandler) + { + var query = new GetAllLikesQuery(postId, first); + + var result = await getAllLikesQueryHandler.HandleAsync(query); + + if (!result.IsSuccess) + { + throw new GraphQLException(new Error(result.Error.Message)); + } + + return result.Response; + } + + public string Test() => "Hello World"; +} \ No newline at end of file diff --git a/backend/src/PostService/PostService.Api/Program.cs b/backend/src/PostService/PostService.Api/Program.cs index 0801cc7..825b4da 100644 --- a/backend/src/PostService/PostService.Api/Program.cs +++ b/backend/src/PostService/PostService.Api/Program.cs @@ -1,9 +1,7 @@ using Microsoft.EntityFrameworkCore; -using PostService.Api.GraphQL.Mutations; -using PostService.Api.GraphQL.Queries; +using PostService.Api.GraphQL; using PostService.Application; using PostService.Domain.Constants; -using PostService.Domain.Entities; using PostService.Infrastructure; using PostService.Persistence; @@ -38,12 +36,8 @@ builder.Services .AddGraphQLServer() - .AddQueryType() - .AddQueryType() - .AddQueryType() - .AddMutationType() - .AddMutationType() - .AddMutationType() + .AddQueryType() + .AddMutationType() .AddType(new UuidType()) .AddType(); diff --git a/backend/src/PostService/PostService.Api/Properties/launchSettings.json b/backend/src/PostService/PostService.Api/Properties/launchSettings.json index 6b5b134..a08192a 100644 --- a/backend/src/PostService/PostService.Api/Properties/launchSettings.json +++ b/backend/src/PostService/PostService.Api/Properties/launchSettings.json @@ -14,7 +14,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "graphql", - "applicationUrl": "http://localhost:8081", + "applicationUrl": "http://localhost:8002", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -24,7 +24,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "graphql", - "applicationUrl": "https://localhost:7112;http://localhost:8081", + "applicationUrl": "https://localhost:7112;http://localhost:8002", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/backend/src/PostService/PostService.Api/appsettings.json b/backend/src/PostService/PostService.Api/appsettings.json index 78e7ac5..74ec804 100644 --- a/backend/src/PostService/PostService.Api/appsettings.json +++ b/backend/src/PostService/PostService.Api/appsettings.json @@ -8,7 +8,7 @@ "AllowedHosts": "*", "AllowedOrigins": [ "http://localhost:4000", "http://localhost:4200", "http://localhost:8080" ], "DatabaseConnectionString": "Host=localhost;Port=5432;Username=postgres;Password=mysecretpasswordfordevelopment;Database=PostsDb", - "WebHostUrl": "http://0.0.0.0:8081", + "WebHostUrl": "http://0.0.0.0:8002", "RabbitMq": { "Host": "localhost", "Username": "guest", diff --git a/backend/src/PostService/PostService.Application/Consumers/UserUpdatedEventConsumer.cs b/backend/src/PostService/PostService.Application/Consumers/UserUpdatedEventConsumer.cs index e60edbd..1d017ba 100644 --- a/backend/src/PostService/PostService.Application/Consumers/UserUpdatedEventConsumer.cs +++ b/backend/src/PostService/PostService.Application/Consumers/UserUpdatedEventConsumer.cs @@ -26,5 +26,7 @@ public async Task Consume(ConsumeContext context) @event.Username, @event.ImageUrl ); + + await _dbContext.SaveChangesAsync(); } } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 587e0f6..699b58d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,26 +11,50 @@ services: ports: - "4200:80" depends_on: - post-service: + gateway: condition: service_healthy networks: - microservices + gateway: + container_name: gateway + build: + context: ./backend/src + dockerfile: Gateway/Dockerfile + ports: + - "8000:8000" + environment: + ReverseProxy__Clusters__auth-service__Destinations__destination1__Address: http://auth-service:8001 + ReverseProxy__Clusters__post-service__Destinations__destination1__Address: http://post-service:8002 + depends_on: + postgres: + condition: service_healthy + rabittmq: + condition: service_healthy + healthcheck: + test: [ "CMD-SHELL", "curl -f http://gateway:8000/health || exit 1" ] + interval: 10s + timeout: 5s + retries: 3 + networks: + - microservices + auth-service: container_name: auth-service build: context: ./backend/src dockerfile: AuthService/Dockerfile ports: - - "8082:8082" + - "8001:8001" environment: DatabaseConnectionString: Host=postgres;Port=5432;Username=postgres;Password=mysecretpasswordfordevelopment;Database=AuthDb Keycloak__Url: http://keycloak:8080 + RabbitMq__Host: rabbitmq depends_on: - postgres: + gateway: condition: service_healthy healthcheck: - test: [ "CMD-SHELL", "curl -f http://auth-service:8082/health || exit 1" ] + test: [ "CMD-SHELL", "curl -f http://auth-service:8001/health || exit 1" ] interval: 10s timeout: 5s retries: 3 @@ -43,14 +67,15 @@ services: context: ./backend/src dockerfile: PostService/Dockerfile ports: - - "8081:8081" + - "8002:8002" environment: DatabaseConnectionString: Host=postgres;Port=5432;Username=postgres;Password=mysecretpasswordfordevelopment;Database=PostsDb + RabbitMq__Host: rabbitmq depends_on: - postgres: + gateway: condition: service_healthy healthcheck: - test: [ "CMD-SHELL", "curl -f http://post-service:8081/health || exit 1" ] + test: [ "CMD-SHELL", "curl -f http://post-service:8002/health || exit 1" ] interval: 10s timeout: 5s retries: 3 @@ -100,6 +125,11 @@ services: ports: - 5672:5672 - 15672:15672 + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 30s + timeout: 10s + retries: 5 networks: - microservices diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index 0d85646..0f0784a 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -1,23 +1,26 @@ -import { ApplicationConfig, provideZoneChangeDetection, inject } from '@angular/core'; +import {ApplicationConfig, inject, provideZoneChangeDetection} from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; -import { provideHttpClient} from '@angular/common/http'; +import {provideHttpClient, withFetch} from '@angular/common/http'; import { provideApollo } from 'apollo-angular'; -import { HttpLink } from 'apollo-angular/http'; -import {ApolloLink, InMemoryCache} from '@apollo/client/core'; -import {environment} from "../environments/environment"; +import { ApolloLink, InMemoryCache} from '@apollo/client/core'; import {setContext} from "@apollo/client/link/context"; -import {DOCUMENT} from "@angular/common"; +import {environment} from "../environments/environment"; +import {HttpLink} from "apollo-angular/http"; +import {loadDevMessages, loadErrorMessages} from "@apollo/client/dev"; export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), - provideHttpClient(), + provideHttpClient(withFetch()), provideApollo(() => { + if (!environment.production) { + loadDevMessages(); + loadErrorMessages(); + } const httpLink = inject(HttpLink); - const document = inject(DOCUMENT); const authLink = setContext(() => { const token = document.cookie @@ -25,9 +28,10 @@ export const appConfig: ApplicationConfig = { .find(row => row.startsWith('token=')) ?.split('=')[1]; return { + headers: { Authorization: token ? `Bearer ${token}` : '', - }, + } }; }); @@ -37,7 +41,7 @@ export const appConfig: ApplicationConfig = { ]); return { - link, + link: link, cache: new InMemoryCache(), }; }) diff --git a/frontend/src/app/features/auth/services/auth.service.ts b/frontend/src/app/features/auth/services/auth.service.ts index 050a1a6..9d176a0 100644 --- a/frontend/src/app/features/auth/services/auth.service.ts +++ b/frontend/src/app/features/auth/services/auth.service.ts @@ -6,13 +6,20 @@ import {TokenService} from "../../../core/services/token.service"; import {MutationResponse} from "../responses/mutation.response"; import {QueryResponses} from "../responses/query.responses"; import {IS_AUTHENTICATED} from "../requests/queries"; +import {environment} from "../../../../environments/environment"; +import {InMemoryCache} from "@apollo/client/core"; @Injectable({ providedIn: 'root' }) export class AuthService { - constructor(private apollo: Apollo, private tokenService: TokenService) { } + constructor(private apollo: Apollo, private tokenService: TokenService) { + // apollo.create({ + // uri: environment.auth_service, + // cache: new InMemoryCache() + // }); + } public login(username: string, password: string): Observable { return this.apollo.mutate({ @@ -60,7 +67,7 @@ export class AuthService { public isAuthenticated(): Observable { - return this.apollo.query({ + return this.apollo.query({ query: IS_AUTHENTICATED }).pipe( map(response => response.data.isAuthenticated)); diff --git a/frontend/src/app/features/auth/services/user.service.ts b/frontend/src/app/features/auth/services/user.service.ts index 7a4b7b8..efdd4dd 100644 --- a/frontend/src/app/features/auth/services/user.service.ts +++ b/frontend/src/app/features/auth/services/user.service.ts @@ -4,13 +4,16 @@ import {map, Observable} from "rxjs"; import {GET_USER_BY_USERNAME} from "../requests/queries"; import {User} from "../models/user.model"; import {QueryResponses} from "../responses/query.responses"; +import {ApolloLink} from "@apollo/client/core"; @Injectable({ providedIn: 'root' }) export class UserService { - constructor(private apollo: Apollo) { } + constructor(private apollo: Apollo) { + // apollo.client.setLink(ApolloLink.from()) + } getUserByUsername(username: string): Observable { return this.apollo.query({ diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index da2fa86..9796f82 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -1,4 +1,5 @@ export const environment = { - auth_service: "http://localhost:8082/graphql", + auth_service: "http://localhost:8000/auth/graphql", + post_service: "http://localhost:8000/post/graphql", production: false }; diff --git a/keycloak/fitness-app-realm.json b/keycloak/fitness-app-realm.json index 0766401..a964d1f 100644 --- a/keycloak/fitness-app-realm.json +++ b/keycloak/fitness-app-realm.json @@ -99,6 +99,7 @@ "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, "webAuthnPolicyPasswordlessAcceptableAaguids": [], "webAuthnPolicyPasswordlessExtraOrigins": [], + "users": [], "scopeMappings": [ { "clientScope": "offline_access", @@ -123,33 +124,100 @@ "id": "5f30eb33-363f-4e7b-b2f4-732e0d7143b1", "clientId": "account", "name": "${client_account}", + "description": "", "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/fitness-app-realm/account/", + "adminUrl": "", + "baseUrl": "", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", + "secret": "**********", "redirectUris": [ - "/realms/fitness-app-realm/account/*" + "*" + ], + "webOrigins": [ + "*" ], - "webOrigins": [], "notBefore": 0, "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "authorizationServicesEnabled": true, + "publicClient": false, "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { + "client.secret.creation.time": "1736111053", + "client.introspection.response.allow.jwt.claim.enabled": "false", + "post.logout.redirect.uris": "+", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "use.refresh.tokens": "true", "realm_client": "false", - "post.logout.redirect.uris": "+" + "oidc.ciba.grant.enabled": "false", + "client.use.lightweight.access.token.enabled": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "tls.client.certificate.bound.access.tokens": "false", + "require.pushed.authorization.requests": "false", + "acr.loa.map": "{}", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false" }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "83e4c5a8-cbbf-4fcf-b45e-ace4ffbaa847", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "79ac7af9-893d-4f1d-98a2-f6b809c719e6", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "client_id", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "client_id", + "jsonType.label": "String" + } + }, + { + "id": "6b15965b-ba23-4553-bb34-c44b03ae8c1e", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], "defaultClientScopes": [ "web-origins", "acr", @@ -164,7 +232,47 @@ "offline_access", "organization", "microprofile-jwt" - ] + ], + "authorizationSettings": { + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [ + { + "name": "Default Resource", + "type": "urn:account:resources:default", + "ownerManagedAccess": false, + "attributes": {}, + "uris": [ + "/*" + ] + } + ], + "policies": [ + { + "name": "Default Policy", + "description": "A policy that grants access only for users within this realm", + "type": "js", + "logic": "POSITIVE", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n" + } + }, + { + "name": "Default Permission", + "description": "A permission that applies to the default resource type", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "defaultResourceType": "urn:account:resources:default", + "applyPolicies": "[\"Default Policy\"]" + } + } + ], + "scopes": [], + "decisionStrategy": "UNANIMOUS" + } }, { "id": "03ae488f-7ca5-469a-8aba-90cb0df1a629", @@ -325,10 +433,10 @@ "clientAuthenticatorType": "client-secret", "secret": "**********", "redirectUris": [ - "http://0.0.0.0:8081/*" + "*" ], "webOrigins": [ - "http://0.0.0.0:8081" + "*" ], "notBefore": 0, "bearerOnly": false, @@ -341,7 +449,7 @@ "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { - "client.secret.creation.time": "1733417203", + "client.secret.creation.time": "1736108457", "client.introspection.response.allow.jwt.claim.enabled": "false", "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", @@ -418,6 +526,7 @@ "defaultClientScopes": [ "web-origins", "acr", + "aud", "profile", "roles", "basic", @@ -1018,6 +1127,35 @@ "display.on.consent.screen": "true" } }, + { + "id": "724eace5-6ad6-4246-b1f4-5e4e3e1370ab", + "name": "aud", + "description": "", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "gui.order": "", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "8d0b616f-50a0-433b-9441-43da45bc410b", + "name": "Audience", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "account", + "id.token.claim": "true", + "lightweight.claim": "false", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "false" + } + } + ] + }, { "id": "561ffffc-ed7a-44b8-b8ee-6e047e5b03d8", "name": "roles", @@ -1179,7 +1317,8 @@ "roles", "acr", "basic", - "web-origins" + "web-origins", + "aud" ], "defaultOptionalClientScopes": [ "offline_access", @@ -1272,13 +1411,13 @@ "config": { "allowed-protocol-mapper-types": [ "oidc-usermodel-attribute-mapper", - "oidc-address-mapper", - "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "oidc-address-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", - "oidc-usermodel-property-mapper", - "saml-role-list-mapper" + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper" ] } }, @@ -1290,14 +1429,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-usermodel-property-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-address-mapper", - "oidc-full-name-mapper", - "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", "saml-user-property-mapper", - "saml-user-attribute-mapper" + "oidc-usermodel-property-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper" ] } },