Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ public static WebApplication UseAPIServices(this WebApplication app)

private static bool Authorize(HttpContext ctx)
{
if (ctx.Items.DownstreamRoute().AuthenticationOptions.AuthenticationProviderKey == null) return true;
if (ctx.Items.DownstreamRoute()
.AuthenticationOptions
.AuthenticationProviderKeys?.Length == 0)
{
return true;
Comment on lines +142 to +146

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't short-circuit role checks on an empty provider list.

This now returns true before RouteClaimsRequirement is evaluated. If a route is configured with AuthenticationProviderKeys: [] but still carries a role requirement, this method will authorize it anyway and skip the claim check entirely.

Suggested fix
-        if (ctx.Items.DownstreamRoute()
-                .AuthenticationOptions
-                .AuthenticationProviderKeys?.Length == 0)
+        var downstreamRoute = ctx.Items.DownstreamRoute();
+        var providerKeys = downstreamRoute.AuthenticationOptions.AuthenticationProviderKeys;
+        var required = downstreamRoute.RouteClaimsRequirement;
+
+        if ((providerKeys == null || providerKeys.Length == 0) &&
+            (required == null || required.Count == 0))
         {
             return true;
         }
-        else
-        {
-            bool auth = false;
-            Claim[] claims = ctx.User.Claims.ToArray<Claim>();
-            Dictionary<string, string> required = ctx.Items.DownstreamRoute().RouteClaimsRequirement;
+        else
+        {
+            bool auth = false;
+            Claim[] claims = ctx.User.Claims.ToArray<Claim>();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/server/APIGateway/src/APIGateway/DependenciesInjection.cs` around lines
142 - 146, The current early-return treats an empty AuthenticationProviderKeys
array as unauthenticated and skips claim checks; change the condition in the
block that references
ctx.Items.DownstreamRoute().AuthenticationOptions.AuthenticationProviderKeys so
it only returns true when AuthenticationProviderKeys is null (no auth
configured), not when its Length == 0, ensuring RouteClaimsRequirement is still
evaluated for routes that explicitly supply an empty provider list.

}
else
{
bool auth = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using MassTransit;
using MassTransit;
namespace Contract.Event.UserEvent;
[EntityName("UpdateUserTotalRecipeEvent")]
public class UpdateUserTotalRecipeEvent
Expand Down
1 change: 0 additions & 1 deletion app/server/Contract/Contract/Protos/TrackingService.proto
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
syntax = "proto3";

import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";

package TrackingProto;

Expand Down
4 changes: 4 additions & 0 deletions app/server/Contract/Contract/Utilities/EnvUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public static string GetConnectionString()
var user = DotNetEnv.Env.GetString("POSTGRES_USER", "Not found").Trim();
var pwd = DotNetEnv.Env.GetString("POSTGRES_PASSWORD", "Not found").Trim();
var connectionString = $"Host={host};Port={port};Database={db};Username={user};Password={pwd};";
if (IsProduction())
{
connectionString = $"{connectionString}SSL Mode=Require;Trust Server Certificate=true;";
}
return connectionString;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Contract.Constants;
using Microsoft.EntityFrameworkCore;
using Duende.IdentityServer.EntityFramework.DbContexts;
using IdentityService.Domain.Interfaces;
using IdentityService.Infrastructure;

Expand Down Expand Up @@ -29,9 +31,10 @@ public static async Task<bool> TryHandleAsync(this WebApplicationBuilder builder
{
case COMMAND_ARGS.MIGRATE:
dbContext.MigrateDb(sp);
sp.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
break;
case COMMAND_ARGS.SEED:
dbContext.SeedDb(sp).GetAwaiter().GetResult();
await dbContext.SeedDb(sp);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using AutoMapper;
using Contract.DTOs.UserDTO;
using IdentityService.Application.Account.Commands;

namespace DuendeIdentityServer.DTOs;

public class IdentityProfile : Profile
{
public IdentityProfile()
{
CreateMap<RegisterAccountDTO, RegisterAccountCommand>().ReverseMap();
CreateMap<ApplicationAccount, ApplicationUserResponseDTO>().ReverseMap();
CreateMap<VerifyAccountDTO, VerifyAccountCommand>().ReverseMap();
CreateMap<ApplicationAccount, AccountDTO>().ReverseMap();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,12 @@
</PropertyGroup>

<ItemGroup>
<!-- Utility libraries -->
<PackageReference Include="AutoMapper" Version="13.0.1" />

<!-- Identity dependencies -->
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.11" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />

<!-- Reinforce.Typing dependencies -->
<PackageReference Include="Reinforced.Typings" Version="1.6.5" />
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.7.1">
Expand All @@ -34,7 +30,11 @@

<!-- Swagger dependency -->
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />

<!-- Just for duendeidentity server to generate migration -->
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<!-- SignalR client dependency -->

<!-- Other dependecies -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Contract.Extension;
using IdentityService.Domain.Errors;
using Reinforced.Typings.Ast.TypeNames;
using Reinforced.Typings.Fluent;
using ConfigurationBuilder = Reinforced.Typings.Fluent.ConfigurationBuilder;
namespace DuendeIdentityServer.Extensions;
Expand All @@ -20,6 +21,10 @@ public static void ConfigureReinforcedTypings(ConfigurationBuilder builder)

builder.ConfigCommonReinforcedTypings(EXPORT_FILE_PATH, FILE_NAME, errorsTypes);

builder.Substitute(typeof(DateTimeOffset), new RtSimpleTypeName("string"));
builder.Substitute(typeof(Microsoft.AspNetCore.Identity.IdentityRole), new RtSimpleTypeName("any"));
builder.Substitute(typeof(IFormFile), new RtSimpleTypeName("any"));

// DTO and Entities
builder.ExportAsInterfaces([
typeof(StatisticEntity),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;

namespace DuendeIdentityServer;
Expand Down Expand Up @@ -43,9 +44,12 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
});

// Register automapper
IMapper mapper = MappingConfig.RegisterMaps().CreateMapper();
services.AddSingleton(mapper);
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddAutoMapper(
cfg =>
{
cfg.LicenseKey = DotNetEnv.Env.GetString("LUCKYPENNYSOFTWARE_LICENSE_KEY", "Not Found");
},
AppDomain.CurrentDomain.GetAssemblies());

services.AddCommonAPIWithoutAuthServices();
services
Expand All @@ -68,6 +72,20 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
// keep old key for 7 days in discovery for validation of tokens
options.KeyManagement.RetentionDuration = TimeSpan.FromDays(7);
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(EnvUtility.GetConnectionString(),
options => options.MigrationsAssembly("IdentityService.Infrastructure")
.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(15),
errorCodesToAdd: null
));

options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 3600;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
Expand Down Expand Up @@ -123,7 +141,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
return builder.Build();
}

public static async Task<WebApplication> ConfigurePipelineAsync(this WebApplication app)
public static Task<WebApplication> ConfigurePipeline(this WebApplication app)
{
if (EnvUtility.IsProduction() || EnvUtility.IsStaging())
{
Expand Down Expand Up @@ -184,11 +202,11 @@ public static async Task<WebApplication> ConfigurePipelineAsync(this WebApplicat

app.UseSignalRServiceAsync();
app.Use(async (context, next) =>
{
Console.WriteLine($"RemoteIp: {context.Connection.RemoteIpAddress}");
await next();
});
{
Console.WriteLine($"RemoteIp: {context.Connection.RemoteIpAddress}");
await next();
});
Comment on lines 204 to +208

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid logging raw client IPs to stdout on every request.

This writes PII from every auth request into unstructured logs. If you still need this for debugging, gate it behind ILogger and a debug-level setting instead of Console.WriteLine.

Suggested change
-        app.Use(async (context, next) =>
-            {
-                Console.WriteLine($"RemoteIp: {context.Connection.RemoteIpAddress}");
-                await next();
-            });
+        app.Use(async (context, next) =>
+        {
+            await next();
+        });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.Use(async (context, next) =>
{
Console.WriteLine($"RemoteIp: {context.Connection.RemoteIpAddress}");
await next();
});
{
Console.WriteLine($"RemoteIp: {context.Connection.RemoteIpAddress}");
await next();
});
app.Use(async (context, next) =>
{
await next();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs`
around lines 204 - 208, Replace the Console.WriteLine call in the inline
middleware (the app.Use(...) block in HostingExtensions.cs) with structured
logging via an ILogger at Debug level: obtain an ILogger (e.g., via app.Logger
or app.ApplicationServices.GetRequiredService<ILogger<HostingExtensions>>()),
check logger.IsEnabled(LogLevel.Debug) before reading/formatting
context.Connection.RemoteIpAddress, and log the IP using logger.LogDebug with a
structured message (avoid writing raw PII to stdout and only emit when debug
logging is enabled).


return app;
return Task.FromResult(app);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public Index(
_sender = sender;
}

public async Task<IActionResult> OnGet(string returnUrl, string identifier)
public Task<IActionResult> OnGet(string returnUrl, string identifier)
{
Input = new InputModel
{
Expand All @@ -53,7 +53,7 @@ public async Task<IActionResult> OnGet(string returnUrl, string identifier)

ViewData["ReturnUrl"] = encodedRedirectUri;

return Page();
return Task.FromResult<IActionResult>(Page());
}

public async Task<IActionResult> OnPost()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ public class Index : PageModel
public Index()
{
}
public async Task<IActionResult> OnGet(string returnUrl)
public Task<IActionResult> OnGet(string returnUrl)
{
var encodedRedirectUri = WebUtility.UrlEncode(returnUrl);

ViewData["ReturnUrl"] = encodedRedirectUri;
return Page();
return Task.FromResult<IActionResult>(Page());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public async Task<IActionResult> OnPost()
}
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, "Send OTP failed! Please try again");
ModelState.AddModelError(string.Empty, $"Send OTP failed! Please try again ({ex.Message})");
Comment on lines 129 to +131

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't echo exception details back to the forgot-password UI.

ex.Message is now rendered to the end user on an unauthenticated recovery flow. That can leak internal provider state or validation details; keep the generic message here and log the exception server-side instead.

Suggested change
                     catch (Exception ex)
                     {
-                        ModelState.AddModelError(string.Empty, $"Send OTP failed! Please try again ({ex.Message})");
+                        ModelState.AddModelError(string.Empty, "Send OTP failed! Please try again");
+                        // Log `ex` with the server-side logger instead of exposing it in the UI.
                     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, "Send OTP failed! Please try again");
ModelState.AddModelError(string.Empty, $"Send OTP failed! Please try again ({ex.Message})");
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, "Send OTP failed! Please try again");
// Log `ex` with the server-side logger instead of exposing it in the UI.
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/IdentityService/src/DuendeIdentityServer/Pages/Account/ForgotPassword/Index.cshtml.cs`
around lines 129 - 131, In the catch block in Index.cshtml.cs (the
forgot-password handler, e.g., OnPostAsync), stop echoing ex.Message to the
user: replace ModelState.AddModelError(string.Empty, $"Send OTP failed! Please
try again ({ex.Message})") with a generic message such as
ModelState.AddModelError(string.Empty, "Send OTP failed! Please try again.");
and instead log the exception server-side via the class logger (e.g.,
_logger.LogError(ex, "Failed to send OTP for ForgotPassword")); ensure no
exception details are returned to the unauthenticated UI.

}
break;
case "ReturnFind":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public Index(
_sender = sender;
}

public async Task<IActionResult> OnGet(string returnUrl, string identifier)
public Task<IActionResult> OnGet(string returnUrl, string identifier)
{
Input = new InputModel
{
Expand All @@ -45,7 +45,7 @@ public async Task<IActionResult> OnGet(string returnUrl, string identifier)
IsValidOTP = false,
};

return Page();
return Task.FromResult<IActionResult>(Page());
}

public async Task<IActionResult> OnPost()
Expand Down Expand Up @@ -176,7 +176,7 @@ public async Task<IActionResult> OnPost()
Identifier = Input.Identifier,
OTP = Input.OTP!,
Method = IdentifierUtility.Check(Input.Identifier),
Password = Input.Password
Password = Input.Password!
});

result.ThrowIfFailure();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public async Task<IActionResult> OnGet()
externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
throw new InvalidOperationException("Unknown userid");

var provider = result.Properties.Items["scheme"] ?? throw new InvalidOperationException("Null scheme in authentiation properties");
var provider = result.Properties?.Items["scheme"] ?? throw new InvalidOperationException("Null scheme in authentiation properties");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix typo in exception message.

The exception message contains a typo: "authentiation" should be "authentication".

📝 Proposed fix
-            var provider = result.Properties?.Items["scheme"] ?? throw new InvalidOperationException("Null scheme in authentiation properties");
+            var provider = result.Properties?.Items["scheme"] ?? throw new InvalidOperationException("Null scheme in authentication properties");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var provider = result.Properties?.Items["scheme"] ?? throw new InvalidOperationException("Null scheme in authentiation properties");
var provider = result.Properties?.Items["scheme"] ?? throw new InvalidOperationException("Null scheme in authentication properties");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/IdentityService/src/DuendeIdentityServer/Pages/ExternalLogin/Callback.cshtml.cs`
at line 74, The InvalidOperationException message contains a typo: update the
thrown exception text used when resolving provider (the expression assigning var
provider in Callback.cshtml.cs) to replace "authentiation" with "authentication"
so the message reads something like "Null scheme in authentication properties".

var providerUserId = userIdClaim.Value;

// find external user
Expand All @@ -87,7 +87,7 @@ public async Task<IActionResult> OnGet()
Provider = provider,
ProviderUserId = providerUserId,
Claims = externalUser.Claims,
AccessToken = accessToken
AccessToken = accessToken!
});
response.ThrowIfFailure();
user = response.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

var app = await builder
.ConfigureServices()
.ConfigurePipelineAsync();
.ConfigurePipeline();

app.Start();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using AutoMapper;
using Google.Protobuf.Collections;
using IdentityService.Application.Configs.MapperConverters;

namespace IdentityService.Application.Configs;

public class IdentityProfile : Profile
{
public IdentityProfile()
{
CreateMap(typeof(List<>), typeof(RepeatedField<>)).ConvertUsing(typeof(ListToRepeatedFieldConverter<,>));
CreateMap(typeof(RepeatedField<>), typeof(List<>)).ConvertUsing(typeof(RepeatedFieldToListConverter<,>));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
services.AddGrpcClientServices();
// Register automapper
IMapper mapper = MappingConfig.RegisterMaps().CreateMapper();
services.AddSingleton(mapper);
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddAutoMapper(
cfg =>
{
cfg.LicenseKey = DotNetEnv.Env.GetString("LUCKYPENNYSOFTWARE_LICENSE_KEY", "Not Found");
},
AppDomain.CurrentDomain.GetAssemblies());

return services;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="AutoMapper" Version="16.1.1" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,23 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti

public static IServiceCollection AddMinimalInfrastructureServices(this IServiceCollection services)
{
var connectionString = Contract.Utilities.EnvUtility.GetConnectionString();
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
services.AddDbContext<IApplicationDbContext, ApplicationDbContext>(options =>
options.UseNpgsql(Contract.Utilities.EnvUtility.GetConnectionString()));
options.UseNpgsql(connectionString));
services.AddIdentityServer()
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(connectionString,
options => options.MigrationsAssembly("IdentityService.Infrastructure")
.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(15),
errorCodesToAdd: null
));

});
Comment on lines 32 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify Npgsql registrations and retry-policy presence across related contexts.
rg -n --type=cs -C3 'UseNpgsql\(' \
  app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs \
  app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs \
  app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/ApplicationDbContext.cs

rg -n --type=cs -C3 'EnableRetryOnFailure' \
  app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs \
  app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs \
  app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/ApplicationDbContext.cs

Repository: Carl-Johnsons/tastopia

Length of output: 4597


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show relevant code blocks for operational-store wiring
sed -n '1,120p' app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs
echo "----"
sed -n '1,140p' app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs
echo "----"
sed -n '1,120p' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/ApplicationDbContext.cs

echo "----"
# Trace whether operational-store uses ApplicationDbContext or a different Duende context
rg -n --type=cs 'OperationalStore|AddOperationalStore|ConfigureDbContext|ApplicationDbContext|IApplicationDbContext' \
  app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs \
  app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs \
  app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/ApplicationDbContext.cs

Repository: Carl-Johnsons/tastopia

Length of output: 14374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --type=cs 'AddOperationalStore\(' app/server/IdentityService/src
rg -n --type=cs 'EnableRetryOnFailure' app/server/IdentityService/src

Repository: Carl-Johnsons/tastopia

Length of output: 464


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --type=cs 'PersistedGrant|OperationalStore|DeviceFlow|ConfigurationDbContext|PersistedGrantDbContext|Granular?DbContext' app/server/IdentityService/src

Repository: Carl-Johnsons/tastopia

Length of output: 7543


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the actual DbContext classes used by Duende operational store
rg -n --type=cs 'class\s+PersistedGrantDbContext\b|DbContext\s*:\s*' \
  app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer

# Show PersistedGrantDbContext and any other operational-store DbContext implementations
fd -a 'PersistedGrantDbContext*.cs' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer || true
fd -a '*.cs' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer | wc -l

# Print the relevant DbContext class bodies (small files/sections only)
for f in $(fd -a 'PersistedGrantDbContext*.cs' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer); do
  echo "---- $f ----"
  sed -n '1,160p' "$f"
done

# Also check DeviceFlow (if present)
for f in $(rg -l --type=cs 'class\s+\w*DeviceFlow\w*DbContext|DeviceFlow.*DbContext' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer); do
  echo "---- $f ----"
  sed -n '1,200p' "$f"
done

# Confirm how AppCommandHandler migrates persisted grant DbContext
sed -n '1,120p' app/server/IdentityService/src/DuendeIdentityServer/AppCommandHandler.cs

Repository: Carl-Johnsons/tastopia

Length of output: 48


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Identify operational-store DbContext classes in our repo
# (we expect ones like PersistedGrantDbContext, DeviceFlowCodesDbContext, etc.)
mapfile -t ctx_files < <(
  fd -a '*.cs' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer \
  | head -n 200
)

echo "Context candidate files: ${`#ctx_files`[@]} (showing up to 200)"
for f in "${ctx_files[@]}"; do
  rg -n --type=cs 'class\s+\w*DbContext\b|:?\s*DbContext\b|\[DbContext' "$f" || true
done

# Specifically look for PersistedGrantDbContext definition and any DbContext base with retry
mapfile -t target_files < <(
  (fd -a 'PersistedGrantDbContext*.cs' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer || true)
)

echo "PersistedGrantDbContext files: ${`#target_files`[@]}"
for f in "${target_files[@]}"; do
  echo "---- $f ----"
  sed -n '1,220p' "$f"
done

# Also inspect the persisted grant model snapshot file where retry might be set/expected via configuration
sed -n '1,140p' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer/PersistedGrant/PersistedGrantDbContextModelSnapshot.cs

# Inspect AppCommandHandler migration wiring
sed -n '1,120p' app/server/IdentityService/src/DuendeIdentityServer/AppCommandHandler.cs

Repository: Carl-Johnsons/tastopia

Length of output: 123


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer"

echo "Listing DbContext-related files under $base (first 200):"
fd -a '*.cs' "$base" | head -n 200

echo "---- PersistedGrantDbContext* ----"
fd -a 'PersistedGrantDbContext*.cs' "$base" || true

echo "---- Files containing 'class' + 'DbContext' under $base ----"
rg -n --type=cs 'class\s+\w*DbContext\b' "$base" || true
rg -n --type=cs '\[DbContext\(' "$base" || true

echo "---- AppCommandHandler migration wiring ----"
sed -n '1,160p' app/server/IdentityService/src/DuendeIdentityServer/AppCommandHandler.cs

echo "---- HostingExtensions AddOperationalStore block ----"
sed -n '50,120p' app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs

echo "---- DependencyInjection AddMinimalInfrastructureServices operational store block ----"
sed -n '1,120p' app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs

Repository: Carl-Johnsons/tastopia

Length of output: 508


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show top of files to see using directives for TimeSpan (and EF Core types)
sed -n '1,80p' app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs
echo "----"
sed -n '1,110p' app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs

echo "----"
sed -n '1,120p' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/ApplicationDbContext.cs

echo "----"
base="app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer"
echo "Listing ${base} (few levels):"
find "$base" -maxdepth 4 -type f -name '*.cs' | head -n 200

echo "----"
rg -n --type=cs 'PersistedGrantDbContext|DeviceFlowCodes|ConfigurationDbContext|AddOperationalStore|ConfigureDbContext' "$base" app/server/IdentityService/src/DuendeIdentityServer/HostingExtensions.cs app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs

Repository: Carl-Johnsons/tastopia

Length of output: 13683


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check for global usings that might provide TimeSpan
rg -n --type=cs 'global using\s+System;' . || true
rg -n --type=cs 'global using\s+System\.Collections|global using\s+System\.Threading|global using\s+System\.' . || true

# Check if DependencyInjection.cs or nearby files use TimeSpan already (without an explicit using)
rg -n --type=cs 'TimeSpan\.FromSeconds' app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs || true

