58 min readSep 26, 2026by jakub

Changelog

Current version: 1.3.0

1.3.0 — 2026-09-26

Base (core)

Fixed

  • Checkout sign-in buttons never rendered. Both themes attached them to checkout.login.container, which no Magento, Hyvä or Luma checkout declares, so the "Show on Checkout" setting did nothing on any store. The buttons now render above the checkout in content, for guests only (Block\CheckoutSocialButtons). Verified in a browser on Luma and Hyvä.
  • A sign-in started at checkout now returns to checkout. Whatever "Redirect After Login" is set to, a shopper who signs in from the checkout or the cart comes back to it, with the guest cart Magento merges into the account on login. The redirect decision is now one class (Model\Auth\PostLoginRedirect) shared by the callback and the confirmation step, which each carried their own copy.
  • Hyvä sign-in links carried no return page. SocialButtons::getLoginUrl() omitted the referer parameter the Luma templates add inline, so on Hyvä the "previous page" redirect setting never worked.
  • Full-width Luma buttons overflowed their column by their padding and border (content-box sizing), scrolling the checkout sideways. The button now uses box-sizing: border-box.
  • Confirmation dropped the provider's email verification for an unchanged address. Verification is kept when the submitted email is the provider-verified one (trimmed, case-insensitive) and lost when it is changed.
  • Newsletter consent on the confirmation form was ignored. Ticking "Subscribe to our newsletter" now subscribes the new customer in the current store; a failure is logged and never blocks sign-up. magento/module-newsletter is now declared — Magento_Customer already requires it.
  • A refreshed token could keep a stale expiry and profile. TokenStorage::save() now always writes the new expiry (none when the token has none) and the new profile data.
  • Admin diagnostics used the default store view. The Redirect URI shown in Stores > Configuration and Test Connection now use the website or store view being edited. Test Connection checks the saved configuration for that scope.

Added

  • Model\Auth\PendingProfileHandoff, shared by the redirect callback and Google One Tap to store the pending profile before the confirmation step.
  • Tests for each fix above, including Test/Unit/Layout/CheckoutButtonsLayoutTest, Test/Unit/Layout/DefaultHandleCacheabilityTest and Playwright specs for checkout sign-in and newsletter consent (Test/E2E/specs/checkout-signin.spec.ts, newsletter-consent.spec.ts).
  • Test/E2E/run-in-docker.sh runs the Playwright suite in Playwright's official image (the reference for screenshot baselines, and the way to run WebKit on hosts Playwright does not support), with its own network so host network changes cannot abort requests; a generated config smoke spec checks every provider's settings render and save.

Removed

  • The experimental GraphQL module (Qoliber_SocialLoginGraphQl) is discontinued. Its headless flow was never complete (providers returned to the storefront callback), and it was not part of the released suite. A headless storefront implements the sign-in flow itself.

Amazon

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

Apple

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

Bitbucket

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

Discord

Fixed

  • Unlinking never revoked the token at the provider: the client secret is no longer decrypted a second time, which made every revocation fail.

Facebook

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

GitHub

Fixed

  • Unlinking never revoked the token at the provider: the client secret is no longer decrypted a second time, which made every revocation fail.

GitLab

Fixed

  • Unlinking never revoked the token at the provider: the client secret is no longer decrypted a second time, which made every revocation fail, and revocation goes to the configured GitLab instance instead of always gitlab.com.

Google

Fixed

  • Full page caching was disabled on every storefront page. The One Tap block was marked cacheable="false" in default.xml, even with One Tap switched off. The page HTML is now public and cacheable; the per-visitor parts (nonce, whether to prompt) come from a private, no-store endpoint, sociallogin/onetap/config, fetched by view/frontend/web/js/onetap.js.
  • One Tap sign-in failed from a prompt left open in another tab. A session now holds one outstanding nonce that every page view shares until a sign-in uses it, instead of minting one per page.
  • One Tap could not complete registration when confirmation is required. The pending profile is now stored before redirecting to the confirmation step (PendingProfileHandoff).

Changed

  • onetap.phtml emits only public markup (a root element and the script). A theme that overrides it must be updated to match. The unused form key is no longer sent.

Hyvä theme support

Fixed

  • Checkout sign-in buttons. The checkout layout is now hyva_checkout_index_index.xml, so it applies to Hyvä themes only — the old checkout_index_index.xml also applied to Luma store views of the same installation. The buttons render above the checkout for guests, and a sign-in started there returns to it.
  • Identical rendering on Tailwind 3 and 4. shadow-sm is a larger shadow on Tailwind 4 and focus:outline-none drops the transparent outline that keeps a focus indicator in forced-colors mode. Both are replaced by values that compile identically on Tailwind 3.4 (Hyvä 1.3) and 4.1 / 4.3 (Hyvä 1.4 / 1.5).

Added

  • HyvaTailwindCompatTest also fails on utilities rescaled between Tailwind 3 and 4 (shadow-sm, rounded-sm, blur-sm, drop-shadow-sm, outline-none, bare ring) and on Tailwind-4-only names (*-xs, outline-hidden, ring-3).

Upgrade notes

  • Rebuild the Hyvä theme's Tailwind bundle after upgrading (bin/magento hyva:config:generate, then npm run build in the theme's web/tailwind).

LINE

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

LinkedIn

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

Microsoft

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

OAuth 2.0 strategy

Fixed

  • TikTok sign-in failed after authorization. The PKCE token exchange now honours a provider's client_id_param (TikTok sends client_key), and a TikTok response whose error.code is "ok" is treated as success rather than an error.

OpenID Connect strategy

Added

  • Identity providers on a private network. The JWKS check refuses any host that resolves to a private address, which blocked self-hosted providers on an intranet (e.g. Keycloak) and a development store's own mock provider. The server owner can now name exact hostnames in app/etc/env.php: 'qoliber_social_login_jwks_allowed_private_hosts' => ['idp.internal.example']. It relaxes only the DNS check — HTTPS is still required, subdomains are not included, and literal private IPs and localhost are still refused. Deployment config only, so it cannot be enabled from the admin.

PayPal

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

Pinterest

Security

  • Pinterest customers are identified by their account ID, not their username. Pinterest lets a username change and be taken by another account, so a later owner of a name could sign in as the earlier customer. The link now uses the user_account id; a response without it is refused, never matched by username. Customers who linked Pinterest before this release must link it once more — existing username links are deliberately not converted.

Reddit

Fixed

  • Unlinking never revoked the token at the provider: the client secret is no longer decrypted a second time, which made every revocation fail.
  • Reddit's token endpoint needs HTTP Basic authentication. The token request now sends it.

Slack

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

TikTok

Fixed

  • TikTok sign-in works again; the fix is in qoliber/social-login-oauth2 (see its changelog). No code change in this module.

Twitch

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

X (Twitter)

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

Mock provider (testing)

Changed

  • Version bump to keep the suite aligned; no code change in this module. See the Base, Google, Pinterest, OpenID and Hyvä changelogs for this release.

Mock OAuth 2.0 server (testing)

Changed

  • The "Success - New User" scenario returns a random email on every sign-in instead of one derived from the current second, so two sign-ups in the same second no longer collide on one customer.

Mock OpenID Connect server (testing)

Fixed

  • ID tokens now pass real verification. The mock signed with a fixed string and published a placeholder key, so no sign-in through it could pass signature verification. It now signs with an RSA key generated once per installation (var/qoliber_mock_oidc/signing-key.pem, owner-readable only) and publishes the matching key.
  • The nonce reaches the ID token. /authorize dropped the client's nonce and /token looked for it in a parameter no client sends; it now travels in the authorization code, as with a real provider.

Added

  • Unit tests for the token signing and nonce handling (the module had none).

1.2.0 — 2026-09-22

Base (core)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Amazon

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Apple

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Bitbucket

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Discord

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Facebook

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

GitHub

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

GitLab

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Google

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Hyvä theme support

Changed

  • Stylesheet moved into the Hyvä Tailwind build. The module shipped view/frontend/web/css/social-login.css and loaded it with <css src> from default.xml. That path is never read by the Hyvä Tailwind build, so the provider brand colours arrived as a separate render-blocking request outside the compiled styles.css, and duplicated colour tokens that tailwind.config.js already declared. The two had already drifted: the config declared mock with no rule, while twitch, pinterest, slack and gitlab had rules with no token.
  • The module now uses the four-file layout the Hyvä build actually reads — module.css (Tailwind 4, Hyvä 1.4+), tailwind.config.js (Tailwind 3, Hyvä 1.3), tailwind-source.css (Tailwind 3) and components/social-login.css holding the rules as the single source of truth. All 18 provider colours are declared in both the v3 and v4 token syntaxes, with identical values, and each token backs exactly one rule.

Removed

  • view/frontend/web/css/social-login.css and view/frontend/layout/default.xml. Integrators must rebuild the theme's Tailwind bundle (npm run build in the theme's web/tailwind) — a cache flush alone will not pick these rules up.

Added

  • Test/Unit/View/HyvaTailwindCompatTest — fails on each way this module can silently ship broken CSS to one Hyvä line: a module.css without its own @source (which suppresses the automatic one and purges every template class on Tailwind 4), a missing tailwind.config.js (which purges them on Tailwind 3), colour tokens declared for only one version, tokens and rules drifting apart, and the version-specific utilities bg-opacity-*, bg-linear-* and postfix !.

LINE

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

LinkedIn

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Microsoft

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

OAuth 2.0 strategy

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

OpenID Connect strategy

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

PayPal

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Pinterest

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Reddit

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Slack

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

TikTok

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Twitch

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

X (Twitter)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Mock provider (testing)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Mock OAuth 2.0 server (testing)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

Mock OpenID Connect server (testing)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Hyvä module moves its stylesheet into the Tailwind build — see its changelog.

1.1.2 — 2026-09-01

Base (core)

Fixed

  • Provider icons in the Luma customer dashboard rendered at hundreds of pixels. The "Connect another account" list emits .qoliber-connect-account__icon, but that class was never styled — the sibling "Connected Social Accounts" list was. An <img> with no CSS dimensions falls back to the SVG's intrinsic size, and the provider icons are inconsistent: Google declares 48x48 and GitHub 98x96, while Bitbucket, Discord, Slack, Twitch and Reddit carry a viewBox only and so resolved to the replaced-element default of 300x150 scaled to their aspect ratio. The block is now styled, with the icon at the suite's standard 24px. Hyvä was unaffected — its template sizes icons inline.

Added

  • Test/Unit/View/LumaIconSizingTest — asserts that every qoliber-*__icon class a Luma template emits has a stylesheet rule setting BOTH width and height. A missing rule is not a syntax error in any language involved, so nothing else could have caught this.

Amazon

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Apple

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Bitbucket

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Discord

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Facebook

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

GitHub

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

GitLab

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Google

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Hyvä theme support

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

LINE

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

LinkedIn

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Microsoft

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

OAuth 2.0 strategy

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

OpenID Connect strategy

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

PayPal

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Pinterest

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Reddit

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Slack

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

TikTok

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Twitch

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

X (Twitter)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Mock provider (testing)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Mock OAuth 2.0 server (testing)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

Mock OpenID Connect server (testing)

Changed

  • Version bump to keep the suite aligned; no code change in this module. The Base module fixes oversized provider icons in the Luma customer dashboard — see its changelog.

1.1.1 — 2026-08-31

Base (core)

Security

  • A social identity could sign a visitor into another website's customer account. qoliber_social_login_account stores one link per provider identity with no website dimension, and the link was followed before any website check. Magento ships customer/account_share/scope as 1 (Per Website), where customer entities are isolated per website — so on a multi-website store, signing in with a provider on website B authenticated the customer that identity was linked to on website A. Authentication now only follows a link whose customer belongs to the website being browsed, and falls through to that website's own linking rules otherwise. Stores using Global account sharing are unaffected and behave exactly as before.
  • An unconfirmed account could sign in even where email confirmation was mandatory. Social login does not pass through Magento\Customer\Model\AccountManagement\Authenticate, which is where Magento rejects an unconfirmed customer, so Require Emails Confirmation was bypassed entirely by using a provider — and "Require Verified Provider Email" is off by default, so the provider address need not have been verified either. LoginService now consults AccountConfirmation and refuses, following the store's policy rather than merely the presence of a confirmation hash.
  • The provider's entire profile response is no longer stored verbatim. Every authentication wrote the raw response to profile_data as unencrypted JSON — phone numbers, locale metadata, friend counts, whatever the provider chose to include. The Save Profile Photo / Collect Gender / Collect Birthday settings only ever gated mapping onto customer attributes, so a merchant who switched them off still had that data retained indefinitely. Model\Auth\ProfileDataSanitizer now applies a whitelist at the storage boundary and honours those settings as an actual retention policy; unrecognised fields are dropped, so a provider adding one cannot silently start populating a merchant's database.

Fixed

  • Token purging no longer deletes the account link. qoliber:social-login:tokens:purge and its nightly cron deleted the whole row once the access token had been expired past the grace window, silently unlinking the customer's social account. The row is the provider-identity -> customer link, and the customer may be unable to re-link at all — automatic linking can be off, the provider may not return a verified email, or the address may have changed. The purge now clears access_token, refresh_token and token_expires_at and keeps the row; only an explicit unlink or customer deletion (FK cascade) removes it. Rows already cleared are no longer re-counted on every run.
  • Auto-approved sign-up failed at random on the store's password policy. The throwaway password given to createAccount() came from Random::getRandomString(16), which draws only from lower case, upper case and digits — so a draw that happened to miss one of them was rejected with "Minimum of different classes of characters in password is 3", failing roughly one sign-up in twelve, and every sign-up on a store configured to require all four classes. The generator now guarantees one character from each class Magento counts, and reads minimum_password_length exactly as AccountManagement does so the two cannot disagree.
  • Login events fired twice. LoginService regenerated the session id and dispatched customer_login / customer_data_object_login itself, then called setCustomerDataAsLoggedIn(), which does both again — so every observer ran twice (duplicate third-party side effects) and the session was regenerated a second time mid-login. The duplicates are gone; Magento's own call still performs both.
  • One provider identity can now hold one link per website, enforced by the database. The unique index covered (provider_code, provider_user_id), which under per-website account sharing made the second website's link unstorable: after the isolation fix above, signing in there created the customer and then died on Unique constraint violation found, leaving an orphaned customer_entity row. The table gains a website_id column, denormalised from the linked customer and backfilled for existing rows, and the index becomes (provider_code, provider_user_id, website_id) — so "this identity is already claimed here" stays a database invariant rather than a check performed before inserting, which two concurrent link attempts could both pass. LinkingService still refuses a claimed identity up front (any other customer under Global sharing, another customer on the same website under per-website sharing) and now reports the loser of a race with the same message instead of a raw database error. Both earlier index shapes are whitelisted in db_schema_whitelist.json so setup:upgrade actually drops them — Magento names a constraint from its columns and silently refuses to remove one whose physical name it has not been given, which would have left the old key in place on every upgraded store.
  • Facebook data-deletion callbacks now remove every link held by the identity rather than only the first, so a request is honoured across all websites.
  • The entire admin configuration section failed to render. RedirectUriDisplay and TestConnectionButton extended Data\Form\Element\AbstractElement, but a system.xml <frontend_model> is attached with setRenderer() and instantiated through the layout, which requires a BlockInterface. Opening Stores > Configuration > Social Login threw LogicException: ... does not implement BlockInterface, so no provider could be configured at all. Both now extend Magento\Config\Block\System\Config\Form\Field, as Magento's own field renderers do, and read their provider code from the element's field_config. Present since 1.0.0; only loading the page could catch it, which is what the end-to-end work in this release finally did.
  • The admin Test Connection button never worked. Its onclick was emitted into a double-quoted attribute while json_encode keeps its own double-quote delimiters, so the browser terminated the attribute at the first JSON quote and the handler was never called. The attribute is now single-quoted (JSON_HEX_APOS already escapes apostrophes in the data, so nothing can break out).
  • The Redirect URI shown in admin was wrong, and leaked the admin secret key. The field is copied verbatim into an OAuth provider's console and its own comment says to configure that exact URL, but it was built from the injected UrlInterface — which the adminhtml area preferences to Magento's BACKEND url model. With the default admin/security/use_form_key that yields an admin route carrying key/<admin secret>, and it omitted the provider path segment that AbstractProvider::getRedirectUri() actually sends. A merchant following the instruction registered a URL the module never uses (guaranteeing redirect_uri_mismatch) while disclosing the admin secret key to a third party. It is now built from the storefront base URL and carries the provider segment.
  • etc/adminhtml/routes.xml referenced urn:magento:module:Magento_Backend:etc/routes.xsd, which does not exist in Magento 2.4.7; it now uses urn:magento:framework:App/etc/routes.xsd as core does.

Added

  • TokenStorageInterface::getAllByProviderUserId() — returns every link held by one provider identity, oldest first. getByProviderUserId() remains and now returns the oldest deterministically instead of whatever the database offered first.
  • Test/Integration/Model/MultiWebsiteLinkIsolationTest — drives the real AuthenticationService with only the provider pool stubbed, across both account-sharing modes, and asserts one identity keeps a separate link per website.
  • Test/Integration/Model/Auth/LoginServiceTest — the confirmation policy against a real unconfirmed customer, and exactly-once dispatch of customer_login / customer_data_object_login with the session id still regenerated.
  • Test/Integration/Model/Maintenance/ExpiredTokenPurgerTest — exercises the purge filter as SQL, and pins that the identity link survives it.
  • Test/Unit/Model/Auth/ProfileDataSanitizerTest — each retention gate, independently, plus the drop of anything outside the whitelist.
  • Test/Integration/Etc/SocialAccountSchemaTest — reads the keys back off the database the declarative schema produced, so neither an identity-only nor a merely customer-scoped unique key can be reintroduced.
  • Test/Integration/Model/IdentityLinkUniquenessTest — writes through the resource model with every application guard bypassed, which is what a lost race amounts to: the database refuses a second link for one identity on one website, and still allows one per website.
  • Setup/Patch/Data/BackfillSocialAccountWebsiteId — fills website_id on links created before 1.1.1 from customer_entity; without it every pre-existing row would claim website 0 and the second website's link would be unstorable again.
  • Test/Integration/Model/Auth/CustomerCreatorTest — sign-up against the real AccountManagement under the strictest character-class policy, a 40-character minimum, and repeated attempts at the default policy.
  • Controller and block coverage for the remaining admin surface: LinkTest (a link writes the SHARED session keys the login callback reads — writing link-specific keys is what made linking impossible — and a mismatched state is refused in constant time and drops the intent), TestConnectionButtonTest (the provider code and form key reach the request, the attribute cannot be terminated by the JSON quoting, and neither a quote nor an apostrophe in the provider code can break out).
  • Controller coverage for the paths this release touched, none of which had an executing test: CallbackTest (state validation, link-intent routing, and that a stale intent is cleared on every failure path so a later login cannot silently become a link), UnlinkTest (the revocation entry point), and ConfirmPostTest (a customer-typed email is never treated as provider-verified). Callback previously had only an integration test, which has never executed.
  • Test/Unit/Block/Adminhtml/RedirectUriDisplayTest — pins the redirect URI to what the provider sends, and asserts it never contains /key/ or the admin front name.
  • Test/E2E/specs/admin-credentials.spec.ts — credentials survive a save/reload, and the encrypted client_secret is never rendered back as plaintext.
  • Test/E2E/specs/admin-redirect-uri.spec.ts — checks EVERY installed provider's redirect URI is a storefront URL carrying its own provider segment and no admin key.
  • Test/E2E/support/env.ts — missing configuration now FAILS under CI instead of skipping, so an unrun spec can no longer report green.
  • A CI job that installs a real Magento, serves it, and runs the Playwright suite against it — previously no job ran E2E at all.
  • Test/Integration/Etc/SystemConfigStructureTest — loads the admin configuration through the real Magento config stack, which is the layer the setup:upgrade failure actually occurred at. Asserts the section merges, every installed provider contributes its group, and each Test Connection field carries the provider code its group owns.
  • Test/Unit/Etc/SystemXmlProviderCodeTest — runs each system.xml through Magento's own Structure\Converter and asserts the Test Connection field still carries its provider code. The fix above moved that value to <attribute type="provider_code"> on the strength of reading the converter; this executes it, because a wrong value would make the button test the wrong provider silently, with the XML still valid.
  • Test/E2E/specs/admin-config.spec.ts — first admin coverage: the configuration section renders its provider groups and Test Connection posts the provider it belongs to. Neither defect fixed in this release is reachable from a browser (both abort before a storefront exists), so this covers the part that is.
  • Test/Unit/Etc/XmlSchemaValidationTest — validates every config XML in the suite against the Magento XSD it declares. This is the gate that was missing: no PHP linter, PHPStan, PHPCS or unit test opens these files, so an invalid one could only be caught by a live setup:upgrade.
  • Test/Unit/Etc/QoliberDependencyConstraintsTest — separates suite-internal dependencies (which track the suite version) from external Qoliber packages (which do not), so a version bump can no longer raise a constraint past what is published.
  • Test/E2E/ — the Playwright suite now ships inside the package instead of living only in the development workbench, so it is visible and runnable from the repository.

Changed

  • Declares magento/module-config. The admin field renderers extend Magento\Config\Block\System\Config\Form\Field, and etc/adminhtml/system.xml uses Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Amazon

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Apple

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Bitbucket

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Discord

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Facebook

