Skip to content

Browser sign-in with OpenID Connect: core (5/7) - #895

Merged
KrzysztofPajak merged 111 commits into
developfrom
feature/oidc-login-core
Oct 11, 2026
Merged

KrzysztofPajak merged 111 commits into
developfrom
feature/oidc-login-core

Conversation

@KrzysztofPajak

@KrzysztofPajak KrzysztofPajak commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Type: feature

Issue

Customers and staff should be able to sign in to the store with the same identity provider, without silent account creation and without weakening the protection against account takeover. Part 5 of the stack (base: PR 4) - the core pieces of browser sign-in.

Solution

  • IOidcCustomerResolver.ResolveForLogin / LinkIdentity: never registers; an existing link signs in; an existing account with a provider-verified e-mail links automatically, except back-office accounts and customers with GrandNode two-factor authentication, which (like unverified e-mails) connect once with their store password; no account leads to the registration form; registration disabled, an inactive account or an account linked to another identity is refused.
  • IExternalRegistrationContext: the pending provider identity lives briefly (15 minutes, absolute, non-persistent) in the existing ExternalAuthentication cookie; only the server reads it.
  • /register external mode: the e-mail comes from the provider (read-only, the form value is ignored). Registration type: a verified e-mail turns e-mail validation into standard, an unverified one turns standard into e-mail validation, admin approval stays approval.
    • Verified e-mail: no password fields (a random password; "forgot password" sets one), and the identity is connected. If connecting fails, the account stays and the customer is told.
    • Unverified e-mail: the customer chooses a password and the identity is not connected; after activation it connects with that password, like any existing account. Connecting at registration would let whoever holds the identity keep the account once the owner of the address activates it, even after a password reset.
    • Resources in DefaultLanguage.xml and en_250.xml.
  • Login keeps a local returnUrl across the two-factor step (needed by "connect your account").
  • My account: the "Remove" link of external sign-in connections called an undefined function; it now uses the existing confirm-delete behaviour (this fixes every external method).

Breaking changes

None. RegisterValidator and AccountController.Register take one more injected service; without a pending identity, registration behaves exactly as before.

Testing

  1. dotnet test for Grand.Business.Authentication.Tests, Grand.Web.Common.Tests and Grand.Web.Tests - all pass.
  2. Regular registration and login (with and without 2FA) behave as before.
  3. My account, external connections, Remove: asks for confirmation and removes the row (for example with the Google plugin).
  4. The browser flow itself is exercised with the plugin in PR 6.

… conditions, scheme always registered

The derived audience and resource_metadata URL come from the store resolved for the request, never from the Host header. Resources with account conditions are refused until those checks exist. With the resource server disabled the Oidc scheme is still registered with no issuer and no keys, so endpoints naming it answer 401. authorization_servers publishes the Authority as configured, an http Authority requires RequireHttpsMetadata=false, garbage tokens log at Information, and the startup skips an uninstalled database.
…e request through a request customer resolver
…back-office accounts; one identity per customer
…, staff and unverified e-mails link by password
…8 and Zitadel v4.19

Web API module flag, provider switch and identity links, Keycloak resource indicators accepted with the
Audience mapper, Zitadel role assertion for user_grants, client_id instead of azp, project-wide aud, and the
target reading user.human.is_email_verified.
…identity per customer enforced by the link id
…identity per customer enforced by the link id

# Conflicts:
#	src/Business/Grand.Business.Authentication/Services/OidcCustomerResolver.cs
…tomer resolution, Deny defaults for the Web API
…s-e2e

# Conflicts:
#	src/Business/Grand.Business.Core/Interfaces/Authentication/IJwtBearerAuthenticationService.cs
#	src/Business/Grand.Business.Core/Interfaces/Customers/IUserApiService.cs
#	src/Core/Grand.Domain/Customers/UserApi.cs
#	src/Web/Grand.Web/App_Data/appsettings.json
Base automatically changed from feature/oidc-guides-e2e to develop October 11, 2026 16:10
…ecting fails

An identity whose e-mail the provider did not verify registers with a typed
password and is not connected; it connects later with that password, like an
existing account. Otherwise whoever holds that identity would keep the account
once the owner of the address activates it, even after a password reset.
A failed connection after registration now tells the customer. The random
password of a connected account is 32 random bytes.
Copilot AI balanced review requested due to automatic review settings October 11, 2026 16:24

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 e034fbd into develop Oct 11, 2026
6 checks passed
@KrzysztofPajak
KrzysztofPajak deleted the feature/oidc-login-core branch October 11, 2026 16:39
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