Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Apple Laravel Package

socialiteproviders/apple

Apple provider for Laravel Socialite, enabling Sign in with Apple via a native OAuth provider. Install with Composer, configure your Apple client ID, team ID, key ID, and private key, then authenticate users with a single Socialite call. Maintained by the Socialite Providers community.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

socialiteproviders/apple is a driver plugin for Laravel Socialite, not an auth system. It is a subtree split of the SocialiteProviders/Providers monorepo, and the surface is deliberately thin: SocialiteProviders\Apple\Provider implements roughly four methods (getAuthUrl, getTokenFields, getUserByToken, mapUserToObject). It plugs into the Socialite driver registry via the SocialiteWasCalled event, so it inherits the same ergonomics, controller shape, and test seams as the Google/Facebook/GitHub drivers already in a typical Laravel app.

That is a good architectural fit if and only if the product's auth layer already speaks Socialite. If login is currently a bespoke OAuth implementation, this package introduces a second auth idiom rather than simplifying the first — a consolidation cost worth naming before adoption.

The critical fit boundary: the package does not sign the ES256 client secret, does not verify JWKS, and does not revoke tokens. Those are integration responsibilities. Architecturally this should sit behind an internal SignInWithApple service (redirect(), userFromCallback(Request), revoke(string $refreshToken)), so the Socialite-specific surface lives in one class. That boundary is what makes the vendor-risk story tolerable and an exit cheap.

Integration feasibility

Feasible in days for the happy path, weeks for the full lifecycle.

Workstream Effort Owner
Apple Developer provisioning (Services ID, .p8, Team/Key ID, byte-matched Return URL) Low, but blocking TPM + Apple account holder
Composer install + provider registration Hours Backend
config/services.php entry + .env Hours Backend
POST callback route + CSRF exemption + SameSite=none Hours, high footgun density Backend
Client secret signing (ES256 JWT) + cache/scheduler 1–2 days Backend
User model: apple_id column, firstOrCreate on sub Hours Backend
First-consent name/email capture Half day Backend
Refresh-token capture + revocation endpoint (if App Store) 2–4 days Backend
Multi-audience config + security sign-off (6.1.0) 1–3 days Backend + Security

The prerequisite chain is the schedule risk, not the code. The Return URL must be HTTPS and byte-match the Apple console entry — no trailing slash, no query string. http://localhost will not work, so local development requires ngrok/expose/Cloudflare Tunnel. If the Apple Developer account holder is not on the team or the Sign in with Apple capability is not enabled on the identifier, the sprint stalls before line one of code.