Fixed

  • Data-deletion callbacks now remove every link held by the Facebook identity, not just the first. Under per-website customer sharing — Magento's shipped default for customer/account_share/scope — one Facebook identity owns a separate customer on each website, so a deletion request left the remaining links in place.
  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

GitHub

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

GitLab

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Google

Added

  • Test/Unit/Controller/Login/OneTapTest — pins the guards added for this endpoint: a disabled module, disabled provider, unconfigured provider or provider without One Tap support must all refuse the credential, CSRF failure stops authentication, and a missing session nonce is fatal. The credential arrives in a browser POST, so these are the difference between a gate and a comment.

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Hyvä theme support

Fixed

  • The package can be installed again. 1.1.0 required qoliber/hyva-module-registration: ^1.1, but that package is released separately and its highest version is 1.0.2, so Composer refused to resolve the module at all. The constraint is back to ^1.0.

LINE

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

LinkedIn

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Microsoft

Added

  • Test/Unit/Model/Config/Source/TenantTest — asserts every selectable tenant is handled by the issuer logic. A tenant option outside MULTI_TENANT_ALIASES would be validated against a literal issuer that can never match, reintroducing the 1.1.1 defect for one option only.

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

OAuth 2.0 strategy

Changed

  • Version bump to keep the suite aligned; no code change in this module.

OpenID Connect strategy

Changed

  • Version bump to keep the suite aligned; no code change in this module.

PayPal

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Pinterest

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Reddit

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Slack

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

TikTok

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Twitch

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

X (Twitter)

Fixed

  • setup:upgrade no longer aborts. etc/adminhtml/system.xml used a <provider_code> element that no Magento schema allows, so Magento rejected the file outright (Element 'provider_code': This element is not expected.) and the upgrade failed, making the module impossible to install. It now uses <attribute type="provider_code">, the XSD-valid form that Magento's config converter maps to the same getData('provider_code') the Test Connection button already reads — no behaviour change.

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.

Mock provider (testing)

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
  • Version bump to keep the suite aligned; no code change in this module.

Mock OAuth 2.0 server (testing)

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
  • Version bump to keep the suite aligned; no code change in this module.

Mock OpenID Connect server (testing)

Changed

  • Declares magento/module-config. etc/adminhtml/system.xml uses Magento\Config\Model\Config\Backend\Encrypted — the backend model that encrypts the client secret at rest — and Magento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only — Magento_Config ships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
  • Version bump to keep the suite aligned; no code change in this module.

1.1.0 — 2026-08-23

Base (core)

Fixed

  • Provider-side token revocation now receives a usable token. Unlink passed the encrypted stored token straight to the provider, so every revoke call failed; the failure was swallowed and the local link deleted anyway, leaving the token live at the provider. Tokens are now decrypted before going over the wire.
  • Account linking from the My Account page can complete. The link flow wrote its own social_link_* session keys, but a provider app has a single registered redirect URI, so the authorization always returned to the shared login callback — which read different keys and rejected the state. Linking now travels through that callback via an explicit intent marker.
  • The linked-accounts block no longer hides itself from customers with no links, which was precisely the audience needing the "connect an account" action. Available providers are now offered.
  • Blank provider identities are rejected. A profile mapped without an id produced provider_user_id = ''; because the provider-id lookup runs before the email path, one such stored row would match every later identity-less response for that provider and log in as that customer. Guarded at authentication, at linking, and at the storage layer.
  • A refreshed token no longer keeps the previous token's expiry when the provider omits a new one.
  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • TokenStorageInterface::decryptAccessToken() / ::decryptRefreshToken() — anything handing a token back to a provider must go through these.
  • FormPostProviderInterface::getFormPostSessionKeys() — lets a response_mode=form_post provider declare session values that must survive its cross-site POST.
  • Controller\Login\LinkIntentCompleter — owns the link-vs-login decision shared by the link and callback controllers.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Amazon

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Apple

Fixed

  • Apple sign-in no longer fails when the session is dropped. Apple's response_mode=form_post callback is a cross-site POST that drops the session cookie, taking the OIDC nonce with it. With the nonce check now mandatory, that rejected every Apple login; the nonce is carried in the sealed, browser-bound fallback cookie.
  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Bitbucket

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Discord

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Facebook

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Test/Unit/Block/Datadeletion/StatusTest — a known confirmation code reports the deletion and an unknown or absent one reports nothing rather than implying a deletion happened. Also makes getCompletedAt() null-safe: it declared : string but returned formatDate(), which is not guaranteed to be one.
  • Data-deletion status endpoint. Meta's callback contract requires the returned status URL to resolve; the confirmation code was generated and discarded, so that URL 404'd. Codes are now persisted as receipts (qoliber_social_login_deletion_request) and the status page reports the outcome. The receipt deliberately stores no user identifier, and codes come from a CSPRNG rather than being derived from the user id and a timestamp.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

GitHub

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

GitLab

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Google

Fixed

  • The One Tap endpoint now respects the feature switches. The block that renders the prompt checked whether social login and One Tap were enabled, but the endpoint itself did not — leaving a working authentication endpoint behind after a merchant switched the feature off.
  • The One Tap nonce is mandatory. The credential arrives in a browser POST body and is therefore attacker-suppliable, so a Google-issued token for this client that carried no nonce could previously be replayed.
  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Hyvä theme support

Fixed

  • The linked-accounts block no longer returns early for customers with no connected accounts, and now offers the available providers to connect.
  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

LINE

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

LinkedIn

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Microsoft

Fixed

  • Microsoft sign-in works on all tenant options. The expected issuer was built from the configured alias (common / organizations / consumers), but Microsoft mints tokens under the signing directory's own GUID, so the exact-match check could never succeed and every login failed. Alias tenants now validate against a {tid} template resolved from the token; an explicitly configured tenant still matches exactly, so cross-tenant tokens stay rejected.
  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

OAuth 2.0 strategy

Fixed

  • userinfo_headers from a provider config is now honoured on the profile request. The strategy only ever read headers, so a provider declaring headers for the profile call had them silently dropped — breaking any API requiring more than a bearer token.
  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

OpenID Connect strategy

Fixed

  • ID token claim validation no longer fails open. exp, aud, iss and sub were validated only when present, so a token without exp never expired and a token without aud was not bound to this client. All four are now mandatory, per OpenID Connect Core 1.0 §3.1.3.7.
  • The nonce check is mandatory. It previously ran only if the token happened to carry a nonce, so a token without one skipped replay protection entirely.
  • Multi-tenant issuers validate correctly. iss may now be expressed with a {tid} placeholder, resolved from the token's own tid claim — required for providers whose tokens are issued under a concrete directory rather than the configured alias.
  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

PayPal

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Pinterest

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Reddit

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Slack

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

TikTok

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Twitch

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

X (Twitter)

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Added

  • Provider contract test coverage.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Mock provider (testing)

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Mock OAuth 2.0 server (testing)

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

Mock OpenID Connect server (testing)

Fixed

  • PHP 8.1 / 8.2 compatibility restored. Typed class constants (a PHP 8.3+ feature) were present in code declaring support for PHP 8.1, causing a fatal parse error on any store running the declared floor. The full ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 range is now verified by parsing and running the unit suite on every one of those interpreters.

Changed

  • composer.json now declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.

1.0.0 — 2026-05-26

Base (core)

