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 incontent, 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 therefererparameter 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-newsletteris 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/DefaultHandleCacheabilityTestand 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.shruns 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.
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.
Fixed
- Full page caching was disabled on every storefront page. The One Tap block was marked
cacheable="false"indefault.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-storeendpoint,sociallogin/onetap/config, fetched byview/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.phtmlemits 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 oldcheckout_index_index.xmlalso 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-smis a larger shadow on Tailwind 4 andfocus:outline-nonedrops 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
HyvaTailwindCompatTestalso fails on utilities rescaled between Tailwind 3 and 4 (shadow-sm,rounded-sm,blur-sm,drop-shadow-sm,outline-none, barering) 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, thennpm run buildin the theme'sweb/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.
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 sendsclient_key), and a TikTok response whoseerror.codeis"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 andlocalhostare 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.
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_accountid; 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.
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.
/authorizedropped the client's nonce and/tokenlooked 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.
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.
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.cssand loaded it with<css src>fromdefault.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 compiledstyles.css, and duplicated colour tokens thattailwind.config.jsalready declared. The two had already drifted: the config declaredmockwith no rule, whiletwitch,pinterest,slackandgitlabhad 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) andcomponents/social-login.cssholding 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.cssandview/frontend/layout/default.xml. Integrators must rebuild the theme's Tailwind bundle (npm run buildin the theme'sweb/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: amodule.csswithout its own@source(which suppresses the automatic one and purges every template class on Tailwind 4), a missingtailwind.config.js(which purges them on Tailwind 3), colour tokens declared for only one version, tokens and rules drifting apart, and the version-specific utilitiesbg-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.
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.
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.
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 aviewBoxonly 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 everyqoliber-*__iconclass 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.
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.
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.
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.
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.
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_accountstores one link per provider identity with no website dimension, and the link was followed before any website check. Magento shipscustomer/account_share/scopeas1(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, soRequire Emails Confirmationwas 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.LoginServicenow consultsAccountConfirmationand 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_dataas unencrypted JSON — phone numbers, locale metadata, friend counts, whatever the provider chose to include. TheSave Profile Photo/Collect Gender/Collect Birthdaysettings only ever gated mapping onto customer attributes, so a merchant who switched them off still had that data retained indefinitely.Model\Auth\ProfileDataSanitizernow 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:purgeand 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 clearsaccess_token,refresh_tokenandtoken_expires_atand 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 fromRandom::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 readsminimum_password_lengthexactly asAccountManagementdoes so the two cannot disagree. - Login events fired twice.
LoginServiceregenerated the session id and dispatchedcustomer_login/customer_data_object_loginitself, then calledsetCustomerDataAsLoggedIn(), 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 onUnique constraint violation found, leaving an orphanedcustomer_entityrow. The table gains awebsite_idcolumn, 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.LinkingServicestill 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 indb_schema_whitelist.jsonsosetup:upgradeactually 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.
RedirectUriDisplayandTestConnectionButtonextendedData\Form\Element\AbstractElement, but asystem.xml<frontend_model>is attached withsetRenderer()and instantiated through the layout, which requires aBlockInterface. Opening Stores > Configuration > Social Login threwLogicException: ... does not implement BlockInterface, so no provider could be configured at all. Both now extendMagento\Config\Block\System\Config\Form\Field, as Magento's own field renderers do, and read their provider code from the element'sfield_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
onclickwas emitted into a double-quoted attribute whilejson_encodekeeps 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_APOSalready 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 defaultadmin/security/use_form_keythat yields an admin route carryingkey/<admin secret>, and it omitted theproviderpath segment thatAbstractProvider::getRedirectUri()actually sends. A merchant following the instruction registered a URL the module never uses (guaranteeingredirect_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.xmlreferencedurn:magento:module:Magento_Backend:etc/routes.xsd, which does not exist in Magento 2.4.7; it now usesurn:magento:framework:App/etc/routes.xsdas 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 realAuthenticationServicewith 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 ofcustomer_login/customer_data_object_loginwith 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— fillswebsite_idon links created before 1.1.1 fromcustomer_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 realAccountManagementunder 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), andConfirmPostTest(a customer-typed email is never treated as provider-verified).Callbackpreviously 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 encryptedclient_secretis 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 thesetup:upgradefailure 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 eachsystem.xmlthrough Magento's ownStructure\Converterand 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 livesetup: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 extendMagento\Config\Block\System\Config\Form\Field, andetc/adminhtml/system.xmlusesMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Amazon
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Apple
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Bitbucket
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Discord
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
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:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
GitHub
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
GitLab
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
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:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships 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:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships 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 outsideMULTI_TENANT_ALIASESwould be validated against a literal issuer that can never match, reintroducing the 1.1.1 defect for one option only.
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships 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:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Slack
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
TikTok
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
Twitch
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships with every Magento installation, so nothing changes at runtime; the manifest now states a dependency that was always real.
X (Twitter)
Fixed
setup:upgradeno longer aborts.etc/adminhtml/system.xmlused 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 samegetData('provider_code')the Test Connection button already reads — no behaviour change.
Changed
- Declares
magento/module-config.etc/adminhtml/system.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships 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.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships 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.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships 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.xmlusesMagento\Config\Model\Config\Backend\Encrypted— the backend model that encrypts the client secret at rest — andMagento\Config\Model\Config\Source\Yesno, but the package never required the module that provides them. Composer-level only —Magento_Configships 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.0range 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 aresponse_mode=form_postprovider 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.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow 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_postcallback 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.
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.0range 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 makesgetCompletedAt()null-safe: it declared: stringbut returnedformatDate(), 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.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.
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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.
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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.
OAuth 2.0 strategy
Fixed
userinfo_headersfrom a provider config is now honoured on the profile request. The strategy only ever readheaders, 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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,issandsubwere validated only when present, so a token withoutexpnever expired and a token withoutaudwas 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.
issmay now be expressed with a{tid}placeholder, resolved from the token's owntidclaim — 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.
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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow declares every Magento package the code actually uses, with pinned minimums, and carries the canonical author block.
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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Added
- Provider contract test coverage.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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.0range is now verified by parsing and running the unit suite on every one of those interpreters.
Changed
composer.jsonnow 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. Encryptsaccess_token/refresh_tokenat rest viaMagento\Framework\Encryption\EncryptorInterfaceso 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 thecustomer_data_object_loginevent.Model\Auth\CustomerCreator— owns customer-from-profile creation, including theMailExceptionrepository-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'sresponse_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 inMAGE_MODE=productionunless the operator explicitly setsqoliber_social_login_mock_enabled: trueinapp/etc/env.php.Model\Security\CallbackRateLimiter— cache-backed per-(IP+session) counter that caps OAuth callback attempts toN/minute. Config:qoliber_social_login/security/callback_rate_limit(default 20;0disables). Over-limit returns a generic flash message and logs atwarning.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 inController\Login\Callback.Controller\Login\PopupResultEmitter— renders origin-scopedpostMessageHTML for the optional popup OAuth flow. User-controlled fields are XSS-hardened viaJSON_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 (fromauthenticate()) and the Google One Tap ID-token flow.- Parent-page JS
view/frontend/web/js/popup-login.jsfor 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\AuthenticationServiceis a thin orchestrator — 14 → 10 dependencies, 378 → ~230 LOC. The publicAuthenticationServiceInterfacesurface is unchanged; third-party callers are unaffected.Controller\Login\Callbackslimmed from ~250 LOC to ~175 LOC; Apple-relay and error-handling branches delegate to dedicated collaborators.Controller\Account\ConfirmPostno longer depends onSessionManagerInterfacedirectly — it reads the decrypted pending-profile payload throughPendingProfileSessionStorage.Model\Pkceno longer depends onMagento\Framework\Math\Random(removed constructor argument). State nonce is nowbin2hex(random_bytes(16)).Model\ProfileMappercollapses the duplicatedextractFirstName/extractLastNamelogic into a singleextractNamePart()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) gainedaria-labeland moved icon images toalt=""+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--loadingclass, 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.
PendingProfileSessionStoragetransparently encrypts/decrypts secret fields. - CSPRNG state nonce. Moved from
Magento\Framework\Math\Random::getRandomString()(no CSPRNG contract) torandom_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_verifiermissing 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.phpflag. - 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\AuthenticationServiceInterfaceextended withcompleteAuthenticationWithProfile()andcompleteAccountCreation()so One Tap and the confirm-and-create controller can depend on the@apiinterface 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::unlinkAccountcalls it best-effort during disconnect.
Operability
Cron\PurgeExpiredTokensdaily at 03:00 — sweeps rows wheretoken_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\ExpiredTokenPurgershared by both. Filter shape:lton the timestamp +null: trueon refresh token.
Schema
last_authenticated_atcolumn onqoliber_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.xmldeclaring theqoliber_socialloginadmin 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
LinkingServiceaccepts an optionalLoggerInterfaceso revoke-failures land inqoliber_social_login.logatwarningrather than disappearing.
Test infrastructure
- Generic
Test/Unit/Etc/DbSchemaWhitelistConsistencyTest— every Qoliber module with adb_schema.xmlmust ship a matching whitelist covering every column / index / constraint. Caught the missing Passkey whitelist. - Generic
Test/Unit/Etc/RegistrationPhpStyleTest— everyregistration.phpmust useuse Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(...)rather than inline FQN. 28 modules pinned. - Generic
Test/Unit/Etc/ComposerConstraintsTest— no shipping module'scomposer.jsonmay pin a dependency to"*". 27 modules pinned (Admin out of scope). Test/Unit/Api/AuthenticationServiceInterfaceShapeTest— every public method on the concreteAuthenticationServicemust appear on the@apiinterface.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/HeadlessStateSignermints<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.
JwksUriValidatornow 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$_COOKIEsuperglobal access removed. - OneTap consumes Google's JWKS via
WellKnownConfig, not a hardcoded constant. NewModel\Discovery\GoogleJwksUriResolverwraps the discovery call. - GraphQl
qoliberSocialLoginCompleteCallbackmutation actually works. ReplacedcreateCustomerAccessToken($email, '')(which always threw) withMagento\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 forauthorization_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.xmlcarry 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
OIDC discovery
- Optional
WellKnownConfigconstructor 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
Provider-side token revoke
Model\Provider\DiscordTokenRevokercalls the provider's revoke endpoint best-effort during disconnect.Discordprovider implementsQoliber\SocialLoginBase\Api\ProviderRevokeInterface;LinkingService::unlinkAccountpropagates 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).
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 asigned_request, deletes the user's data, and returns{url, confirmation_code}JSON. The newController\Datadeletion\Callbackdelegates signature verification to a dedicatedModel\SignedRequestValidator(HMAC-SHA256 against the app secret), deletes the matching row fromqoliber_social_login_accountby(provider_code=facebook, provider_user_id), and returns the required JSON. Idempotent — unknown user_id is a no-op. Model\SignedRequestValidatorwith 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_requestsignature replaces the usual form-key token. - All failure modes are logged via the dedicated
qoliber_social_login.logchannel; 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
Provider-side token revoke
Model\Provider\GitHubTokenRevokercalls the provider's revoke endpoint best-effort during disconnect.GitHubprovider implementsQoliber\SocialLoginBase\Api\ProviderRevokeInterface;LinkingService::unlinkAccountpropagates 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
Provider-side token revoke
Model\Provider\GitLabTokenRevokercalls the provider's revoke endpoint best-effort during disconnect.GitLabprovider implementsQoliber\SocialLoginBase\Api\ProviderRevokeInterface;LinkingService::unlinkAccountpropagates 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).
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 viaCookieManagerInterface(no$_COOKIEsuperglobal).Model\Discovery\GoogleJwksUriResolver— wrapsWellKnownConfigfor the Google issuer; fail-closed with a clearProviderExceptionif discovery returns nojwks_uri.Model\Provider\GoogleTokenRevoker—oauth2.googleapis.com/revokePOST.GoogleimplementsProviderRevokeInterface; "Disconnect & revoke" propagates out to Google.- Optional
WellKnownConfigconstructor dep onModel\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 sharedProviderContractTrait(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.xmlmarks the LinkedAccounts block withcacheable="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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
OIDC discovery
- Optional
WellKnownConfigconstructor 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
OIDC discovery
- Optional
WellKnownConfigconstructor 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
OAuth2Strategyexposesprotected buildAuthorizationParams()as the extension point for subclasses.OAuth2PkceStrategyoverrides it to layer PKCE challenge fields instead of duplicating URL assembly — cleaner and closes a latent bug (see Fixed).GenericProfileMapperdefaultsemailVerifiedtofalse(wastrue). Providers must now opt-in to verified-email status per claim.
Fixed
OAuth2PkceStrategy::buildAuthorizationUrlrespectsclient_id_param. Previously the PKCE path hardcodedclient_id=…regardless of provider config, so TikTok (which requiresclient_key=…at the authorization endpoint) would silently send the wrong parameter name and be rejected. The PKCE strategy now inheritsclient_id_paramhandling fromOAuth2Strategyvia the sharedbuildAuthorizationParamstemplate method.- PHPStan level 8 type safety (
curl_exec,Json::unserializeassertions). - PSR-12 compliance.
- Rector PHP 8.4 typed constants.
Security
- Explicit TLS hardening on all three HTTP paths (
HttpClient::postTokenRequestnative curl,getUserInfo,postJsonvia 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
ProviderExceptionifcode_verifieris 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\WellKnownConfigwith a two-layer cache (process L1 + Magento shared L2, 24h TTL under tagqoliber_social_login_discovery) andflushIssuer($issuer)for targeted invalidation. Issuer URLs pass theJwksUriValidatorSSRF guard before any fetch; the fetch itself usesSecureCurlOptionshardening. 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: noneor HS256 confusion). - OIDC nonce validation. Stored in session at
/authorize, validated at/callbackagainst the decoded ID token'snonceclaim usinghash_equals. When the ID token carries anonceclaim 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_urimust 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
Provider-side token revoke
Model\Provider\RedditTokenRevokercalls the provider's revoke endpoint best-effort during disconnect.Redditprovider implementsQoliber\SocialLoginBase\Api\ProviderRevokeInterface;LinkingService::unlinkAccountpropagates 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand reports✓/✗inline.
Provider-side token revoke
Model\Provider\SlackTokenRevokercalls the provider's revoke endpoint best-effort during disconnect.Slackprovider implementsQoliber\SocialLoginBase\Api\ProviderRevokeInterface;LinkingService::unlinkAccountpropagates 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand 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.xmlcarries the readonly redirect URI display + "Test Connection" button. The button hits the shared admin endpoint atqoliber_sociallogin/provider/testConnectionand 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 tofalsewhen Magento is inMAGE_MODE=productionunless the operator explicitly setsqoliber_social_login_mock_enabled: trueinapp/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_SocialLoginMockOpenIDviaQoliber\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 tofalsewhen Magento is inMAGE_MODE=productionunless the operator explicitly setsqoliber_social_login_mock_enabled: trueinapp/etc/env.php. - The production check is shared with
Qoliber_SocialLoginMockOAuth2viaQoliber\SocialLoginBase\Model\MockProviderGate.