Skip to content

Move the HTTP authentication services to Web.Common - #943

Merged
KrzysztofPajak merged 2 commits into
developfrom
refactor/http-auth-services-to-web
Oct 18, 2026
Merged

KrzysztofPajak merged 2 commits into
developfrom
refactor/http-auth-services-to-web

Conversation

@KrzysztofPajak

@KrzysztofPajak KrzysztofPajak commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Part of the explicit-context work in Business (after #930–#942)
Type: feature

Issue

After #941 no Grand.Business.* service reads IContextAccessor, but two still read the HTTP request directly: CookieAuthenticationService and ApiAuthenticationService in Grand.Business.Authentication inject IHttpContextAccessor. Their whole job is HTTP: the customer cookie, SignInAsync/SignOutAsync, the Authorization header, AuthenticateAsync for the JWT schemes. No explicit argument could replace that read, so they are in the wrong layer.

Reproduce: grep -rln -E 'IContextAccessor|IHttpContextAccessor|HttpContext\b' src/Business --include=*.cs | grep -v Startup lists both files.

Solution

  • CookieAuthenticationService, ApiAuthenticationService and CookieOptionsFactory move to Grand.Web.Common/Security/Authentication (namespace Grand.Web.Common.Security.Authentication), and their DI registrations move from Grand.Business.Authentication's StartupApplication to Grand.Web.Common's.
  • IGrandAuthenticationService stays in Grand.Business.Core: ExternalAuthenticationService and plugins use it. Only its cookie implementation moves.
  • IApiAuthenticationService and ICookieOptionsFactory have no users outside Web (the resolvers, theme contexts, CommonController), so they move with their implementations. This also takes Microsoft.AspNetCore.Http.CookieOptions out of the Business.Core contracts.
  • Everything the moved classes depend on is a Business.Core contract, so Grand.Web.Common gains no dependency on a Business implementation (compatible with Move the business layer implementations to the hosts #772).
  • Tests move to Grand.Web.Common.Tests/Security/Authentication. The JwtBearerAuthenticationService tests that lived inside ApiAuthenticationServiceTests stay in Grand.Business.Authentication.Tests as JwtBearerAuthenticationServiceTests.

The grep above now returns nothing. Grand.Business.Authentication still depends on ASP.NET Core (its StartupApplication registers the JWT and cookie schemes, JwtBearer*Service uses JWT types); that is a separate step.

Breaking changes

  • Namespace of CookieAuthenticationService, ApiAuthenticationService and CookieOptionsFactory: Grand.Business.Authentication.Services → Grand.Web.Common.Security.Authentication; they also move to the Grand.Web.Common assembly. External subclasses of CookieAuthenticationService (it has virtual members) must update the namespace and reference.
  • Namespace of IApiAuthenticationService and ICookieOptionsFactory: Grand.Business.Core.Interfaces.Authentication → Grand.Web.Common.Security.Authentication. A theme or plugin deriving from ThemeContextBase or injecting ICookieOptionsFactory updates its using and is rebuilt.
  • Call sites of IGrandAuthenticationService do not change. No in-repo plugin implements or subclasses the moved types.
  • Registration order: the default IGrandAuthenticationService, ICookieOptionsFactory and IApiAuthenticationService are now registered by Grand.Web.Common (startup Priority 0) instead of Grand.Business.Authentication (Priority 100). A plugin or module with Priority 1–100 that registers its own implementation of one of them used to be overridden by the core one; now its implementation wins. No in-repo plugin or module registers these services.

Testing

Automated: the solution builds with no new warnings. Grand.Business.Authentication.Tests (295), Grand.Web.Common.Tests (538) and Grand.Web.Tests (152) pass. Test count across the two auth/web-common projects is unchanged (679 before and after).

Manual (not run yet):

  1. Storefront: log in as a registered customer, reload (cookie session survives), log out. A guest gets a customer cookie and keeps the cart across requests.
  2. Admin, Store owner and Vendor panels: log in and out; switch the admin theme (cookie written via ICookieOptionsFactory).
  3. With the OpenID Connect login plugin configured: log in through the provider, then log out; the provider session ends.
  4. Web API: get a token for an API user and call /api/... with Authorization: Bearer <token> → 200; call without a token → 401.

CookieAuthenticationService and ApiAuthenticationService were the last
Grand.Business.* classes reading HttpContext. They do nothing but HTTP
work (the customer cookie, SignInAsync/SignOutAsync, the Authorization
header, AuthenticateAsync for the JWT schemes), so no explicit argument
could replace the ambient read: they belong in the web layer.

IGrandAuthenticationService stays in Business.Core, because
ExternalAuthenticationService and plugins use it; only its cookie
implementation and DI registration move. IApiAuthenticationService and
ICookieOptionsFactory have no users outside Web, so the interfaces move
with their implementations, which also takes Microsoft.AspNetCore.Http
CookieOptions out of the Business.Core contracts.

Everything the moved classes depend on is a Business.Core contract, so
Web.Common does not gain a dependency on a Business implementation.

The JwtBearerAuthenticationService tests that lived in
ApiAuthenticationServiceTests stay in Grand.Business.Authentication.Tests
as JwtBearerAuthenticationServiceTests.
Copilot AI balanced review requested due to automatic review settings October 18, 2026 10:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@KrzysztofPajak
KrzysztofPajak merged commit 4338d56 into develop Oct 18, 2026
8 of 9 checks passed
@KrzysztofPajak
KrzysztofPajak deleted the refactor/http-auth-services-to-web branch October 18, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants