Skip to content

Split ExternalAuthenticationService into a Business result and a Web sign-in - #944

Merged
KrzysztofPajak merged 2 commits into
developfrom
refactor/ExternalAuthentication
Oct 18, 2026
Merged

KrzysztofPajak merged 2 commits into
developfrom
refactor/ExternalAuthentication

Conversation

@KrzysztofPajak

Copy link
Copy Markdown
Member

Part of the work on keeping ASP.NET Core out of Business (after #943)
Type: feature

Issue

ExternalAuthenticationService was the only Grand.Business.* user of IGrandAuthenticationService, and it and IExternalAuthenticationService returned IActionResult (using Microsoft.AspNetCore.Mvc in Grand.Business.Core). Signing in through a cookie and picking a redirect are HTTP concerns, so the domain part (find the associated customer, associate the account, register, raise the events) could not be used without MVC.

Reproduce: grep -rln -E 'IGrandAuthenticationService|Microsoft.AspNetCore.Mvc' src/Business --include=*.cs lists ExternalAuthenticationService.cs, IExternalAuthenticationService.cs and IGrandAuthenticationService.cs.

Solution

  • IExternalAuthenticationService.Authenticate(parameters, customer, store, language) returns ExternalAuthResult (Grand.Business.Core.Utilities.Authentication, same shape as OidcLoginResult): Status, Customer, Errors. Statuses: LoggedIn, Registered, Associated (the account is, or now is, linked to the customer already signed in), AwaitingApproval, AlreadyAssigned, RegistrationDisabled, Error. The service no longer signs in and no longer takes returnUrl; CustomerLoggedInEvent / CustomerRegisteredEvent are still published there.
  • New IExternalSignInService / ExternalSignInService in Grand.Web.Common.Security.Authentication: calls the Business service, signs in on LoggedIn / Registered (after the event, as before) and maps the result to the same redirects as before (HomePage or returnUrl, RegisterResult Standard / AdminApproval, Common/ExternalAuthenticationError with the errors). Its Authenticate keeps the old signature.
  • IGrandAuthenticationService has no Business user left and moves to Grand.Web.Common.Security.Authentication, next to CookieAuthenticationService. Call sites change only their using.
  • Facebook and Google login controllers call IExternalSignInService.Authenticate; they still use IExternalAuthenticationService for AuthenticationProviderIsAvailable.

The grep above now returns nothing. Grand.Business.Authentication still depends on ASP.NET Core through its StartupApplication and the JWT services; that is a separate step.

Breaking changes

  • IExternalAuthenticationService.Authenticate returns Task<ExternalAuthResult> instead of Task<IActionResult> and has no returnUrl parameter. External login plugins call IExternalSignInService.Authenticate (same arguments as the old method) instead.
  • ExternalAuthenticationService: the constructor no longer takes IGrandAuthenticationService; the protected virtual helpers return ExternalAuthResult and lost returnUrl; Error(...) moved to ExternalSignInService.
  • Namespace of IGrandAuthenticationService: Grand.Business.Core.Interfaces.Authentication → Grand.Web.Common.Security.Authentication, assembly Grand.Web.Common.

Testing

Automated: the solution builds. Grand.Business.Authentication.Tests (299), Grand.Web.Common.Tests (545), Grand.Web.Tests (152), Grand.Web.Admin.Tests (1578) and Grand.Modules.Tests (379) pass. New tests cover every ExternalAuthStatus in the Business service and the sign-in / redirect mapping in ExternalSignInServiceTests.

Manual (not run yet), with the Google or Facebook login plugin configured:

  1. As a guest, log in with an account already linked to a customer → signed in, redirected to returnUrl or the home page, guest cart merged.
  2. As a guest, log in with a new account (registration Standard) → customer registered, signed in, "registration completed" page. With AdminApproval → not signed in, "awaiting approval" page. With registration disabled → external authentication error page.
  3. Signed in, link a new external account → redirected back, account visible on the customer info page. Link an account that belongs to another customer → external authentication error page.

…sign-in

ExternalAuthenticationService.Authenticate returns ExternalAuthResult instead of
IActionResult and no longer signs in. The new ExternalSignInService in Web.Common
signs the customer in and maps the result to the same redirects as before.
IGrandAuthenticationService has no Business user left and moves to Web.Common.
Copilot AI balanced review requested due to automatic review settings October 18, 2026 11:40

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.

- Redirect to the home page when returnUrl is not a local URL
- Map every ExternalAuthStatus explicitly and throw on an unknown one
- Translate the already-assigned and registration-disabled messages in Web
  (new resources in DefaultLanguage.xml and Upgrade/en_250.xml)
- Expose ExternalAuthResult.Errors as IReadOnlyList and drop the unreachable
  registration error branch
- Document that callers of IExternalAuthenticationService.Authenticate must
  sign in a LoggedIn or Registered customer
@KrzysztofPajak
KrzysztofPajak merged commit 48afee0 into develop Oct 18, 2026
9 checks passed
@KrzysztofPajak
KrzysztofPajak deleted the refactor/ExternalAuthentication branch October 18, 2026 12:52
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