Skip to content

Socialite: harden provider lifecycles and first-party extensibility - #485

Merged
binaryfire merged 15 commits into
0.4from
audit/socialite-correctness-extensibility-lifecycle
Aug 7, 2026
Merged

Socialite: harden provider lifecycles and first-party extensibility#485
binaryfire merged 15 commits into
0.4from
audit/socialite-correctness-extensibility-lifecycle

Conversation

@binaryfire

@binaryfire binaryfire commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR completes Socialite's OAuth 2 and OpenID Connect correctness work while preserving Hypervel's cached-provider design.

The main changes are:

  • isolate mutable provider state per coroutine while retaining one cached provider per driver and worker;
  • give Factory and concrete manager resolution one service identity without changing Factory-only fakes;
  • move Bitbucket, GitLab, and generic OIDC credentials to Bearer authorization;
  • centralize token-response parsing, preserve refresh tokens, expose the complete response on returned users, and publish cached users only after construction succeeds;
  • share one bounded, exact-URL JWKS cache across generic OIDC, Google, and Facebook;
  • validate issuer, audience, nonce, discovery, and key-rotation behavior consistently;
  • make secret-bearing call frames redact their arguments;
  • document custom providers and dynamic tenant configuration as first-party Socialite workflows.

The PR also fixes three service-identity issues found while tracing Socialite's container behavior: Support managers now refresh their configuration repository when swapping containers, Object Pool uses one manager and recycler owner, and Reverb uses one array channel repository.

Motivation

Hypervel keeps Socialite providers alive for the worker lifetime. That is useful for stable metadata and HTTP/JWKS reuse, but mutable request state cannot live on those shared objects. The old provider context used recyclable object IDs, retained request ownership indirectly, and allowed separately resolved manager identities to disagree about drivers and configuration.

The provider implementations also had several protocol-level gaps. Some credentials were sent in query strings, generic discovery and key caches could be reused across changing tenant URLs, nonce validation did not honor providers that disable nonce protection, and an exception after partial user construction could leave a cached user behind.

These issues are fixed at their owning boundaries without cloning providers, adding locks, retaining per-tenant maps, or introducing a second provider registry.

Provider lifecycle

Provider instances now receive a monotonic process-lifetime context namespace. Request, credentials, scopes, HTTP client, state, and user memoization remain coroutine-local. Cached driver resolution refreshes the current request, while direct cross-coroutine reuse without a request fails clearly instead of reading stale state.

SocialiteManager is the canonical worker-lifetime service and the Factory contract aliases to it. Application bindings and Factory-only fake swaps still take precedence.

OAuth responses and extension points

The base OAuth 2 provider now owns protected parsers for access tokens, refresh tokens, expiry, approved scopes, and whole-response user lookup. Login and refresh use the same parsing rules. Missing refresh-token rotation preserves the submitted token, valid zero-padded expiry values remain valid, and malformed advisory expiry values become null.

Returned users expose the complete token response. Providers retain no response property or context slot. User memoization happens only after mapping and every setter succeeds.

These hooks, along with request access and generic OpenIdProvider, provide the first-party surface needed by external provider packages and tenant-driven applications without the separate SocialiteProviders manager layer.

OIDC and JWKS

Generic OIDC, Google, and Facebook now use one shared JWKS concern. It retains one parsed key set and one refresh-attempt timestamp per cached provider, keys entries by exact URL, honors useful Cache-Control directives, and retries decoding once after supported rotation failures. A short cooldown prevents repeated refresh traffic while matching cached keys remain available.

Discovery publishes URL and metadata atomically. Audience validation accepts scalar and list claims, requires the configured client ID, and rejects untrusted extra audiences. Google issuer checks retain only the documented issuer forms. Nonces are consumed once and required only when the provider enables nonce protection.

The shared Firebase JWT path removes Socialite's manual RSA construction and the direct phpseclib dependency.

Compatibility and performance

Supported Laravel OAuth 2 APIs, named arguments, protected extension points, and provider ergonomics remain intact. Hypervel continues to omit OAuth 1 and legacy Twitter support and retains the x driver. Additions such as complete token responses, parser hooks, trusted audiences, and User::fake() are additive.

Ordinary non-Socialite requests are unchanged. Provider construction adds one local integer increment. Callback paths add bounded local type and array checks beside existing network and JWT work. JWKS reuse removes repeated network requests. There is no new lock, timer, background job, unbounded tenant cache, provider clone, serialization layer, or ordinary network round trip.

Verification

The changes were verified with focused Socialite, Support, Object Pool, and Reverb coverage, root and split Composer validation, facade and documentation checks, stale-symbol scans, formatting, both PHPStan configurations, the complete parallel components suite, Testbench package mode, dogfood, and diff checks.

For more details, see: docs/plans/2026-08-07-1416-socialite-correctness-first-party-extensibility-and-lifecycle.md.

