Installation
composer require patrickbussmann/oauth2-apple:^0.4.0
Add the provider to your config/auth.php under providers:
'apple' => [
'client_id' => env('APPLE_CLIENT_ID'),
'team_id' => env('APPLE_TEAM_ID'),
'key_id' => env('APPLE_KEY_ID'),
'private_key' => env('APPLE_PRIVATE_KEY'),
'redirect' => env('APPLE_REDIRECT_URI'),
],
Environment Variables
Store sensitive keys in .env:
APPLE_CLIENT_ID=your_client_id
APPLE_TEAM_ID=your_team_id
APPLE_KEY_ID=your_key_id
APPLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...
APPLE_REDIRECT_URI=http://your-app.com/auth/apple/callback
First Use Case: Add Login Button
Use the Socialite facade to generate an Apple login link:
use Laravel\Socialite\Facades\Socialite;
$appleAuthUrl = Socialite::driver('apple')->stateless()->redirect()->getTargetUrl();
Render a button in your Blade view:
<a href="{{ $appleAuthUrl }}" class="btn-apple">Sign in with Apple</a>
Redirect to Apple
return Socialite::driver('apple')->stateless()->redirect();
stateless() for SPF compliance (required for Apple).Handle Callback
public function handleAppleCallback()
{
try {
$user = Socialite::driver('apple')->stateless()->user();
// Attach or create user in your system
return redirect()->route('dashboard');
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
// Enhanced error handling with error_description
\Log::error('Apple OAuth Error: ' . $e->getMessage() . ' | Description: ' . $e->getErrorDescription());
return back()->with('error', 'Apple login failed. Please try again.');
}
}
User Data Handling Extract key fields from the response:
$email = $user->getEmail(); // May be null (privacy-preserving)
$name = $user->getName();
$userId = $user->getId(); // Unique Apple ID
Fallback for Missing Email Apple may omit the email for privacy. Use a placeholder or prompt the user to provide it:
$email = $user->getEmail() ?? 'apple_' . $user->getId() . '@user.example.com';
State Management For CSRF protection, pass a state parameter:
$state = Str::random(40);
session(['apple_state' => $state]);
$appleAuthUrl = Socialite::driver('apple')->stateless()->with(['state' => $state])->redirect()->getTargetUrl();
Testing Use a mock provider for local testing. Note: PHP 8.3+ is now required for testing:
Socialite::driver('apple')->shouldReceive('stateless')->andReturnSelf();
Socialite::driver('apple')->shouldReceive('user')->andReturn((new MockUser())->setEmail('test@example.com'));
SPF Requirement
stateless()). Omitting it will fail with:
Error: Invalid client configuration. SPF must be enabled.
Socialite::driver('apple')->stateless().Email Privacy
null for email. Handle this gracefully:
if (!$user->getEmail()) {
// Prompt user to enter email or use a generated one.
}
Key Rotation
.env and clear cached configurations:
php artisan config:clear
Redirect URI Mismatch
APPLE_REDIRECT_URI matches exactly (including http/https) with your Apple Developer account settings. A mismatch throws:
Error: Redirect URI mismatch.
PHP Version Compatibility
Enhanced Error Handling
The package now includes error_description in exception messages. Log errors for debugging:
try {
$user = Socialite::driver('apple')->stateless()->user();
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
\Log::debug('Apple OAuth Error Details:', [
'message' => $e->getMessage(),
'error_description' => $e->getErrorDescription(),
'response' => $e->getResponseBody(),
]);
}
Common Errors
| Error Message | Solution |
|---|---|
invalid_client |
Verify APPLE_CLIENT_ID and APPLE_TEAM_ID in Apple Developer Console. |
invalid_request |
Check APPLE_REDIRECT_URI format. |
invalid_scope |
Ensure you’re requesting only email or name (Apple’s allowed scopes). |
Server Error: 500 (no details) |
Enable debug mode (APP_DEBUG=true) and check logs. |
| `Error: [message] | Description: [details]` |
Custom User Mapping Override the default user mapping in a service provider:
public function boot()
{
Socialite::extend('apple', function ($app) {
$config = $app['config']['services.apple'];
return Socialite::buildProvider(
AppleProvider::class,
$config
)->setUserFromResponseCallback(function ($response) {
return new CustomAppleUser($response);
});
});
}
Additional Scopes
Apple only supports email and name scopes. Extend the provider to handle custom logic:
$provider = Socialite::driver('apple')->stateless();
$provider->scopes(['email', 'name']); // Apple ignores extra scopes but won’t error.
Webhook Validation
For server-side validation (e.g., JWT), use the apple package’s verify() method:
use Patrickbussmann\OAuth2Apple\Apple;
$isValid = Apple::verify($user->token, $user->raw);
How can I help you explore Laravel packages today?