diff --git a/docs/web/architecture.md b/docs/web/architecture.md
index e11f7c1b..85b20264 100644
--- a/docs/web/architecture.md
+++ b/docs/web/architecture.md
@@ -89,7 +89,6 @@ flowchart TB
subgraph INFRA["Infrastructure Layer"]
direction LR
I1["Database.Connection"]
- I2["Infrastructure.Jwt"]
I3["Infrastructure.PasswordHashing"]
I4["Infrastructure.Email /
Email.Templates"]
I5["Infrastructure.FileUpload"]
@@ -133,7 +132,6 @@ own
- Every `Features.*` project (for controller/MediatR discovery)
- `Shared.Contracts`, `Shared.Application`
-- `Infrastructure.Jwt` (for the auth middleware)
**Rules**:
@@ -167,10 +165,9 @@ brewery photo upload command) rather than bound to an HTTP route directly.
- `Domain.Entities`, `Domain.Exceptions`
- `Database.Connection` (generic ADO.NET connection/command plumbing) plus
- whichever infrastructure project the slice needs (`Infrastructure.Jwt`/
- `Infrastructure.PasswordHashing` for Users, `Infrastructure.Email`/
- `Infrastructure.Email.Templates` for Emails, `Infrastructure.FileUpload` for
- PhotoUpload)
+ whichever infrastructure project the slice needs (`Infrastructure.PasswordHashing`
+ for Users, `Infrastructure.Email`/`Infrastructure.Email.Templates` for
+ Emails, `Infrastructure.FileUpload` for PhotoUpload)
- `Shared.Contracts`, `Shared.Application`
- No other `Features.*` project, with two exceptions: `Features.Users`
references `Features.PhotoUpload` directly for avatar uploads, and
@@ -210,7 +207,6 @@ whichever slices need them
| Project | Description |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Database.Connection` | Generic ADO.NET connection/command plumbing (`DefaultSqlConnectionFactory`, the abstract `DapperRepository` base class), not domain-specific; each slice's own `Repository/` folder builds on this. |
-| `Infrastructure.Jwt` | JWT token generation and validation. |
| `Infrastructure.PasswordHashing` | Argon2id password hashing. |
| `Infrastructure.Email` | Email sending capabilities (SMTP/MailKit). |
| `Infrastructure.Email.Templates` | Email template rendering (Razor components). |
diff --git a/docs/web/diagrams/vertical-slice.md b/docs/web/diagrams/vertical-slice.md
index 44c7a1c1..02285b69 100644
--- a/docs/web/diagrams/vertical-slice.md
+++ b/docs/web/diagrams/vertical-slice.md
@@ -97,7 +97,7 @@ see the notes for what else each of those foundation projects pulls in.
- **Omitted from the diagram**:
- `Domain.Exceptions` (referenced by nearly every project directly, including
- all of `Shared.Contracts`, `Infrastructure.Jwt`, and each feature slice)
+ all of `Shared.Contracts` and each feature slice)
- `Infrastructure.Configuration` (the shared options-binding project
referenced by `Database.Connection`, `Infrastructure.Email`,
`Infrastructure.FileUpload`, and `Features.Emails` directly)
diff --git a/docs/web/diagrams/vertical-slice/project-references.mmd b/docs/web/diagrams/vertical-slice/project-references.mmd
index 5ea0a881..982666e3 100644
--- a/docs/web/diagrams/vertical-slice/project-references.mmd
+++ b/docs/web/diagrams/vertical-slice/project-references.mmd
@@ -23,7 +23,6 @@ end
subgraph INFRA["Slice-specific Infrastructure"]
direction LR
-JWT["Infrastructure.Jwt"]
PwdHash["Infrastructure.PasswordHashing"]
FileUpload["Infrastructure.FileUpload"]
Email["Infrastructure.Email"]
@@ -36,6 +35,6 @@ BrewerySlice --> FOUNDATION
LocationsSlice --> FOUNDATION
BeersSlice --> FOUNDATION
PhotoSlice --> FOUNDATION & FileUpload
-UsersSlice --> FOUNDATION & JWT & PwdHash
+UsersSlice --> FOUNDATION & PwdHash
EmailsSlice --> Email & EmailTemplates
DBConn -- Dapper SQL, no ORM --> Tables[("SQL Server
Tables")]
diff --git a/docs/web/token-validation.md b/docs/web/token-validation.md
index aff82837..d37ae96d 100644
--- a/docs/web/token-validation.md
+++ b/docs/web/token-validation.md
@@ -22,7 +22,7 @@ The Biergarten API validates JSON Web Tokens (JWTs) across three token types:
### Infrastructure layer
-#### [ITokenInfrastructure](../../web/backend/Infrastructure/Infrastructure.Jwt/ITokenInfrastructure.cs)
+#### [ITokenInfrastructure](../../web/backend/Features/Features.Users/Services/ITokenInfrastructure.cs)
Low-level JWT operations.
@@ -32,7 +32,7 @@ Low-level JWT operations.
- `ValidateJwtAsync()` - Validates token signature, expiration, and format
**Implementation:**
-[JwtInfrastructure.cs](../../web/backend/Infrastructure/Infrastructure.Jwt/JwtInfrastructure.cs)
+[JwtInfrastructure.cs](../../web/backend/Features/Features.Users/Services/JwtInfrastructure.cs)
- Uses Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler
- Algorithm: HS256 (HMAC-SHA256)
@@ -76,12 +76,11 @@ directly, bypassing the `Features.Users` slice entirely.
- Extracts and validates claims (Sub, UniqueName)
- Throws `UnauthorizedException` on validation failure
-`TokenService` is registered by `Features.Users`' own `AddFeaturesUsers()`
-extension method, except for the lower-level `ITokenInfrastructure` (JWT
-signing/verification) it depends on, which is registered by the host
-(`API.Core/ServiceCollectionExtensions.cs`, via `AddCoreInfrastructure()`,
-called from `Program.cs`) since `JwtAuthenticationHandler` (host-level auth
-middleware) also depends on it directly.
+`TokenService` and the lower-level `ITokenInfrastructure` (JWT
+signing/verification) it depends on are both registered by `Features.Users`'
+own `AddFeaturesUsers()` extension method. `JwtAuthenticationHandler`
+(host-level auth middleware) also depends on `ITokenInfrastructure` directly,
+resolving it from the same DI container.
### Integration points
diff --git a/web/backend/API/API.Core/API.Core.csproj b/web/backend/API/API.Core/API.Core.csproj
index 2cc523a4..4e4abe30 100644
--- a/web/backend/API/API.Core/API.Core.csproj
+++ b/web/backend/API/API.Core/API.Core.csproj
@@ -23,7 +23,6 @@
-
diff --git a/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs b/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs
index d838cd24..b51e808f 100644
--- a/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs
+++ b/web/backend/API/API.Core/Authentication/JwtAuthenticationHandler.cs
@@ -1,7 +1,7 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
+using Features.Users.Services;
using Infrastructure.Configuration;
-using Infrastructure.Jwt;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
diff --git a/web/backend/API/API.Core/Dockerfile b/web/backend/API/API.Core/Dockerfile
index 867f0a9f..c8b111bd 100644
--- a/web/backend/API/API.Core/Dockerfile
+++ b/web/backend/API/API.Core/Dockerfile
@@ -36,7 +36,6 @@ COPY ["Features/Features.Users.Tests/Features.Users.Tests.csproj", "Features/Fea
COPY ["Infrastructure/Infrastructure.Configuration/Infrastructure.Configuration.csproj", "Infrastructure/Infrastructure.Configuration/"]
COPY ["Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj", "Infrastructure/Infrastructure.Email/"]
COPY ["Infrastructure/Infrastructure.FileUpload/Infrastructure.FileUpload.csproj", "Infrastructure/Infrastructure.FileUpload/"]
-COPY ["Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj", "Infrastructure/Infrastructure.Jwt/"]
COPY ["Shared/Shared.Application/Shared.Application.csproj", "Shared/Shared.Application/"]
COPY ["Shared/Shared.Contracts/Shared.Contracts.csproj", "Shared/Shared.Contracts/"]
RUN dotnet restore "Biergarten.API.slnx"
diff --git a/web/backend/API/API.Core/ServiceCollectionExtensions.cs b/web/backend/API/API.Core/ServiceCollectionExtensions.cs
index d3621cf0..d8d01a8c 100644
--- a/web/backend/API/API.Core/ServiceCollectionExtensions.cs
+++ b/web/backend/API/API.Core/ServiceCollectionExtensions.cs
@@ -13,7 +13,6 @@ using Features.Users.DependencyInjection;
using FluentValidation;
using FluentValidation.AspNetCore;
using Infrastructure.FileUpload;
-using Infrastructure.Jwt;
using Microsoft.OpenApi.Models;
using Shared.Application.Behaviors;
@@ -105,8 +104,6 @@ internal static class ServiceCollectionExtensions
public IServiceCollection AddCoreInfrastructure() =>
services
- // ITokenInfrastructure is registered here because JwtAuthenticationHandler depends on it directly.
- .AddScoped()
.AddSingleton()
.AddScoped();
diff --git a/web/backend/API/API.Specs/Dockerfile b/web/backend/API/API.Specs/Dockerfile
index 008695b6..c0b248a2 100644
--- a/web/backend/API/API.Specs/Dockerfile
+++ b/web/backend/API/API.Specs/Dockerfile
@@ -26,7 +26,6 @@ COPY ["Features/Features.Users.Tests/Features.Users.Tests.csproj", "Features/Fea
COPY ["Infrastructure/Infrastructure.Configuration/Infrastructure.Configuration.csproj", "Infrastructure/Infrastructure.Configuration/"]
COPY ["Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj", "Infrastructure/Infrastructure.Email/"]
COPY ["Infrastructure/Infrastructure.FileUpload/Infrastructure.FileUpload.csproj", "Infrastructure/Infrastructure.FileUpload/"]
-COPY ["Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj", "Infrastructure/Infrastructure.Jwt/"]
COPY ["Shared/Shared.Application/Shared.Application.csproj", "Shared/Shared.Application/"]
COPY ["Shared/Shared.Contracts/Shared.Contracts.csproj", "Shared/Shared.Contracts/"]
RUN dotnet restore "Biergarten.API.slnx"
diff --git a/web/backend/API/API.Specs/Steps/AuthSteps.cs b/web/backend/API/API.Specs/Steps/AuthSteps.cs
index ab889921..90d6e7c4 100644
--- a/web/backend/API/API.Specs/Steps/AuthSteps.cs
+++ b/web/backend/API/API.Specs/Steps/AuthSteps.cs
@@ -1,8 +1,8 @@
using System.Text;
using System.Text.Json;
+using Features.Users.Services;
using FluentAssertions;
using Infrastructure.Configuration;
-using Infrastructure.Jwt;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Reqnroll;
diff --git a/web/backend/Biergarten.API.slnx b/web/backend/Biergarten.API.slnx
index d24083dd..c49f3bee 100644
--- a/web/backend/Biergarten.API.slnx
+++ b/web/backend/Biergarten.API.slnx
@@ -23,7 +23,6 @@
-
diff --git a/web/backend/Features/Features.Users.Tests/Services/TokenServiceRefreshTests.cs b/web/backend/Features/Features.Users.Tests/Services/TokenServiceRefreshTests.cs
index 581cac4c..04c9b242 100644
--- a/web/backend/Features/Features.Users.Tests/Services/TokenServiceRefreshTests.cs
+++ b/web/backend/Features/Features.Users.Tests/Services/TokenServiceRefreshTests.cs
@@ -6,7 +6,6 @@ using Features.Users.Services;
using Features.Users.Tests.TestSupport;
using FluentAssertions;
using Infrastructure.Configuration;
-using Infrastructure.Jwt;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Moq;
diff --git a/web/backend/Features/Features.Users.Tests/Services/TokenServiceValidationTests.cs b/web/backend/Features/Features.Users.Tests/Services/TokenServiceValidationTests.cs
index b29fdf4c..e4782b1a 100644
--- a/web/backend/Features/Features.Users.Tests/Services/TokenServiceValidationTests.cs
+++ b/web/backend/Features/Features.Users.Tests/Services/TokenServiceValidationTests.cs
@@ -5,7 +5,6 @@ using Features.Users.Services;
using Features.Users.Tests.TestSupport;
using FluentAssertions;
using Infrastructure.Configuration;
-using Infrastructure.Jwt;
using Microsoft.Extensions.Configuration;
using Moq;
diff --git a/web/backend/Features/Features.Users/DependencyInjection/FeaturesUsersServiceCollectionExtensions.cs b/web/backend/Features/Features.Users/DependencyInjection/FeaturesUsersServiceCollectionExtensions.cs
index bd334afb..f8bac6b8 100644
--- a/web/backend/Features/Features.Users/DependencyInjection/FeaturesUsersServiceCollectionExtensions.cs
+++ b/web/backend/Features/Features.Users/DependencyInjection/FeaturesUsersServiceCollectionExtensions.cs
@@ -18,6 +18,7 @@ public static class FeaturesUsersServiceCollectionExtensions
public static IServiceCollection AddFeaturesUsers(this IServiceCollection services)
{
services.AddScoped();
+ services.AddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
diff --git a/web/backend/Features/Features.Users/Features.Users.csproj b/web/backend/Features/Features.Users/Features.Users.csproj
index 692af470..6bbd0552 100644
--- a/web/backend/Features/Features.Users/Features.Users.csproj
+++ b/web/backend/Features/Features.Users/Features.Users.csproj
@@ -15,7 +15,6 @@
-
@@ -24,5 +23,7 @@
+
+
diff --git a/web/backend/Infrastructure/Infrastructure.Jwt/ITokenInfrastructure.cs b/web/backend/Features/Features.Users/Services/ITokenInfrastructure.cs
similarity index 94%
rename from web/backend/Infrastructure/Infrastructure.Jwt/ITokenInfrastructure.cs
rename to web/backend/Features/Features.Users/Services/ITokenInfrastructure.cs
index fe7f5d5d..e4cf4d4c 100644
--- a/web/backend/Infrastructure/Infrastructure.Jwt/ITokenInfrastructure.cs
+++ b/web/backend/Features/Features.Users/Services/ITokenInfrastructure.cs
@@ -1,6 +1,6 @@
using System.Security.Claims;
-namespace Infrastructure.Jwt;
+namespace Features.Users.Services;
///
/// Service for generating and validating JSON Web Tokens (JWTs) used for authentication.
diff --git a/web/backend/Infrastructure/Infrastructure.Jwt/JwtInfrastructure.cs b/web/backend/Features/Features.Users/Services/JwtInfrastructure.cs
similarity index 98%
rename from web/backend/Infrastructure/Infrastructure.Jwt/JwtInfrastructure.cs
rename to web/backend/Features/Features.Users/Services/JwtInfrastructure.cs
index 72815038..7a173a5e 100644
--- a/web/backend/Infrastructure/Infrastructure.Jwt/JwtInfrastructure.cs
+++ b/web/backend/Features/Features.Users/Services/JwtInfrastructure.cs
@@ -5,7 +5,7 @@ using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using JwtRegisteredClaimNames = System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames;
-namespace Infrastructure.Jwt;
+namespace Features.Users.Services;
///
/// Generates and validates HMAC-SHA256 signed JWTs using .
diff --git a/web/backend/Features/Features.Users/Services/TokenService.cs b/web/backend/Features/Features.Users/Services/TokenService.cs
index c738e2db..2566c8b3 100644
--- a/web/backend/Features/Features.Users/Services/TokenService.cs
+++ b/web/backend/Features/Features.Users/Services/TokenService.cs
@@ -3,7 +3,6 @@ using System.Security.Claims;
using Domain.Exceptions;
using Features.Users.Identity;
using Infrastructure.Configuration;
-using Infrastructure.Jwt;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
diff --git a/web/backend/Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj b/web/backend/Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj
deleted file mode 100644
index d66c84f5..00000000
--- a/web/backend/Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- net10.0
- enable
- enable
- Infrastructure.Jwt
-
-
-
-
-
-
-
-
-
-
-