Summary by CodeRabbit

  • New Features
    • Expanded OAuth 2.0 and OpenID Connect support with custom providers, trusted audiences, nonce validation, JWKS caching, and complete token-response access.
    • Added provider fakes with sensible defaults and customizable user attributes for testing.
  • Bug Fixes
    • Improved token security through authorization headers and issuer, audience, and signing-key validation.
    • Fixed provider state isolation, token refresh handling, and avatar fallback behavior.
  • Documentation
    • Added comprehensive guidance for custom providers, configuration, testing, and security practices.

Keep the manager configuration repository aligned with its active container when tests or rebinding replace the application instance.

This removes the need for package-specific rebinding workarounds and adds coverage proving cached managers resolve configuration from the replacement container.
Alias the concrete pool manager and recycler to their canonical contracts so concrete and contract consumers use the same worker-lifetime registries.

Preserve application overrides while adding focused provider coverage for shared pool and recycler state.
Alias the array channel manager to the channel-manager contract when the application has not supplied its own binding.

This keeps concrete and contract resolutions on one worker-local repository while preserving both early and late application overrides.
Give each cached provider a monotonic process-lifetime context namespace, move request ownership into coroutine context, and refresh the active request whenever a cached driver is resolved.

Unify concrete manager and Factory resolution without breaking Factory-only fakes, remove duplicate rebinding behavior, protect raw provider-context internals, regenerate facade metadata, and make manager and fake return contracts truthful. The regressions cover tenant isolation, request reuse failures, enum drivers, config key edge cases, and manager rebinding.
Parse token responses through protected access-token, refresh-token, expiry, scope, and whole-response seams shared by login and refresh flows.

Preserve unrotated refresh tokens, accept bounded protocol digit strings, publish complete token responses on returned users, and cache authenticated users only after mapping and decoration succeed. Restore the current OAuth 2 user fake and cover parser overrides, transactional memoization, direct token lookup, response publication, and fake-manager separation.
Use one exact-URL, bounded JWKS implementation for generic OIDC, Google, and Facebook, with cache-control expiry, atomic publication, and one throttled rotation retry.

Correct issuer, audience, nonce, discovery, and failure-class behavior; preserve complete token-response mapping without worker-retained response state; and remove the obsolete phpseclib dependency. The coverage exercises tenant URL switching, malformed metadata, cache directives, failed refresh cooldowns, key rotation, disabled nonce flows, scalar and list audiences, and provider-specific validation.
Send Bitbucket, GitLab, and generic provider credentials through their correct Bearer transport, update GitLab user lookup to the current endpoint, and make LinkedIn image mapping tolerate missing optional nodes.

Mark secret-bearing provider frames with SensitiveParameter while keeping non-secret diagnostics visible. Add exact request-shape, optional-image, and derived provider-surface reflection coverage so new first-party providers inherit the same security contract.
Document custom OAuth 2 and OpenID Connect providers, protected response parsers, full token responses, request access, trusted audiences, testing fakes, and provider registration in Laravel-style prose.

Clarify boot-time versus request-local configuration, require tenant configuration on both redirect and callback requests, explain session requirements for PKCE and nonce validation, and record the intentional OAuth 1 and legacy Twitter differences without exposing internal lifecycle machinery.
Mark Socialite complete in the package checklist and routing index, record all accepted and rejected findings, and capture the final lifecycle, security, performance, compatibility, and validation outcomes.

Record Support, Object Pool, and Reverb corrections at their owning package entries, add their dependency-index routes, and retain the completed Sanctum records merged from the latest 0.4 branch.
Document the final Socialite architecture, accepted findings, rejected complexity, implementation boundaries, regression strategy, and performance and compatibility gates.

The plan captures the current Laravel and SocialiteProviders research, OAuth and OpenID Connect protocol decisions, coroutine and worker ownership model, shared JWKS design, cross-package corrections, and completion review used for this work.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@binaryfire, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 53376cd4-38df-4540-bd6c-242cd758819a

📥 Commits

Reviewing files that changed from the base of the PR and between b79d85f and 04a003e.

📒 Files selected for processing (1)
  • docs/plans/2026-08-07-1416-socialite-correctness-first-party-extensibility-and-lifecycle.md
📝 Walkthrough

Walkthrough

Socialite now isolates provider state per coroutine, exposes typed OAuth contracts, normalizes token responses, validates OIDC tokens, caches JWKS data, uses Bearer token transport, and supports custom providers and fake users. Related lifecycle bindings, dependencies, documentation, and tests were updated.

Changes

Socialite correctness and lifecycle