Added

  • Initial release of Qoliber Social Login Base module.
  • Core framework for social authentication — provider pool and registration system, OAuth state management with CSRF protection, customer account linking, social account database schema (qoliber_social_login_account) with FK-CASCADE on customer delete and unique (provider_code, provider_user_id) constraint to prevent identity hijacking.
  • Frontend login buttons block + My Account linked-accounts management + sociallogin confirm flow.
  • Event system for extensibility: qoliber_social_login_before_auth, _after_auth, _customer_login, _customer_linked, _customer_created, _account_unlinked.
  • Checkout page social login integration (layout XML with config toggle).
  • "Require Verified Provider Email" + "Send Password Email" + "Send Linking Email" config settings, all wired to the auth flow.
  • Extensible child block container (social_login_confirm_extra) for merchant GDPR consent + newsletter subscription checkbox on confirm page.
  • CSP whitelist + ACL definitions + button styles for all 18 providers in LESS.
  • Model\Auth\PendingProfileSessionStorage — session-backed storage for the pending-profile payload. Encrypts access_token / refresh_token at rest via Magento\Framework\Encryption\EncryptorInterface so a session-file disclosure cannot leak live OAuth credentials.
  • Model\Auth\PostAuthNotifier — owns welcome-email and linking-email side-effects.
  • Model\Auth\LoginService — owns session login, lock-check, regenerateId (session fixation defence), and the customer_data_object_login event.
  • Model\Auth\CustomerCreator — owns customer-from-profile creation, including the MailException repository-fallback path.
  • Model\Auth\LinkingService — owns link/unlink lifecycle, hijack rejection when the social identity is already bound to another customer, and the linked/unlinked events.
  • Controller\Login\AppleFormPostRelay — cross-site POST → same-origin GET relay for Apple Sign In's response_mode=form_post (SameSite=Lax cookie compatibility).
  • Controller\Login\CallbackErrorResolver — three error-path branches (provider error, authentication exception, unexpected exception) extracted into a testable side-effect channel.
  • Model\MockProviderGate — enforces that mock providers (Qoliber_SocialLoginMockOAuth2, Qoliber_SocialLoginMockOpenID) are inert in MAGE_MODE=production unless the operator explicitly sets qoliber_social_login_mock_enabled: true in app/etc/env.php.
  • Model\Security\CallbackRateLimiter — cache-backed per-(IP+session) counter that caps OAuth callback attempts to N/minute. Config: qoliber_social_login/security/callback_rate_limit (default 20; 0 disables). Over-limit returns a generic flash message and logs at warning.
  • Model\Logging\SensitiveDataRedactor — produces log-safe fingerprints (first 6 chars + length, or preserved-domain email form) for OAuth state, session IDs, codes, ID tokens, and emails. Applied at the state-mismatch and rate-limit log sites in Controller\Login\Callback.
  • Controller\Login\PopupResultEmitter — renders origin-scoped postMessage HTML for the optional popup OAuth flow. User-controlled fields are XSS-hardened via JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP.
  • AuthenticationService::completeAuthenticationWithProfile() — shared post-profile decision ladder used by both the auth-code flow (from authenticate()) and the Google One Tap ID-token flow.
  • Parent-page JS view/frontend/web/js/popup-login.js for opt-in popup OAuth (data-popup-login="1" on the anchor).
  • dev/tools/scaffold-provider.php — one-command CLI to scaffold a new provider module.
  • PHPUnit configuration, factory stubs, and .github/workflows/ci.yml (PHP 8.1–8.4 matrix × PHPUnit + PHPStan L8 + Rector dry-run + phpcs).
  • 213 unit tests passing at PHPStan level 8 (up from 119 during hardening).

Changed

  • Model\AuthenticationService is a thin orchestrator — 14 → 10 dependencies, 378 → ~230 LOC. The public AuthenticationServiceInterface surface is unchanged; third-party callers are unaffected.
  • Controller\Login\Callback slimmed from ~250 LOC to ~175 LOC; Apple-relay and error-handling branches delegate to dedicated collaborators.
  • Controller\Account\ConfirmPost no longer depends on SessionManagerInterface directly — it reads the decrypted pending-profile payload through PendingProfileSessionStorage.
  • Model\Pkce no longer depends on Magento\Framework\Math\Random (removed constructor argument). State nonce is now bin2hex(random_bytes(16)).
  • Model\ProfileMapper collapses the duplicated extractFirstName / extractLastName logic into a single extractNamePart() parameterised helper.
  • All 16 interfaces and exception classes under Api/ carry /** @api */ docblocks, marking the module's frozen public surface per Magento marketplace convention.
  • Frontend button templates (buttons.phtml, buttons-sidebar.phtml) gained aria-label and moved icon images to alt="" + aria-hidden="true" — screen readers announce the provider name exactly once per button (WCAG 2.1 AA).
  • Luma buttons render a CSS spinner revealed via the existing .qoliber-social-button--loading class, matching the Alpine-based spinner in SocialLoginHyva.
  • Confirm page templates aligned between Luma and Hyva (editable name fields, cancel button).
  • Hardcoded terms checkbox replaced with extensible child block.
  • Removed profile-picture avatar from Luma confirm template.
  • Removed mock-provider CSS from shipped styles.
  • Config label renamed from "Require Email Confirmation" to "Require Verified Provider Email".