Repository: Carl-Johnsons/tastopia

Length of output: 287


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find any global usings / using System in IdentityService project
rg -n --type=cs 'global using System\b|using System;' app/server/IdentityService || true
rg -n --type=cs 'TimeSpan\.From' app/server/IdentityService || true

# 2) Find where PersistedGrantDbContext is implemented and whether it sets retries
rg -n --type=cs 'class\s+PersistedGrantDbContext\b|PersistedGrantDbContext\s*:\s*DbContext' app/server/IdentityService/src || true
rg -n --type=cs 'EnableRetryOnFailure' app/server/IdentityService/src || true
rg -n --type=cs 'OnConfiguring\s*\(' app/server/IdentityService/src/IdentityService.Infrastructure/Persistence/IdentityServer || true

Repository: Carl-Johnsons/tastopia

Length of output: 4792


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find any definition of PersistedGrantDbContext in the repo
rg -n --type=cs 'class\s+PersistedGrantDbContext\b' app/server/IdentityService/src || true
rg -n --type=cs 'PersistedGrantDbContext\b' app/server/IdentityService/src/DuendeIdentityServer app/server/IdentityService/src/IdentityService.Infrastructure || true

# Inspect AppCommandHandler where persisted grant DbContext is migrated
sed -n '1,140p' app/server/IdentityService/src/DuendeIdentityServer/AppCommandHandler.cs

Repository: Carl-Johnsons/tastopia

Length of output: 2031


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find IdentityService csproj(s)
fd -a -e csproj 'IdentityService*.csproj' . || true
fd -a -e csproj '*IdentityService*.csproj' app/server/IdentityService || true

# If exactly one, print relevant property lines around ImplicitUsings / TargetFramework
for proj in $(fd -a -e csproj '*IdentityService*.csproj' app/server/IdentityService || true); do
  echo "---- $proj ----"
  sed -n '1,200p' "$proj" | rg -n 'ImplicitUsings|TargetFramework|LangVersion' || true
done

# Also check for repo-wide Directory.Build.props that could set ImplicitUsings
for f in $(fd -a 'Directory.Build.props' . || true); do
  echo "---- $f (ImplicitUsings) ----"
  rg -n 'ImplicitUsings' "$f" || true
done

Repository: Carl-Johnsons/tastopia

Length of output: 674


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find IdentityService csproj(s) using glob (fd defaults to regex)
projs=$(fd -a --glob '*IdentityService*.csproj' app/server || true)
echo "$projs"

for proj in $projs; do
  echo "---- $proj ----"
  sed -n '1,200p' "$proj" | rg -n 'ImplicitUsings|TargetFramework|LangVersion' || true