Layer / File(s) Summary
Audit routing and container ownership
docs/plans/..., src/support/..., src/object-pool/..., src/reverb/..., tests/...
Audit records and related container or service-provider bindings now reflect shared ownership and preserved application overrides.
Provider context and typed manager contracts
src/socialite/src/AbstractProvider.php, src/socialite/src/Concerns/*, src/socialite/src/Socialite*.php
Provider requests and context use coroutine-scoped storage. Manager, facade, factory, service provider, and fake APIs use typed provider contracts.
OAuth token handling and provider behavior
src/socialite/src/Two/AbstractProvider.php, src/socialite/src/Two/User.php, src/socialite/src/Two/*Provider.php
Token responses use shared parsers, preserve refresh tokens, expose the complete response body, validate audiences, and mark secrets as sensitive. Provider requests use Bearer authorization where required.
OIDC validation and shared JWKS caching
src/socialite/src/Two/Concerns/InteractsWithJwks.php, src/socialite/src/Two/FacebookProvider.php, src/socialite/src/Two/GoogleProvider.php, src/socialite/src/Two/OpenIdProvider.php
OIDC providers use shared JWKS decoding, issuer and audience validation, nonce consumption, discovery caching, refresh cooldowns, and cache-control expiration handling.
Documentation, metadata, and regression coverage
src/boost/docs/socialite.md, src/socialite/README.md, composer.json, src/socialite/composer.json, tests/Socialite/*
Documentation covers custom providers, token responses, and fakes. Composer metadata removes phpseclib. Tests cover transport, parsing, validation, caching, isolation, bindings, and sensitive parameters.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SocialiteManager
  participant OAuthProvider
  participant JWKS
  Client->>SocialiteManager: request provider
  SocialiteManager->>OAuthProvider: create or reuse provider
  Client->>OAuthProvider: exchange code and retrieve user
  OAuthProvider->>JWKS: fetch or refresh signing keys
  JWKS-->>OAuthProvider: return JWKS response
  OAuthProvider-->>Client: return validated User
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main Socialite lifecycle and extensibility changes in the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/socialite-correctness-extensibility-lifecycle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown

Greptile Summary

The PR hardens Socialite’s coroutine-local provider state, OAuth/OIDC validation, token handling, and bounded JWKS reuse while unifying several worker-lifetime container identities.

  • Adds shared exact-URL JWKS caching with bounded fallback expiry and rotation retry behavior.
  • Centralizes OAuth response parsing and exposes complete token responses without retaining request-specific response state on cached providers.
  • Aligns Socialite, Object Pool, Reverb, and Support manager ownership with Hypervel’s long-lived worker model.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/socialite/src/Two/Concerns/InteractsWithJwks.php Introduces the shared JWKS cache and now bounds responses without usable cache directives to the configured fallback lifetime.
src/socialite/src/Two/AbstractProvider.php Centralizes token-response parsing and publishes memoized users only after mapping and token metadata assignment complete.
src/socialite/src/Concerns/HasProviderContext.php Gives cached provider instances monotonic namespaces while keeping mutable provider state in coroutine context.
src/socialite/src/Two/OpenIdProvider.php Consolidates OIDC decoding and validates nonce, audience, and issuer claims through the shared provider boundaries.
src/object-pool/src/ObjectPoolServiceProvider.php Unifies concrete and contract resolutions around the same worker-lifetime pool manager and recycler owners.
src/reverb/src/ReverbServiceProvider.php Unifies concrete and contract channel-manager resolution while preserving application binding precedence.
src/support/src/Manager.php Refreshes the retained configuration repository when a cached manager is moved to another container.

Reviews (3): Last reviewed commit: "Resolve Socialite plan approval state" | Re-trigger Greptile

Comment thread src/socialite/src/Two/Concerns/InteractsWithJwks.php
Comment thread composer.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (9)
src/socialite/src/Two/AbstractProvider.php (1)

296-302: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Declare the failure mode when access_token is missing.

parseAccessToken declares a string return type. Arr::get returns null when the provider returns an error body without access_token. PHP then throws a TypeError from inside the parser. The message does not identify the provider or the response. Consider throwing a dedicated Socialite exception so callers can handle failed token exchanges.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/socialite/src/Two/AbstractProvider.php` around lines 296 - 302, Update
AbstractProvider::parseAccessToken to explicitly detect a missing access_token
before returning it, and throw the established dedicated Socialite exception
with provider context and the token response details instead of allowing a
TypeError. Preserve returning the access token unchanged when present.
src/socialite/src/Two/User.php (1)

40-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider new static so subclasses get their own fake instance.

fake() returns new self, so a subclass of User receives a base User instance. If you want provider-specific user subclasses to support fake(), use new static and declare the return type as static.

♻️ Proposed change
-    public static function fake(#[SensitiveParameter] array $attributes = []): self
+    public static function fake(#[SensitiveParameter] array $attributes = []): static
     {
@@
-        return (new self)->setRaw($attributes)->map($attributes)
+        return (new static)->setRaw($attributes)->map($attributes)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/socialite/src/Two/User.php` around lines 40 - 61, Update User::fake() to
instantiate the late-static-bound class with new static instead of new self, and
change its return type from self to static so subclasses receive their own fake
instance while preserving the existing attribute setup.
src/socialite/src/Two/OpenIdProvider.php (1)

148-173: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Single-entry caches thrash when one provider instance serves several tenants. Both caches hold one entry in an instance property. The SocialiteManager caches the provider per driver and worker, while the effective URL comes from setConfig, which stores the value in coroutine context. When concurrent coroutines use different tenants, each request replaces the other's entry and triggers a new HTTP fetch. The check-and-return is atomic under cooperative scheduling, so no wrong-tenant data is returned; the cost is repeated network calls on the login path.

  • src/socialite/src/Two/OpenIdProvider.php#L148-L173: key $openidConfig by URL as a bounded map instead of a single ['url' => ..., 'config' => ...] entry, so tenant A and tenant B do not evict each other.
  • src/socialite/src/Two/Concerns/InteractsWithJwks.php#L61-L102: apply the same bounded per-URL map to $jwks and $jwksRefreshAttempt, and keep the existing expiry and cooldown handling per entry.

Set an explicit maximum entry count so a hostile or misconfigured tenant list cannot grow the map without bound.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/socialite/src/Two/OpenIdProvider.php` around lines 148 - 173, In
src/socialite/src/Two/OpenIdProvider.php lines 148-173, update getOpenIdConfig
to cache configurations in a URL-keyed bounded map so tenant URLs retain
separate entries; define and enforce an explicit maximum entry count with an
eviction policy. In src/socialite/src/Two/Concerns/InteractsWithJwks.php lines
61-102, apply the same bounded URL-keyed map approach to jwks and
jwksRefreshAttempt, preserving each entry’s existing expiry and refresh-cooldown
behavior.
tests/Socialite/LinkedInProviderTest.php (1)

111-124: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Make the avatar selection rule unambiguous.

The 800px image is both the widest and the last valid element. The assertion passes whether the provider picks the widest image or the last image. Place the widest image before a smaller one so the test pins down the intended rule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Socialite/LinkedInProviderTest.php` around lines 111 - 124, Reorder the
valid image fixtures in the profilePicture test data so the 800px
avatar-original image appears before the 100px avatar image, while retaining the
existing assertions for getAvatar() and avatar_original. This ensures the test
distinguishes widest-image selection from simply choosing the last valid
element.
tests/Socialite/GitlabProviderTest.php (1)

17-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering the custom host path.

This test only exercises the default https://gitlab.com host. GitlabProvider::setHost() and the host configuration key both feed getHost() and change the request URL. Add a case that sets a custom host and asserts the trailing slash is trimmed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Socialite/GitlabProviderTest.php` around lines 17 - 42, Extend
testUserRequestUsesTheVersionedApiAndBearerAuthorization to configure
GitlabProvider with a custom host via setHost, then assert the mocked request
uses that host without a trailing slash while preserving the /api/v4/user path
and bearer header expectations.
tests/Socialite/OpenIdProviderTest.php (1)

809-849: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated JWKS test helpers. Three test files now carry byte-identical createRsaKeyPair, jwks, and base64UrlEncode implementations, plus near-identical JWKS response builders that differ only by URL. The shared root cause is the absence of a common JWKS test helper, so every new OIDC provider test copies the same code and all copies must be updated together when the JWKS contract changes.

Move these helpers into one trait or fixture class under tests/Socialite/Fixtures/, then have each test file use it. Also consider caching the generated key pairs per kid inside that helper. Each call to createRsaKeyPair generates a fresh 2048-bit RSA key, and the suite now performs many such generations per run.

  • tests/Socialite/OpenIdProviderTest.php#L809-L849: remove the local createRsaKeyPair, jwks, and base64UrlEncode methods and use the shared helper.
  • tests/Socialite/GoogleProviderIdTokenTest.php#L231-L267: remove the same three local helpers and use the shared helper.
  • tests/Socialite/FacebookProviderTest.php#L183-L219: remove the same three local helpers and use the shared helper.

As per coding guidelines: "Put standalone test support files under a capitalized Fixtures/ directory."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Socialite/OpenIdProviderTest.php` around lines 809 - 849, Extract
createRsaKeyPair, jwks, and base64UrlEncode into one shared trait or fixture
class under tests/Socialite/Fixtures/, optionally caching generated keys by kid,
and update imports/usages accordingly. Remove the duplicated helpers from
tests/Socialite/OpenIdProviderTest.php lines 809-849,
tests/Socialite/GoogleProviderIdTokenTest.php lines 231-267, and
tests/Socialite/FacebookProviderTest.php lines 183-219; each site requires the
same removal and delegation to the shared helper.

Source: Coding guidelines

tests/Socialite/GoogleProviderIdTokenTest.php (1)

79-93: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use setConfig for per-request trusted audience override.

setConfig() merges into the current request configuration, while withConfig() sets the provider baseline for the worker lifetime.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Socialite/GoogleProviderIdTokenTest.php` around lines 79 - 93, Update
testItAcceptsConfiguredTrustedAudiences to apply trusted_audiences through the
per-request setConfig path, preserving the test’s existing audience and user ID
assertions; do not use withConfig, which establishes a worker-lifetime baseline.
tests/Socialite/Fixtures/OpenIdTestProviderStub.php (1)

32-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unnecessary pass-through override.

OpenIdProvider already declares protected function getUserByToken(#[SensitiveParameter] string $token): array. The stub can keep getProviderUserByToken and call inherited getUserByToken, or remove that public wrapper too. If you keep the wrapper, use parent::getUserByToken($token) so the fixture can call the stubbed base provider implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Socialite/Fixtures/OpenIdTestProviderStub.php` around lines 32 - 35,
Remove the unnecessary getUserByToken override from OpenIdTestProviderStub,
since OpenIdProvider already provides it. Preserve getProviderUserByToken only
if needed, ensuring it calls the inherited parent::getUserByToken($token)
implementation.
tests/Socialite/FacebookProviderTest.php (1)

103-114: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a stable assertion for the JWK error message.

firebase/php-jwt owns "kid" invalid, unable to lookup correct key, so this exact-mismatch test can fail across dependency updates. Use assertStringContainsString('"kid" invalid', $e->getMessage()) like the matching OpenID test instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Socialite/FacebookProviderTest.php` around lines 103 - 114, Update
testAnUnknownKidRaisesTheLibraryAuthenticationFailure to capture the expected
UnexpectedValueException and assert that its message contains '"kid" invalid'
using assertStringContainsString, rather than requiring the dependency-owned
full error message. Keep the existing unknown-key token setup and exception type
assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/object-pool/src/ObjectPoolServiceProvider.php`:
- Around line 20-22: Register PoolManager as a singleton before aliasing it to
Factory, and apply the same concrete-singleton-plus-contract-alias pattern to
ArrayChannelManager/ChannelManager in
src/reverb/src/ReverbServiceProvider.php:93 and SocialiteManager/Factory in
src/socialite/src/SocialiteServiceProvider.php:17; update the anchor in
src/object-pool/src/ObjectPoolServiceProvider.php:20-22 accordingly.

In `@src/socialite/README.md`:
- Around line 4-14: Reduce the src/socialite/README.md content to a brief
package description and the existing documentation link, removing the detailed
compatibility and provider-behavior bullets. Retain or move that guidance in the
canonical socialite documentation at src/boost/docs/socialite.md.

In `@src/socialite/src/Socialite.php`:
- Around line 14-22: Update the annotations in Socialite to use imported short
type names instead of fully qualified names: add use statements for Provider,
AbstractProvider, SocialiteManager, and Container, then replace their qualified
references in the `@method` declarations while preserving the existing signatures.

In `@src/socialite/src/Two/OpenIdProvider.php`:
- Around line 184-190: Update getUserByTokenResponse to validate that response
contains a string id_token before calling getUserByOIDCToken. If missing or
invalid, throw the appropriate Socialite exception with a clear cause-specific
message, using a dedicated exception type if ConfigurationFetchingException is
not suitable.

In `@tests/Socialite/AbstractProviderTest.php`:
- Around line 164-187: Update testRecycledObjectIdsCannotReuseProviderContext to
avoid requiring PHP to recycle the object ID within 1000 allocations: either
make the marker-key validation deterministic or skip the assertion when no
matching replacement is found, while retaining the existing validation when a
recycled ID is obtained.

In `@tests/Socialite/OpenIdProviderTest.php`:
- Around line 278-306: Guard the id_token access in
OpenIdProvider::getUserByTokenResponse by validating that it exists and is a
string, then throw the named MissingIdTokenException when validation fails
before calling getUserByOIDCToken. Update
testMissingIdTokenFailsAtTheRequiredResponseBoundary to assert
MissingIdTokenException and remove the set_error_handler, warning capture, and
TypeError-specific assertions.

---

Nitpick comments:
In `@src/socialite/src/Two/AbstractProvider.php`:
- Around line 296-302: Update AbstractProvider::parseAccessToken to explicitly
detect a missing access_token before returning it, and throw the established
dedicated Socialite exception with provider context and the token response
details instead of allowing a TypeError. Preserve returning the access token
unchanged when present.

In `@src/socialite/src/Two/OpenIdProvider.php`:
- Around line 148-173: In src/socialite/src/Two/OpenIdProvider.php lines
148-173, update getOpenIdConfig to cache configurations in a URL-keyed bounded
map so tenant URLs retain separate entries; define and enforce an explicit
maximum entry count with an eviction policy. In
src/socialite/src/Two/Concerns/InteractsWithJwks.php lines 61-102, apply the
same bounded URL-keyed map approach to jwks and jwksRefreshAttempt, preserving
each entry’s existing expiry and refresh-cooldown behavior.

In `@src/socialite/src/Two/User.php`:
- Around line 40-61: Update User::fake() to instantiate the late-static-bound
class with new static instead of new self, and change its return type from self
to static so subclasses receive their own fake instance while preserving the
existing attribute setup.

In `@tests/Socialite/FacebookProviderTest.php`:
- Around line 103-114: Update
testAnUnknownKidRaisesTheLibraryAuthenticationFailure to capture the expected
UnexpectedValueException and assert that its message contains '"kid" invalid'
using assertStringContainsString, rather than requiring the dependency-owned
full error message. Keep the existing unknown-key token setup and exception type
assertion.

In `@tests/Socialite/Fixtures/OpenIdTestProviderStub.php`:
- Around line 32-35: Remove the unnecessary getUserByToken override from
OpenIdTestProviderStub, since OpenIdProvider already provides it. Preserve
getProviderUserByToken only if needed, ensuring it calls the inherited
parent::getUserByToken($token) implementation.

In `@tests/Socialite/GitlabProviderTest.php`:
- Around line 17-42: Extend
testUserRequestUsesTheVersionedApiAndBearerAuthorization to configure
GitlabProvider with a custom host via setHost, then assert the mocked request
uses that host without a trailing slash while preserving the /api/v4/user path
and bearer header expectations.

In `@tests/Socialite/GoogleProviderIdTokenTest.php`:
- Around line 79-93: Update testItAcceptsConfiguredTrustedAudiences to apply
trusted_audiences through the per-request setConfig path, preserving the test’s
existing audience and user ID assertions; do not use withConfig, which
establishes a worker-lifetime baseline.

In `@tests/Socialite/LinkedInProviderTest.php`:
- Around line 111-124: Reorder the valid image fixtures in the profilePicture
test data so the 800px avatar-original image appears before the 100px avatar
image, while retaining the existing assertions for getAvatar() and
avatar_original. This ensures the test distinguishes widest-image selection from
simply choosing the last valid element.

In `@tests/Socialite/OpenIdProviderTest.php`:
- Around line 809-849: Extract createRsaKeyPair, jwks, and base64UrlEncode into
one shared trait or fixture class under tests/Socialite/Fixtures/, optionally
caching generated keys by kid, and update imports/usages accordingly. Remove the
duplicated helpers from tests/Socialite/OpenIdProviderTest.php lines 809-849,
tests/Socialite/GoogleProviderIdTokenTest.php lines 231-267, and
tests/Socialite/FacebookProviderTest.php lines 183-219; each site requires the
same removal and delegation to the shared helper.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d6818157-7adb-429d-ad4f-cf1c129fa254

📥 Commits

Reviewing files that changed from the base of the PR and between dda0344 and d554137.

📒 Files selected for processing (59)
  • composer.json
  • docs/plans/2026-07-12-0900-framework-coroutine-state-lifecycle-audit.md
  • docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
  • docs/plans/2026-08-07-1416-socialite-correctness-first-party-extensibility-and-lifecycle.md
  • src/boost/docs/socialite.md
  • src/object-pool/src/ObjectPoolServiceProvider.php
  • src/reverb/src/ReverbServiceProvider.php
  • src/socialite/README.md
  • src/socialite/composer.json
  • src/socialite/src/AbstractProvider.php
  • src/socialite/src/Concerns/HasProviderContext.php
  • src/socialite/src/Contracts/Factory.php
  • src/socialite/src/HasProviderContext.php
  • src/socialite/src/Socialite.php
  • src/socialite/src/SocialiteManager.php
  • src/socialite/src/SocialiteServiceProvider.php
  • src/socialite/src/Testing/SocialiteFake.php
  • src/socialite/src/Two/AbstractProvider.php
  • src/socialite/src/Two/BitbucketProvider.php
  • src/socialite/src/Two/Concerns/InteractsWithJwks.php
  • src/socialite/src/Two/Exceptions/ConfigurationFetchingException.php
  • src/socialite/src/Two/Exceptions/InvalidUserInfoUrlException.php
  • src/socialite/src/Two/FacebookProvider.php
  • src/socialite/src/Two/GithubProvider.php
  • src/socialite/src/Two/GitlabProvider.php
  • src/socialite/src/Two/GoogleProvider.php
  • src/socialite/src/Two/LinkedInOpenIdProvider.php
  • src/socialite/src/Two/LinkedInProvider.php
  • src/socialite/src/Two/OpenIdProvider.php
  • src/socialite/src/Two/SlackOpenIdProvider.php
  • src/socialite/src/Two/SlackProvider.php
  • src/socialite/src/Two/Token.php
  • src/socialite/src/Two/TwitchProvider.php
  • src/socialite/src/Two/User.php
  • src/socialite/src/Two/XProvider.php
  • src/support/src/Manager.php
  • tests/ObjectPool/ObjectPoolServiceProviderTest.php
  • tests/Reverb/ReverbServiceProviderTest.php
  • tests/Socialite/AbstractProviderTest.php
  • tests/Socialite/BitbucketProviderTest.php
  • tests/Socialite/FacebookProviderTest.php
  • tests/Socialite/Fixtures/GenericTestProviderStub.php
  • tests/Socialite/Fixtures/OAuthTwoTestProviderStub.php
  • tests/Socialite/Fixtures/OpenIdTestProviderStub.php
  • tests/Socialite/Fixtures/VerifyingOpenIdTestProviderStub.php
  • tests/Socialite/GitlabProviderTest.php
  • tests/Socialite/GoogleProviderIdTokenTest.php
  • tests/Socialite/GoogleProviderTest.php
  • tests/Socialite/LinkedInOpenIdProviderTest.php
  • tests/Socialite/LinkedInProviderTest.php
  • tests/Socialite/OAuthTwoTest.php
  • tests/Socialite/OpenIdProviderTest.php
  • tests/Socialite/PackageMetadataTest.php
  • tests/Socialite/SensitiveParameterTest.php
  • tests/Socialite/SlackOpenIdProviderTest.php
  • tests/Socialite/SlackProviderTest.php
  • tests/Socialite/SocialiteFakeTest.php
  • tests/Socialite/SocialiteManagerTest.php
  • tests/Support/ManagerTest.php
💤 Files with no reviewable changes (3)
  • src/socialite/src/HasProviderContext.php
  • composer.json
  • src/socialite/composer.json

Comment thread src/object-pool/src/ObjectPoolServiceProvider.php
Comment thread src/socialite/README.md
Comment thread src/socialite/src/Socialite.php
Comment thread src/socialite/src/Two/OpenIdProvider.php
Comment thread tests/Socialite/AbstractProviderTest.php
Comment thread tests/Socialite/OpenIdProviderTest.php
Apply a five-minute fallback when an identity provider omits usable cache directives so revoked signing keys cannot remain trusted for the worker lifetime. Keep explicit no-cache, no-store, and max-age directives authoritative, tighten the cached expiry shape, and retain the existing forced-refresh cooldown behavior.\n\nAdd deterministic coverage for both fallback caching and expiry, consolidate the repeated RSA/JWK test fixtures without caching key material, stabilize the Facebook unknown-key assertion, and remove the no-op OpenID test override. Document the observable fallback cadence for custom OpenID Connect providers.
Construct OAuth 2 fake users with late static binding so ecosystem-specific User subclasses receive instances of the called class instead of the base Socialite user. Cover the inherited factory directly.\n\nStrengthen LinkedIn avatar mapping coverage with a later unrelated image so the regression continues to prove exact 100px and 800px selection rather than accidentally accepting the final image.
Replace the superseded indefinite-headerless JWKS design with the reviewed five-minute provider fallback in the Socialite plan and audit ledger. Record the non-null cache shape, deterministic regression boundary, performance effect, and late-static User fake divergence without retaining rejected reasoning or decision history.
@binaryfire

Copy link
Copy Markdown
Collaborator Author

@greptileai @coderabbitai Thanks for the review. The follow-up is pushed.

The JWKS finding was valid. Responses without a usable cache directive now expire after five minutes instead of remaining fresh for the worker lifetime. Explicit no-cache, no-store, and valid max-age directives remain authoritative, and focused regressions cover reuse and expiry.

The same pass also:

  • preserves late-static behavior for provider-specific User subclasses;
  • consolidates the repeated JWKS fixtures without caching key material;
  • strengthens the LinkedIn image-selection regression;
  • removes a dead OpenID test override and stabilizes the Facebook unknown-key assertion;
  • updates the public guide and audit records to describe bounded key freshness.

I left the other suggestions unchanged after tracing them against the repository contracts. Hypervel aliases already converge on the auto-singleton concrete, the README section is the required concise Differences From Laravel surface, facade annotations are generated, missing required token fields intentionally fail at the strict native boundary, and the recycled-object-ID regression remains load-bearing. The root lockfile is intentionally untracked; the dependency graph was refreshed through Composer after merging the current 0.4, both manifests validate, and phpseclib/phpseclib is absent.

Validation is green across the Socialite suite, static analysis, the full repository gate, and package-mode checks.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Implementation outline =='
ast-grep outline src/socialite/src/Two/Concerns/InteractsWithJwks.php --items all

echo '== Cache directive and TTL implementation =='
rg -n -C 8 --glob 'InteractsWithJwks.php' \
  'max-age|no-cache|no-store|Cache-Control|cache|ttl|TTL|300|five' \
  src/socialite/src/Two/Concerns/InteractsWithJwks.php

echo '== Relevant JWKS cache regression references =='
rg -n -C 5 --glob '*Test.php' \
  'max-age|no-cache|no-store|Cache-Control|cache|expiry|expire|JWKS|Jwks' \
  tests/Socialite

Length of output: 60898


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== JWKS expiration calculation =='
sed -n '110,155p' src/socialite/src/Two/Concerns/InteractsWithJwks.php

echo '== Test-only default TTL configuration =='
rg -n -C 6 --glob '*.php' \
  'setJwksDefaultTtlSeconds\s*\(' \
  src/socialite tests/Socialite

echo '== Missing-directives and malformed-directives tests =='
sed -n '585,627p' tests/Socialite/OpenIdProviderTest.php

Length of output: 5564


@binaryfire The JWKS finding is addressed.

Static inspection confirms that InteractsWithJwks uses a 300-second fallback TTL. It gives no-cache and no-store immediate expiry. It uses the smallest valid max-age value. It uses the fallback TTL when directives are missing, malformed, or overflow.

OpenIdProviderTest covers immediate expiry and deterministic fallback expiry by setting the fallback TTL to zero. I did not run the test suite in this environment.

I also noted the repository contracts you described for aliases, generated facade annotations, strict token parsing, and recycled-object-ID coverage.

ᕱ⑅ᕱ


✏️ Learnings added
Learnt from: binaryfire
URL: https://github.com/hypervel/components/pull/485

Timestamp: 2026-08-07T18:48:00.297Z
Learning: In the Hypervel container, aliases converge on the auto-singleton concrete binding. Do not flag the alias registrations in service providers as separate-instance risks unless the binding behavior changes.

Learnt from: binaryfire
URL: https://github.com/hypervel/components/pull/485

Timestamp: 2026-08-07T18:48:00.297Z
Learning: The `src/socialite/src/Socialite.php` facade annotations are generated. Review the manager-facing documented surface rather than requesting manual additions to generated facade annotations.

Learnt from: binaryfire
URL: https://github.com/hypervel/components/pull/485

Timestamp: 2026-08-07T18:48:00.297Z
Learning: In Hypervel Socialite OAuth token parsing, missing required token fields intentionally fail at the strict native type boundary. Do not request an earlier custom validation layer unless the public contract changes.

Learnt from: binaryfire
URL: https://github.com/hypervel/components/pull/485

Timestamp: 2026-08-07T18:48:00.297Z
Learning: The recycled-object-ID regression in `tests/Socialite/AbstractProviderTest.php` is load-bearing coverage for coroutine-local provider context. Preserve this test when reviewing context lifecycle changes.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown

The JWKS fix in InteractsWithJwks.php is correct and clean. The implementation handles all three cases properly:

  • no-cache/no-store$now, forcing a fetch on the next call
  • Valid max-age$now + $seconds, with careful zero-pad normalization and overflow guard
  • No usable directive → $now + 300, bounding the worker-lifetime freshness risk that the original finding flagged

The cooldown guard on forced refreshes pairs well with the 5-minute default — a rotation burst won't hammer the endpoint, and stale keys won't persist longer than a predictable window.

The rationale for the unchanged items is sound. The alias-to-concrete convergence, generated facade annotations, strict native boundary for missing token fields, and the recycled-object-ID regression are all repository-specific constraints that aren't visible from the diff alone. Thanks for tracing and documenting each one.

This looks good to merge.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@docs/plans/2026-08-07-1416-socialite-correctness-first-party-extensibility-and-lifecycle.md`:
- Line 466: Resolve the contradiction between the final decisions in the early
plan section and the approval requirement in the paragraph containing “Before
source implementation.” Record owner approval and remove the pending gate for
the listed API changes, or consistently mark those decisions as pending and
state that implementation is blocked until approval is recorded.
- Line 74: Update the JWKS hot path around driver() so mutable state such as
$jwks and $jwksRefreshAttempt is isolated per coroutine or stored in a
coroutine-safe bounded cache keyed by the exact JWKS URL, rather than on the
shared cached provider instance. Preserve the refresh, cooldown, and
rotation-retry behavior, and add an interleaved coroutine test proving
concurrent requests cannot overwrite each other’s keys or refresh state.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fbe633c1-4b4b-41bd-aa56-fc76842c1368

📥 Commits

Reviewing files that changed from the base of the PR and between d554137 and b79d85f.

📒 Files selected for processing (15)
  • composer.json
  • docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
  • docs/plans/2026-08-07-1416-socialite-correctness-first-party-extensibility-and-lifecycle.md
  • src/boost/docs/socialite.md
  • src/reverb/src/ReverbServiceProvider.php
  • src/socialite/src/Two/Concerns/InteractsWithJwks.php
  • src/socialite/src/Two/User.php
  • tests/Socialite/FacebookProviderTest.php
  • tests/Socialite/Fixtures/CreatesJwksFixtures.php
  • tests/Socialite/Fixtures/OpenIdTestProviderStub.php
  • tests/Socialite/Fixtures/VerifyingOpenIdTestProviderStub.php
  • tests/Socialite/GoogleProviderIdTokenTest.php
  • tests/Socialite/LinkedInProviderTest.php
  • tests/Socialite/OpenIdProviderTest.php
  • tests/Socialite/SocialiteFakeTest.php
💤 Files with no reviewable changes (1)
  • tests/Socialite/Fixtures/OpenIdTestProviderStub.php
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/reverb/src/ReverbServiceProvider.php
  • composer.json
  • tests/Socialite/LinkedInProviderTest.php
  • tests/Socialite/FacebookProviderTest.php
  • src/socialite/src/Two/User.php
  • tests/Socialite/Fixtures/VerifyingOpenIdTestProviderStub.php
  • tests/Socialite/OpenIdProviderTest.php
  • src/boost/docs/socialite.md
  • tests/Socialite/GoogleProviderIdTokenTest.php

Replace the stale pre-implementation owner gate with the final intentional API decisions now present in the package. Keep the compatibility rationale and Laravel-facing contract summary while removing procedural text that contradicted the completed implementation.
@binaryfire
binaryfire merged commit de04fad into 0.4 Aug 7, 2026
37 of 38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant