The Application layer defines three port interfaces that act as contracts between the business logic and the infrastructure adapters. By depending only on these abstractions, the use cases remain fully independent of EF Core, ASP.NET Core Identity, and cookie middleware — any of those can be swapped without touching a single line of business logic.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Andrespeerez/porfolio-blog/llms.txt
Use this file to discover all available pages before exploring further.
IUserRepository
Provides read and write access to the user store. Defined inApplication/Interfaces/Repositories to signal that it is a persistence port,
not a service.
Application/Interfaces/Repositories/IUserRepository.cs
Methods
Looks up a user by their email address. Returns
null when no matching
record exists. Used by both AuthenticateUser (to verify credentials) and
RegisterUser (to detect duplicate registrations).Persists a newly created
User entity and saves changes to the underlying
store. Called by RegisterUser after User.Create(...) builds the domain
object.Return values
Resolves to the matching
User entity, or null if the email is not
registered.Completes once the record has been written and the EF Core change tracker has
been flushed. Throws on constraint violations (e.g., duplicate email at the
database level).
Infrastructure implementation
UserRepository in the Infrastructure layer wraps an EF Core DbContext
backed by SQLite. It executes FirstOrDefaultAsync for lookups and calls
SaveChangesAsync after every write.
The interface intentionally exposes only the two operations that the current
use cases require. Additional query methods (e.g.,
GetByIdAsync) should be
added to the interface and implementation together, driven by a new use-case
need — never speculatively.IPasswordHasher
Abstracts password hashing and verification so that the hashing algorithm can be swapped without touching any use-case code.Application/Interfaces/Services/IPasswordHasher.cs
Methods
Accepts a plain-text password and returns a hashed string suitable for
storage. Called by the domain factory
User.Create(...) during registration.
The raw password is never stored.Compares a plain-text password candidate against a previously hashed value.
Returns
true when they match, false otherwise. Called by
AuthenticateUser.ExecuteAsync during login.Return values
A hashed representation of the raw password. The format is determined by the
underlying implementation (BCrypt, PBKDF2, etc.).
true when the plain-text password matches the stored hash; false when it
does not.Infrastructure implementation
IdentityPasswordHasher wraps Microsoft.AspNetCore.Identity.IPasswordHasher<User>,
which uses PBKDF2 with HMAC-SHA256 by default. Swapping to BCrypt or Argon2
requires only a new implementation class registered in the DI container —
no use-case changes.
ISessionManager
Abstracts the creation and revocation of authenticated sessions (authentication cookies). Keeps ASP.NET Core’s cookie middleware out of the Application layer.Application/Interfaces/Services/ISessionManager.cs
Methods
Creates an authentication cookie for the given user. The implementation
builds a
ClaimsPrincipal from the user’s ID and email, then calls
HttpContext.SignInAsync with the configured cookie scheme.Revokes the current user’s authentication cookie by calling
HttpContext.SignOutAsync. Safe to call even when no session is active —
the underlying middleware treats it as a no-op in that case.Return values
Completes once the authentication cookie has been written to the HTTP
response. Throws if
IHttpContextAccessor.HttpContext is null (i.e.,
called outside of an active HTTP request).Completes once the sign-out has been processed and the cookie removed from
the response.
Infrastructure implementation
CookieSessionManager uses IHttpContextAccessor to access the current
HttpContext and delegates to ASP.NET Core’s built-in cookie authentication
handler. Claims written during SignInAsync:
| Claim | Value |
|---|---|
ClaimTypes.NameIdentifier | user.Id.ToString() |
ClaimTypes.Email | user.Email |
Because Blazor Server runs on a persistent SignalR circuit rather than
discrete HTTP requests,
IHttpContextAccessor is injected at startup before
the circuit is established. CookieSessionManager captures the context at
the moment of the initial HTTP handshake, which is the only point at which
cookie auth can write response headers.DI wiring
All three interfaces are registered inProgram.cs with scoped lifetime,
matching the per-request / per-circuit lifecycle of both HTTP and Blazor Server
contexts.
Program.cs