Fixed

  • PSR-12 compliance (line length, brace formatting).
  • PHPStan level 8 errors (type safety, PHPDoc, unused code).
  • Rector PHP 8.4 (typed constants, #[Override] attributes).

Security

  • Session-bound OAuth state validation. Stateless signed-state fallback removed; the callback requires a session-stored state to match. Comparison is constant-time via hash_equals().
  • OAuth tokens never persisted as plaintext in session. PendingProfileSessionStorage transparently encrypts/decrypts secret fields.
  • CSPRNG state nonce. Moved from Magento\Framework\Math\Random::getRandomString() (no CSPRNG contract) to random_bytes(16).
  • Require verified provider email before auto-linking accounts by email — blocks the #1 social-login account-takeover vector.
  • Open-redirect protection on referer URL (host-match against store base URL).
  • PKCE silent-downgrade prevention (throws exception if code_verifier missing when strategy requires PKCE).
  • Apple form_post POST→GET relay preserves SameSite=Lax session cookies.
  • FPC isolation of per-customer linked-accounts block (cacheable="false") so one user's connections never appear on another user's page.
  • Mock providers inert in production by default — require an explicit env.php flag.
  • Callback rate limiting — 20/minute per (IP, session) by default.
  • Log redaction — state, session ID, tokens, and emails are fingerprinted before reaching the log channel.
  • i18n/en_US.csv seed (69 translatable strings); repo-wide regenerator at dev/tools/generate-i18n-seeds.php.

Pre-1.0.0 hardening (2026-05-06 / 2026-05-07)

Added between the 2026-04-12 audit and the first cut. Folded into the 1.0.0 tag because no public release exists.

Public API

  • Api\AuthenticationServiceInterface extended with completeAuthenticationWithProfile() and completeAccountCreation() so One Tap and the confirm-and-create controller can depend on the @api interface instead of the concrete class. Pinned by a reflection-based regression test.
  • New opt-in Api\ProviderRevokeInterface — providers that expose a token-revoke endpoint can implement it; LinkingService::unlinkAccount calls it best-effort during disconnect.

Operability

  • Cron\PurgeExpiredTokens daily at 03:00 — sweeps rows where token_expires_at < now() - 30 days AND refresh_token IS NULL.
  • bin/magento qoliber:social-login:tokens:purge [--dry-run] [--ttl-days=N] for manual sweeps.
  • Model\Maintenance\ExpiredTokenPurger shared by both. Filter shape: lt on the timestamp + null: true on refresh token.

Schema

  • last_authenticated_at column on qoliber_social_login_account — surfaced in the linked-accounts UI so customers can see staleness.

Admin

  • Controller\Adminhtml\Provider\TestConnection — JSON endpoint backing the per-provider "Test Connection" button. Cheap checks (provider exists, configured, authorize URL builds) — no network probe by design.
  • Block\Adminhtml\Form\Field\RedirectUriDisplay — readonly absolute redirect URI for copy/paste into the OAuth provider's developer console.
  • Block\Adminhtml\Form\Field\TestConnectionButton — admin button + inline XHR + status indicator.
  • etc/adminhtml/routes.xml declaring the qoliber_sociallogin admin frontName.

Customer-facing

  • "Disconnect & revoke" copy + last-sign-in timestamp on the linked-accounts list (Luma + Hyva templates).
  • Provider-side token revoke wired through LinkingService::unlinkAccount — propagates the customer's "Disconnect" click out to the provider's own revoke endpoint.

Logging

  • LinkingService accepts an optional LoggerInterface so revoke-failures land in qoliber_social_login.log at warning rather than disappearing.

Test infrastructure

  • Generic Test/Unit/Etc/DbSchemaWhitelistConsistencyTest — every Qoliber module with a db_schema.xml must ship a matching whitelist covering every column / index / constraint. Caught the missing Passkey whitelist.
  • Generic Test/Unit/Etc/RegistrationPhpStyleTest — every registration.php must use use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(...) rather than inline FQN. 28 modules pinned.
  • Generic Test/Unit/Etc/ComposerConstraintsTest — no shipping module's composer.json may pin a dependency to "*". 27 modules pinned (Admin out of scope).
  • Test/Unit/Api/AuthenticationServiceInterfaceShapeTest — every public method on the concrete AuthenticationService must appear on the @api interface.
  • Test/Unit/Provider/ProviderContractTrait — shared contract checks (provider code, name, PKCE support, https authorize URL, state inclusion) usable by every provider's test class.
  • Test/Unit/Cron/PurgeExpiredTokensTest, Test/Unit/Console/Command/PurgeTokensCommandTest, Test/Unit/Model/Maintenance/ExpiredTokenPurgerTest, Test/Unit/Controller/Adminhtml/Provider/TestConnectionTest, Test/Unit/Model/Connection/ConnectionTesterTest — operational and admin code under test.

Security follow-ups (2026-05-06 / 2026-05-07)

  • Headless GraphQl state is now self-describing. SocialLoginGraphQl/Model/HeadlessStateSigner mints <base64url(JSON{p,v,e})>.<hex(hmac)> tokens keyed off the deploy key. The Start/CompleteCallback resolvers no longer require a Magento session cookie across the OAuth round trip — works for genuine PWA / Hyva-Checkout clients.
  • DNS-rebinding guard on JWKS validator. JwksUriValidator now resolves the hostname (injectable resolver) and rejects if any A/AAAA address lands in a private range. Fail-open on DNS failure.
  • OneTap CSRF reads via CookieManagerInterface — direct $_COOKIE superglobal access removed.
  • OneTap consumes Google's JWKS via WellKnownConfig, not a hardcoded constant. New Model\Discovery\GoogleJwksUriResolver wraps the discovery call.
  • GraphQl qoliberSocialLoginCompleteCallback mutation actually works. Replaced createCustomerAccessToken($email, '') (which always threw) with Magento\Integration\Api\UserTokenIssuerInterface::create() + CustomUserContext($customerId, USER_TYPE_CUSTOMER) — the modern, credential-less path.

Provider migrations (2026-05-07)

  • Google, Apple, Microsoft, LinkedIn provider classes accept an optional WellKnownConfig. When wired, getProviderConfig() prefers discovery values for authorization_endpoint / token_endpoint / userinfo_endpoint / jwks_uri. Hardcoded constants stay as fail-open fallbacks.
  • Provider-side revoke implemented for Google (oauth2.googleapis.com/revoke), GitHub (DELETE /applications/{client_id}/token), GitLab (/oauth/revoke, RFC 7009), Discord (/api/oauth2/token/revoke, RFC 7009 + Basic auth), Slack (/api/auth.revoke), Reddit (/api/v1/revoke_token, RFC 7009 + Basic). Microsoft + Bitbucket have no public revoke endpoint (annotated).

Admin Test Connection rolled out

  • All 18 providers' etc/adminhtml/system.xml carry the readonly redirect URI display + "Test Connection" button (Amazon, Apple, Bitbucket, Discord, Facebook, GitHub, GitLab, Google, Line, LinkedIn, Microsoft, PayPal, Pinterest, Reddit, Slack, TikTok, Twitch, Twitter).

Test count

  • Pre-hardening baseline: 237 tests / 525 assertions.
  • After Pass #1 + 6 deferred + to-10: 410 tests / 1002 assertions — all green at 70 ms.
  • PHPStan level 8 clean across the entire repo.

Amazon

Added

  • Initial release of Amazon OAuth 2.0 provider
  • Profile and postal code scopes support
  • Professional Amazon icon from coloured-icons repository

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Apple

Added

  • Initial release of Apple Sign In provider
  • JWT client secret generation from private key
  • Email and name scopes support
  • Cross-site cookie handling for Apple's auth flow
  • Professional Apple icon from coloured-icons repository
  • JWKS URI in provider configuration

Fixed

  • PHPStan level 8 (string casts on json_encode)
  • Rector PHP 8.4 typed constants

Security

  • Fixed config reading to use encryption-aware ConfigInterface (was using raw ScopeConfigInterface)

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

OIDC discovery

  • Optional WellKnownConfig constructor dep — when wired, the provider's authorize / token / userinfo / jwks_uri endpoints are read from the discovery document (24 h cached). Hardcoded constants stay as fail-open fallbacks.

Bitbucket

Added

  • Initial release of Bitbucket OAuth 2.0 provider
  • Account and email read scopes support
  • Professional Bitbucket icon from coloured-icons repository
  • ACL definition

Fixed

  • PHPStan level 8 (removed redundant null coalescing)
  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Discord

Added

  • Initial release of Discord OAuth 2.0 provider
  • Identify and email scopes support
  • Professional Discord icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Provider-side token revoke

  • Model\Provider\DiscordTokenRevoker calls the provider's revoke endpoint best-effort during disconnect.
  • Discord provider implements Qoliber\SocialLoginBase\Api\ProviderRevokeInterface; LinkingService::unlinkAccount propagates the customer's "Disconnect & revoke" click out to the provider.
  • 4–6 unit tests on the revoker (happy path, error statuses, exceptions, missing client creds).

Facebook

Added

  • Initial release of Facebook OAuth 2.0 provider.
  • Email and public profile permissions.
  • Professional Facebook icon from coloured-icons repository.
  • Data Deletion callback at /sociallogin/datadeletion/callback. Facebook requires every app with user data to host a POST endpoint that validates a signed_request, deletes the user's data, and returns {url, confirmation_code} JSON. The new Controller\Datadeletion\Callback delegates signature verification to a dedicated Model\SignedRequestValidator (HMAC-SHA256 against the app secret), deletes the matching row from qoliber_social_login_account by (provider_code=facebook, provider_user_id), and returns the required JSON. Idempotent — unknown user_id is a no-op.
  • Model\SignedRequestValidator with 7 unit tests covering signature tamper, wrong-secret, malformed input, missing secret, and missing user_id paths.

Fixed

  • Rector PHP 8.4 typed constants.

Security

  • Data Deletion callback is CSRF-exempt by design — the cryptographic signed_request signature replaces the usual form-key token.
  • All failure modes are logged via the dedicated qoliber_social_login.log channel; the Facebook user id is never logged in plaintext (8-char SHA256 fingerprint only).
  • Client-secret field carries validate="required-entry no-whitespace" in admin config.

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

GitHub

Added

  • Initial release of GitHub OAuth 2.0 provider
  • User email read scope support
  • Professional GitHub icon from coloured-icons repository
  • ACL definition

Fixed

  • PHPStan level 8 (array_values instead of is_array ternary)
  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Provider-side token revoke

  • Model\Provider\GitHubTokenRevoker calls the provider's revoke endpoint best-effort during disconnect.
  • GitHub provider implements Qoliber\SocialLoginBase\Api\ProviderRevokeInterface; LinkingService::unlinkAccount propagates the customer's "Disconnect & revoke" click out to the provider.
  • 4–6 unit tests on the revoker (happy path, error statuses, exceptions, missing client creds).

GitLab

Added

  • Initial release of GitLab OAuth 2.0 provider
  • Support for both GitLab.com and self-hosted instances
  • Configurable instance URL in admin
  • Read user scope support
  • Professional GitLab icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Provider-side token revoke

  • Model\Provider\GitLabTokenRevoker calls the provider's revoke endpoint best-effort during disconnect.
  • GitLab provider implements Qoliber\SocialLoginBase\Api\ProviderRevokeInterface; LinkingService::unlinkAccount propagates the customer's "Disconnect & revoke" click out to the provider.
  • 4–6 unit tests on the revoker (happy path, error statuses, exceptions, missing client creds).

Google

Added

  • Initial release of Google OAuth 2.0 provider.
  • Google One Tap integration: block + JS + Controller\Login\OneTap (CSRF double-submit, JWKS-verified ID token, nonce session lifecycle).
  • Controller\Login\GoogleOneTapCsrfValidator — testable CSRF check via CookieManagerInterface (no $_COOKIE superglobal).
  • Model\Discovery\GoogleJwksUriResolver — wraps WellKnownConfig for the Google issuer; fail-closed with a clear ProviderException if discovery returns no jwks_uri.
  • Model\Provider\GoogleTokenRevoker — oauth2.googleapis.com/revoke POST. Google implements ProviderRevokeInterface; "Disconnect & revoke" propagates out to Google.
  • Optional WellKnownConfig constructor dep on Model\Provider\Google — when wired, all four endpoints come from the discovery document (24 h cached). Hardcoded constants stay as fail-open fallbacks.
  • Admin "Test Connection" button + readonly redirect URI display in etc/adminhtml/system.xml.
  • Email, profile, OpenID scopes; PKCE support.
  • Professional Google icon (coloured-icons repository).
  • ACL definition + CSP whitelist.

Tests

  • Test/Unit/Model/Provider/GoogleTest — uses the shared ProviderContractTrait (provider code, name, PKCE, https authorize URL, state inclusion).
  • Test/Unit/Model/Provider/GoogleRevokeTest — happy path / non-200 / curl exception / empty token (4 cases).
  • Test/Unit/Model/Discovery/GoogleJwksUriResolverTest — discovery hit / null-throws / issuer constant pin (3 cases).
  • Test/Unit/Controller/Login/GoogleOneTapCsrfValidatorTest — match / missing-form / missing-cookie / mismatch / both-empty (5 cases).

Hyvä theme support

Added

  • Initial release of Hyva theme compatibility module.
  • Alpine.js-based social login buttons.
  • Tailwind CSS styling with provider-specific hover colors.
  • CSS references to provider icons (no duplication with Luma).
  • Responsive design with compact mode support.
  • Loading states driven by Alpine.js (spinner + icon pulse while the OAuth round-trip is in flight).
  • Checkout page layout (checkout_index_index.xml).
  • Cancel button on confirm template.
  • Extensible child block container for merchant GDPR consent.

Changed

  • Frontend button template gained aria-label + alt="" + aria-hidden="true" on icon images (WCAG 2.1 AA).
  • Removed mock-provider CSS and icon references from shipped assets.
  • Removed hardcoded terms checkbox (replaced with extensible child block).

Security

  • hyva_customer_account_index.xml marks the LinkedAccounts block with cacheable="false". Per-customer provider connections are guaranteed not to be served from FPC.

LINE

Added

  • Initial release of LINE OpenID Connect provider
  • Email and profile scopes support
  • Professional LINE icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

LinkedIn

Added

  • Initial release of LinkedIn OpenID Connect provider
  • Email and profile scopes support
  • Professional LinkedIn icon from coloured-icons repository

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

OIDC discovery

  • Optional WellKnownConfig constructor dep — when wired, the provider's authorize / token / userinfo / jwks_uri endpoints are read from the discovery document (24 h cached). Hardcoded constants stay as fail-open fallbacks.

Microsoft

Added

  • Initial release of Microsoft OpenID Connect provider
  • Azure AD integration support
  • Email and profile scopes support
  • Professional Microsoft icon from coloured-icons repository
  • JWKS URI in provider configuration

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

OIDC discovery

  • Optional WellKnownConfig constructor dep — when wired, the provider's authorize / token / userinfo / jwks_uri endpoints are read from the discovery document (24 h cached). Hardcoded constants stay as fail-open fallbacks.

OAuth 2.0 strategy

Added

  • Initial release of OAuth 2.0 authentication strategy.
  • Authorization code flow implementation with PKCE support.
  • Token exchange + refresh functionality.
  • Abstract OAuth 2.0 provider base class.
  • Model\Client\SecureCurlOptions — central HTTP hardening policy for outbound OAuth/OIDC requests: CURLOPT_SSL_VERIFYPEER=true, CURLOPT_SSL_VERIFYHOST=2, CURLOPT_PROTOCOLS=CURLPROTO_HTTPS, CURLOPT_REDIR_PROTOCOLS=CURLPROTO_HTTPS, CURLOPT_FOLLOWLOCATION=false, CURLOPT_CONNECTTIMEOUT=5, CURLOPT_TIMEOUT=30.

Changed

  • OAuth2Strategy exposes protected buildAuthorizationParams() as the extension point for subclasses. OAuth2PkceStrategy overrides it to layer PKCE challenge fields instead of duplicating URL assembly — cleaner and closes a latent bug (see Fixed).
  • GenericProfileMapper defaults emailVerified to false (was true). Providers must now opt-in to verified-email status per claim.

Fixed

  • OAuth2PkceStrategy::buildAuthorizationUrl respects client_id_param. Previously the PKCE path hardcoded client_id=… regardless of provider config, so TikTok (which requires client_key=… at the authorization endpoint) would silently send the wrong parameter name and be rejected. The PKCE strategy now inherits client_id_param handling from OAuth2Strategy via the shared buildAuthorizationParams template method.
  • PHPStan level 8 type safety (curl_exec, Json::unserialize assertions).
  • PSR-12 compliance.
  • Rector PHP 8.4 typed constants.

Security

  • Explicit TLS hardening on all three HTTP paths (HttpClient::postTokenRequest native curl, getUserInfo, postJson via Magento Curl wrapper). Previously cURL defaults applied implicitly — explicit is safer against build-level flips.
  • No silent HTTP redirect following on OAuth endpoints — a 3xx from a provider must surface as an error, not a cross-origin follow.
  • Removed sensitive data from logs (tokens, secret prefixes, response bodies).
  • PKCE silent-downgrade prevention (throws ProviderException if code_verifier is missing when the strategy requires PKCE).

OpenID Connect strategy

Added

  • Initial release of OpenID Connect authentication strategy.
  • ID token validation and verification (signature via JWKS, claims, nonce, iss/aud/exp).
  • Claims extraction and profile mapping.
  • Abstract OpenID Connect provider base class.
  • OIDC discovery support via Model\Discovery\WellKnownConfig with a two-layer cache (process L1 + Magento shared L2, 24h TTL under tag qoliber_social_login_discovery) and flushIssuer($issuer) for targeted invalidation. Issuer URLs pass the JwksUriValidator SSRF guard before any fetch; the fetch itself uses SecureCurlOptions hardening.
  • Model\Jwt\JwksUriValidator — textual SSRF guard rejecting loopback, link-local, RFC1918, IPv6 ULA, the any-address, and non-https schemes before any socket opens.

Fixed

  • PHPStan level 8 type safety.
  • Rector PHP 8.4 typed constants.

Security

  • JWKS-based JWT signature verification (RS256 pinned — no alg: none or HS256 confusion).
  • OIDC nonce validation. Stored in session at /authorize, validated at /callback against the decoded ID token's nonce claim using hash_equals. When the ID token carries a nonce claim but the session has no expected value, the strategy rejects — a captured token cannot be replayed after session eviction.
  • No unvalidated ID-token fallback. When the initial ID-token path cannot yield a profile and no UserInfo endpoint is configured, the strategy throws instead of re-decoding the token without signature/nonce verification.
  • SSRF-hardened JWKS fetch. The jwks_uri must be HTTPS and must not resolve (textually) to loopback (127.0.0.0/8, ::1, localhost), link-local (169.254.0.0/16, fe80::/10), RFC1918 (10/8, 172.16/12, 192.168/16), IPv6 ULA (fc00::/7), or the any-address. Closes a direction where a malicious or misconfigured discovery document could point the app at internal metadata endpoints.
  • HTTP hardening on JWKS and discovery fetches. Strict TLS verification, HTTPS-only protocols (initial + redirect), no redirect following, 5s connect timeout, 30s overall timeout — via the project-wide SecureCurlOptions.

PayPal

Added

  • Initial release of PayPal OpenID Connect provider
  • Email and profile scopes support
  • Sandbox and production environment support
  • Professional PayPal icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Pinterest

Added

  • Initial release of Pinterest OAuth 2.0 provider
  • User accounts read scope support
  • Professional Pinterest icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Reddit

Added

  • Initial release of Reddit OAuth 2.0 provider
  • Identity scope support
  • Professional Reddit icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Provider-side token revoke

  • Model\Provider\RedditTokenRevoker calls the provider's revoke endpoint best-effort during disconnect.
  • Reddit provider implements Qoliber\SocialLoginBase\Api\ProviderRevokeInterface; LinkingService::unlinkAccount propagates the customer's "Disconnect & revoke" click out to the provider.
  • 4–6 unit tests on the revoker (happy path, error statuses, exceptions, missing client creds).

Slack

Added

  • Initial release of Slack OpenID Connect provider
  • Email and profile scopes support
  • Professional Slack icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Provider-side token revoke

  • Model\Provider\SlackTokenRevoker calls the provider's revoke endpoint best-effort during disconnect.
  • Slack provider implements Qoliber\SocialLoginBase\Api\ProviderRevokeInterface; LinkingService::unlinkAccount propagates the customer's "Disconnect & revoke" click out to the provider.
  • 4–6 unit tests on the revoker (happy path, error statuses, exceptions, missing client creds).

TikTok

Added

  • Initial release of TikTok OAuth 2.0 provider
  • User info basic scope support
  • Professional TikTok icon from coloured-icons repository

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Twitch

Added

  • Initial release of Twitch OAuth 2.0 provider
  • User read email scope support
  • Professional Twitch icon from coloured-icons repository
  • CSP whitelist
  • ACL definition

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

X (Twitter)

Added

  • Initial release of Twitter/X OAuth 2.0 provider
  • Tweet read and user read scopes support
  • Professional X/Twitter icon from coloured-icons repository

Fixed

  • Rector PHP 8.4 typed constants

Admin Test Connection

  • etc/adminhtml/system.xml carries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint at qoliber_sociallogin/provider/testConnection and reports ✓ / ✗ inline.

Mock provider (testing)

Added

  • Initial release of Mock provider for testing
  • Simulated OAuth flow with predefined responses
  • Multiple test scenarios (new user, existing user, errors)
  • Professional mock icon with gradient design

Fixed

  • Rector PHP 8.4 typed constants
  • PHPStan level 8 (string casts where applicable)

Mock OAuth 2.0 server (testing)

Added

  • Initial release of Mock OAuth 2.0 provider (for local dev, staging, and integration tests).
  • Simulated OAuth 2.0 flow with PKCE.
  • Test scenarios for OAuth 2.0 specific flows.
  • Professional mock icon with gradient design.

Fixed

  • Rector PHP 8.4 typed constants.
  • PHPStan level 8 (string casts where applicable).

Security

  • Inert in production by default. MockOAuth2::isEnabled() short-circuits to false when Magento is in MAGE_MODE=production unless the operator explicitly sets qoliber_social_login_mock_enabled: true in app/etc/env.php. Stops the module from ever being accidentally wirable in a live deployment, even if its admin toggle is switched on.
  • The production check is shared with Qoliber_SocialLoginMockOpenID via Qoliber\SocialLoginBase\Model\MockProviderGate.

Mock OpenID Connect server (testing)

Added

  • Initial release of Mock OpenID Connect provider (for local dev, staging, and integration tests).
  • Simulated OIDC flow with ID tokens.
  • Test scenarios for OpenID Connect specific flows.
  • Professional mock icon with gradient design.

Fixed

  • Rector PHP 8.4 typed constants.
  • PHPStan level 8 (string casts where applicable).

Security

  • Inert in production by default. MockOpenID::isEnabled() short-circuits to false when Magento is in MAGE_MODE=production unless the operator explicitly sets qoliber_social_login_mock_enabled: true in app/etc/env.php.
  • The production check is shared with Qoliber_SocialLoginMockOAuth2 via Qoliber\SocialLoginBase\Model\MockProviderGate.
Changelog — Social Login — Marketing — Extensions | qoliber Docs