done

# Check for repo-wide Directory.Build.props / Directory.Build.targets
for f in $(fd -a --glob 'Directory.Build.props' . || true); do
  echo "---- $f ----"
  rg -n 'ImplicitUsings|TargetFramework|LangVersion' "$f" || true
done
for f in $(fd -a --glob 'Directory.Build.targets' . || true); do
  echo "---- $f ----"
  rg -n 'ImplicitUsings|TargetFramework|LangVersion' "$f" || true
done

Repository: Carl-Johnsons/tastopia

Length of output: 1036


Add Npgsql EnableRetryOnFailure to Duende IdentityServer operational-store DbContext configuration.

IdentityService.Infrastructure/DependencyInjection.cs’s AddMinimalInfrastructureServices() configures AddOperationalStore(...).ConfigureDbContext with UseNpgsql(...) but no retry policy, weakening resilience for persisted grants/session cleanup during transient DB faults. Same omission exists in DuendeIdentityServer/HostingExtensions.cs’s AddOperationalStore(...).

Suggested patch
         services.AddDbContext<IApplicationDbContext, ApplicationDbContext>(options =>
-                options.UseNpgsql(Contract.Utilities.EnvUtility.GetConnectionString()));
+                options.UseNpgsql(
+                    Contract.Utilities.EnvUtility.GetConnectionString(),
+                    sql => sql.EnableRetryOnFailure(
+                        maxRetryCount: 10,
+                        maxRetryDelay: TimeSpan.FromSeconds(15),
+                        errorCodesToAdd: null)));
         services.AddIdentityServer()
                 .AddOperationalStore(options =>
                 {
                     options.ConfigureDbContext = b =>
                         b.UseNpgsql(
                             Contract.Utilities.EnvUtility.GetConnectionString(),
-                            sql => sql.MigrationsAssembly("IdentityService.Infrastructure"));
+                            sql =>
+                            {
+                                sql.MigrationsAssembly("IdentityService.Infrastructure");
+                                sql.EnableRetryOnFailure(
+                                    maxRetryCount: 10,
+                                    maxRetryDelay: TimeSpan.FromSeconds(15),
+                                    errorCodesToAdd: null);
+                            });
                 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
services.AddDbContext<IApplicationDbContext, ApplicationDbContext>(options =>
options.UseNpgsql(Contract.Utilities.EnvUtility.GetConnectionString()));
services.AddIdentityServer()
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseNpgsql(
Contract.Utilities.EnvUtility.GetConnectionString(),
sql => sql.MigrationsAssembly("IdentityService.Infrastructure"));
});
services.AddDbContext<IApplicationDbContext, ApplicationDbContext>(options =>
options.UseNpgsql(
Contract.Utilities.EnvUtility.GetConnectionString(),
sql => sql.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(15),
errorCodesToAdd: null)));
services.AddIdentityServer()
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseNpgsql(
Contract.Utilities.EnvUtility.GetConnectionString(),
sql =>
{
sql.MigrationsAssembly("IdentityService.Infrastructure");
sql.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(15),
errorCodesToAdd: null);
});
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/IdentityService/src/IdentityService.Infrastructure/DependencyInjection.cs`
around lines 31 - 40, The AddOperationalStore ConfigureDbContext calls (in
AddMinimalInfrastructureServices() within DependencyInjection.cs and the
AddOperationalStore extension in DuendeIdentityServer/HostingExtensions.cs) use
UseNpgsql(...) without a retry policy; update those UseNpgsql invocations to
configure resilient retries by passing a Npgsql-specific SQL options lambda that
calls EnableRetryOnFailure (e.g., sql => {
sql.MigrationsAssembly("IdentityService.Infrastructure");
sql.EnableRetryOnFailure(); }) so persisted-grant/session DB operations use
transient-fault retry; ensure the same pattern is applied in both
AddOperationalStore usages and keep the MigrationsAssembly setting intact.

services.AddIdentity<ApplicationAccount, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Expand Down
Loading
Loading