Technical risk

  1. Vendor concentration. 58 stars, 0 listed dependents, single-maintainer-adjacent project (@atymic authored the 6.1.0 change). This is a real continuity risk for anything on the login critical path. Mitigation is not "find a better package" — there isn't a meaningfully better Laravel-native option — it's the internal adapter boundary plus version pinning.
  2. Config landmines dominate the failure surface. POST callback registered as GET → 419/invalid state. SESSION_SAME_SITE=lax (Laravel's default) → session cookie absent on Apple's cross-site POST → state check fails. Both are one-line fixes that cost days if discovered in staging.
  3. Secret expiry is a silent time bomb. Apple caps the client secret JWT at six months. A static JWT in .env works through launch and breaks a quarter later with invalid_client. This must be a scheduled job from day one, not a follow-up ticket.
  4. Mobile token validation is not provided. userFromToken($idToken) decodes the payload; it does not verify the signature against https://appleid.apple.com/auth/keys, nor check iss/exp/aud. For a token originating from a client you don't control, treating that as authentication is a security defect. Budget firebase/php-jwt JWKS verification explicitly.
  5. 6.1.0 audience handling is behavior-changing. Multiple identity-token audiences unblock web (Services ID) + native (bundle ID) on one backend, but audience validation is security-sensitive. Confirm from the pinned source how the audience list is consumed rather than assuming config shape.

Key questions

  • Do we need native (bundle ID) and web (Services ID) audiences at launch, or is web-only sufficient for the first release? This determines whether 6.1.0's multi-audience support is in scope or a later story.
  • Who holds the Apple Developer account and the .p8 private key, and what is the rotation/escrow procedure if that individual leaves?
  • Does the product have an account-deletion commitment (or App Store distribution)? If yes, refresh-token capture and a revocation endpoint are in scope, and user() — which hides the raw token response — cannot be the integration point.
  • What is the account-linking policy when an Apple sign-in returns an email matching an existing password account? Apple private-relay addresses (@privaterelay.appleid.com) make email an unreliable key; the sub claim must be the identity anchor.
  • Are we funding the internal adapter interface, or accepting a direct Socialite dependency in controllers?

Integration Approach

Stack fit

Laravel 10–12 with Socialite. Requires socialiteproviders/apple and socialiteproviders/manager (the manager is what dispatches SocialiteWasCalled). Add firebase/php-jwt for client-secret signing and, if any mobile flow exists, JWKS verification. Storage: one apple_id column on users (indexed, unique), plus an encrypted apple_refresh_token column if revocation is in scope. No migration of existing rows — Apple sign-in is purely additive.

Migration path

  • Existing users: no data migration. Apple is an additional login method; established sessions and password logins are untouched.
  • Account linking: resolve by sub, never by email. Relay addresses and email changes make email unstable as a key. Decide the linking policy for an Apple login whose email collides with an existing password account — auto-link, or require an authenticated link step? Auto-linking on an unverified relay address is an account-takeover vector; default to explicit linking.
  • Provider registration differs by Laravel version. Laravel ≤10 uses EventServiceProvider::$listen. Laravel 11/12 ship without that file, so registration goes in AppServiceProvider::boot() via Event::listen(...) and $event->extendSocialite('apple', Provider::class). Pick the pattern matching the pinned framework version and document it — this is the source of Driver [apple] is not supported.
  • Callback contract change: the callback route must be Route::post, and the CSRF exemption is a hard requirement (Laravel 11+: $middleware->validateCsrfTokens(except: ['auth/apple/callback']); Laravel 10: VerifyCsrfToken::$except). If the product's web tier is behind a proxy or CDN, confirm SESSION_SAME_SITE=none (HTTPS-only) survives that layer.

Compatibility

  • Pin the version explicitly and read the 6.1.0 changelog before upgrading. Provider behavior changed; do not float the constraint on a login-path dependency.
  • Set SESSION_SAME_SITE=none for the browser flow, or adopt stateless() — but stateless() disables the state check by design, so it is only legitimate for API clients carrying their own id_token, never the browser redirect flow.
  • Scopes ['name','email'] force Apple into response_mode=form_post, which is why the callback is POST. Dropping scopes silently changes the contract back to GET and quietly breaks the route.

Sequencing

  1. Apple Developer provisioning: identifier, Sign in with Apple capability, .p8 key, Return URL recorded byte-exact in config.
  2. composer require socialiteproviders/apple socialiteproviders/manager; register the SocialiteWasCalled listener for the target Laravel version.
  3. Client-secret signing service + cache (Cache::remember('apple.client_secret', now()->addDays(30), ...)) + an apple:secret artisan command wired to the scheduler. Do this before the first staging deploy, not after.
  4. Redirect + POST callback + CSRF exemption + SameSite configuration, deployed together as one atomic change.
  5. User model changes and firstOrCreate(['apple_id' => ...]) resolution by sub.
  6. First-consent profile capture: read $request->input('user'), JSON-decode, concatenate firstName/lastName, persist once. Use firstOrCreate, not updateOrCreate, so later nulls don't clobber a stored name.
  7. Refactor behind the internal SignInWithApple interface; controllers depend on the interface, not Socialite.
  8. Feature tests mocking the driver (Socialite::shouldReceive('driver->user')), including a POST with a user form field to exercise the first-login path, and an assertion that the CSRF exception is in place.
  9. If App Store distribution or a deletion commitment exists: capture refresh_token by calling getAccessTokenResponse() explicitly, store encrypted, and build the revoke call to https://appleid.apple.com/auth/revoke.
  10. Multi-audience configuration, if native is in scope — gated behind security review, not shipped as a free upgrade.

Operational Impact

Maintenance

  • Non-negotiable recurring job: client secret regeneration. Apple's six-month maximum plus a 30-day cache window means a monthly regeneration cadence is safe. Missing this produces a invalid_client outage six months post-launch, on the login path, with no obvious cause.
  • Upstream tracking: the package is small and the maintainer footprint is small. Assign explicit ownership of watching SocialiteProviders/Providers releases and reading provider-behavior changelogs (the 6.1.0 audience change is the template — a behavior change shipped as a minor version).
  • Key material lifecycle: the .p8 file is a long-lived credential. It needs a documented location, a backup, an access restriction, and a procedure for the case where it is lost or the owning Apple account changes hands.

Support

Support volume will concentrate in a small number of recognizable error strings. Publish a runbook mapping symptom → cause:

Symptom Cause Fix
invalid_client Bad client-secret JWT: wrong sub (must be client ID, not Team ID), wrong kid, stale exp, mangled newlines in the .p8 Regenerate secret; file_get_contents the key rather than env-embedding it
Driver [apple] is not supported SocialiteWasCalled listener not registered Check service provider; php artisan config:clear
invalid state / 419 Callback registered as GET, or CSRF not exempted, or SameSite=lax suppressing the session cookie POST route, CSRF exception, SameSite=none
Redirect-URI mismatch Return URL differs by a trailing slash from the Apple console entry Byte-match both sides

Debugging guidance: log the raw token response outside production only, and inspect the id_token at jwt.io (it is unencrypted) checking aud, iss, exp, sub. Note clock skew on exp.

Scaling

The flow itself is cheap — Apple's endpoints are the only external hop, and user resolution is a single indexed lookup on apple_id. Two real scaling considerations:

  • Login-path latency now depends on Apple. The token exchange is a synchronous network call to https://appleid.apple.com in the critical path of every sign-in attempt. Apple-side degradation becomes your sign-in outage. For products where login is revenue-critical, surface this as a known dependency rather than discovering it in an incident review.
  • Caching the signed secret removes JWT signing from the per-request path and from the blast radius of a key-loading error. Without it, every login attempt reads a private key from disk and signs a JWT.

Failure modes

  • Silent secret expiry (highest severity, lowest observability) — launch works, breaks at month six, affects all Apple logins at once. Mitigated only by the scheduled job plus a synthetic login check.
  • Cross-site POST blocked by cookie policy — appears as flaky invalid state affecting some browsers/environments only. The single highest-probability integration defect in this package.
  • Name loss on subsequent logins — Apple sends name/email only on first consent. If the code path uses updateOrCreate, a returning user with null values overwrites a stored name. Product-visible data-quality bug that surfaces weeks later in support tickets and lifecycle email failures.
  • Private relay emails — @privaterelay.appleid.com addresses plus email_verified sometimes arriving as the string "true". Any naive casting or email-derived identity logic fails here. Decide before design whether marketing can rely on Apple-derived emails at all.
  • Unverified mobile tokens — the failure is not an error, it's an accepted forged identity. Security-severity, and invisible without deliberate review.
  • Missing revocation — not a runtime failure; an App Store rejection or a compliance finding at the worst possible moment.
  • Multi-audience misconfiguration — rejecting legitimate native tokens, or (worse) accepting tokens from an audience you didn't intend.

Ramp-up

Low for a Laravel developer — the driver is idiomatic Socialite and the controller is boring by design. Realistic budget:

  • 1 sprint for provisioning, package integration, POST callback with CSRF/SameSite, secret signing, user resolution, first-consent capture, and feature tests.
  • +1 sprint for the lifecycle tail: internal adapter boundary, refresh-token capture and revocation (if App Store/compliance applies), JWKS verification (if native applies), multi-audience configuration and security sign-off.
  • Ongoing: near-zero engineering maintenance beyond the scheduled secret job, plus a named owner for upstream release review.

Frame the funding ask honestly with stakeholders: this package buys the OAuth2 adapter. It does not buy Apple auth. Secret signing, token verification, and revocation are engineering deliverables that exist whether or not this package is adopted — which makes the build-vs-buy decision narrow ("buy the adapter, build the lifecycle") rather than strategic.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib