PHP code example of danyseifeddine / keychain

1. Go to this page and download the library: Download danyseifeddine/keychain library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

danyseifeddine / keychain example snippets


return [
    // Enable social providers
    'providers' => [
        'google' => true,   // Enable Google OAuth
        'github' => true,   // Enable GitHub OAuth
        'facebook' => false, // Disable Facebook OAuth
    ],

    // Basic routes configuration
    'routes' => [
        'enabled' => true,
        'prefix' => 'auth',
        'middleware' => ['web'],
    ],

    // User management
    'user' => [
        'auto_create' => true,
        'auto_link' => true,
        'update_on_login' => true,
    ],
];

// Redirect to provider
GET /auth/{provider}/redirect

// Handle callback
GET /auth/{provider}/callback

// Unlink account
DELETE /auth/{provider}/unlink



namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Danyseifeddine\Keychain\Traits\HasSocialAccounts;

class User extends Authenticatable
{
    use HasSocialAccounts;

    protected $fillable = [
        'name', 'email', 'password', 'avatar_url'
    ];
}

// config/keychain.php
return [
    'guards' => [
        'enabled' => true,
        'default_guard' => 'web',

        'available_guards' => [
            // Regular users
            'web' => [
                'enabled' => true,
                'model' => App\Models\User::class,
                'table' => 'users',
                'name' => 'User',
                'redirect_after_auth' => '/dashboard',
                'middleware' => ['web'],
            ],

            // Restaurant owners
            'restaurant' => [
                'enabled' => true,
                'model' => App\Models\Restaurant::class,
                'table' => 'restaurants',
                'name' => 'Restaurant',
                'redirect_after_auth' => '/restaurant-dashboard',
                'middleware' => ['web'],
            ],

            // Admin users
            'admin' => [
                'enabled' => true,
                'model' => App\Models\Admin::class,
                'table' => 'admins',
                'name' => 'Administrator',
                'redirect_after_auth' => '/admin/dashboard',
                'middleware' => ['web', 'admin'],
            ],
        ],
    ],
];


// app/Models/Restaurant.php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Danyseifeddine\Keychain\Traits\HasSocialAccounts;

class Restaurant extends Authenticatable
{
    use HasSocialAccounts;

    protected $fillable = [
        'name', 'email', 'password', 'website', 'description'
    ];
}

// Generic routes (uses default guard or guard parameter)
GET /auth/{provider}/redirect?guard=restaurant
GET /auth/{provider}/callback?guard=restaurant

// Guard-specific routes (recommended)
GET /auth/restaurant/{provider}/redirect
GET /auth/restaurant/{provider}/callback
GET /auth/admin/{provider}/redirect
GET /auth/admin/{provider}/callback



namespace App\Http\Controllers\Auth;

use Danyseifeddine\Keychain\Http\Controllers\SocialAuthController as BaseController;

class SocialAuthController extends BaseController
{
    /**
     * 🪄 MAGIC METHODS - KeyChain automatically calls these based on guard name
     *
     * Pattern: create{Guard}User() and update{Guard}User()
     * Examples: createWebUser(), createRestaurantUser(), createAdminUser()
     */

    /**
     * Custom user creation for 'web' guard
     */
    protected function createWebUser($socialiteUser)
    {
        return \App\Models\User::create([
            'name' => $socialiteUser->getName(),
            'email' => $socialiteUser->getEmail(),
            'email_verified_at' => now(),
            'password' => bcrypt(str()->random(12)),
            'avatar_url' => $socialiteUser->getAvatar(),
        ]);
    }

    /**
     * Custom restaurant creation for 'restaurant' guard
     */
    protected function createRestaurantUser($socialiteUser)
    {
        return \App\Models\Restaurant::create([
            'name' => 'Custom Restaurant Name', // Your custom logic
            'email' => $socialiteUser->getEmail(),
            'website' => 'https://example.com',
            'description' => 'Restaurant created via social authentication',
            'password' => bcrypt(str()->random(12)),
            'status' => 'active',
        ]);
    }

    /**
     * Custom admin creation for 'admin' guard
     */
    protected function createAdminUser($socialiteUser)
    {
        // Custom validation - only allow specific emails as admins
        $allowedEmails = ['[email protected]', '[email protected]'];

        if (!in_array($socialiteUser->getEmail(), $allowedEmails)) {
            throw new \Exception('Unauthorized admin access attempt');
        }

        return \App\Models\Admin::create([
            'name' => $socialiteUser->getName(),
            'email' => $socialiteUser->getEmail(),
            'role' => 'admin',
            'permissions' => ['manage_users', 'view_reports'],
            'password' => bcrypt(str()->random(12)),
        ]);
    }

    /**
     * Update user data for 'web' guard
     */
    protected function updateWebUser($user, $socialiteUser)
    {
        $user->update([
            'name' => $socialiteUser->getName(),
            'avatar_url' => $socialiteUser->getAvatar(),
            'last_login_at' => now(),
        ]);

        return $user;
    }

    /**
     * Update restaurant data for 'restaurant' guard
     */
    protected function updateRestaurantUser($restaurant, $socialiteUser)
    {
        // Don't update name for restaurants - keep original
        $restaurant->update([
            'last_login_at' => now(),
            'social_avatar' => $socialiteUser->getAvatar(),
        ]);

        return $restaurant;
    }
}

class SocialAuthController extends BaseController
{
    /**
     * Called before redirecting to social provider
     */
    protected function beforeRedirect(string $provider): void
    {
        // Log authentication attempts
        Log::info("Social auth redirect to {$provider}", [
            'ip' => request()->ip(),
            'user_agent' => request()->userAgent(),
        ]);

        // Custom logic before redirect
        if ($provider === 'github' && !config('app.allow_github')) {
            throw new Exception('GitHub authentication is temporarily disabled');
        }
    }

    /**
     * Called after successful authentication
     */
    protected function afterAuthentication(string $provider, $socialiteUser, $token, $user): void
    {
        // Send welcome email
        if ($user->wasRecentlyCreated) {
            Mail::to($user)->send(new WelcomeEmail($user, $provider));
        }

        // Log successful authentication
        Log::info("User authenticated via {$provider}", [
            'user_id' => $user->id,
            'provider' => $provider,
        ]);

        // Update user statistics
        $user->increment('login_count');
        $user->update(['last_social_login' => now()]);
    }

    /**
     * Called when authentication fails
     */
    protected function onAuthenticationFailure(string $provider, \Exception $exception): ?\Illuminate\Http\RedirectResponse
    {
        // Log the error
        Log::error("Social auth failed for {$provider}", [
            'error' => $exception->getMessage(),
            'trace' => $exception->getTraceAsString(),
        ]);

        // Custom error handling
        if ($exception instanceof RateLimitExceededException) {
            return redirect('/login')
                ->with('error', 'Too many authentication attempts. Please try again later.');
        }

        // Return null to use default error handling
        return null;
    }
}

class SocialAuthController extends BaseController
{
    /**
     * Completely override the authentication callback
     * Return null to use KeyChain's default flow
     */
    protected function handleCustomCallback(string $provider): ?\Illuminate\Http\RedirectResponse
    {
        // Your completely custom authentication logic
        $socialiteUser = Socialite::driver($provider)->user();

        // Custom user creation/retrieval logic
        $user = $this->myCustomUserLogic($socialiteUser, $provider);

        // Custom token storage
        $this->myCustomTokenStorage($user, $socialiteUser, $provider);

        // Custom login
        Auth::login($user);

        // Custom redirect
        return redirect('/my-custom-dashboard')
            ->with('success', "Welcome! You've been authenticated via {$provider}");
    }

    private function myCustomUserLogic($socialiteUser, $provider)
    {
        // Your custom logic here...
        return User::firstOrCreate(
            ['email' => $socialiteUser->getEmail()],
            [
                'name' => $socialiteUser->getName(),
                'password' => bcrypt(str()->random(16)),
                'email_verified_at' => now(),
                'provider' => $provider,
                'custom_field' => 'custom_value',
            ]
        );
    }
}

// If createRestaurantUser() doesn't exist, KeyChain does this automatically:
protected function dynamicCreateUser(string $guardName, $socialiteUser)
{
    $guardConfig = $this->getGuardConfig($guardName); // Gets restaurant config
    $modelClass = $guardConfig['model']; // App\Models\Restaurant::class

    return $modelClass::create([
        'name' => $socialiteUser->getName(),
        'email' => $socialiteUser->getEmail(),
        'email_verified_at' => now(),
        'password' => bcrypt(str()->random(12)),
        // Maps other fields from config
    ]);
}

// config/keychain.php
'advanced' => [
    'dynamic_methods' => true, // Enable magic methods
    'method_prefixes' => ['create', 'update'], // Supported prefixes
],

// config/keychain.php
'advanced' => [
    'debug_mode' => true, // Enable debug logging
],

// config/keychain.php
'tokens' => [
    'store_tokens' => true,          // Enable token storage
    'cleanup_expired' => true,       // Auto-cleanup expired tokens
    'cleanup_days' => 30,           // Delete expired tokens after X days

    'expiration' => [
        'infinite' => false,         // Set to true for tokens that never expire
        'default_expiry' => 3600,    // Default expiry in seconds (1 hour)

        // Custom expiry per provider
        'custom_expiry' => [
            'google' => 7200,        // 2 hours for Google tokens
            'github' => 86400,       // 24 hours for GitHub tokens
            'facebook' => 3600,      // 1 hour for Facebook tokens
        ],
    ],
],

// config/keychain.php
'tokens' => [
    'expiration' => [
        'infinite' => true,          // 🔄 Tokens never expire
    ],
],

class SocialAuthController extends BaseController
{
    /**
     * Custom token expiration logic
     */
    protected function determineTokenExpiration(string $provider, ?int $providerExpiresIn): ?\DateTime
    {
        // Different expiration rules per provider
        return match ($provider) {
            'google' => now()->addHours(2),      // 2 hours for Google
            'github' => now()->addDays(7),       // 7 days for GitHub
            'facebook' => now()->addHour(),      // 1 hour for Facebook
            'admin_provider' => null,            // Infinite for admin provider
            default => now()->addHours(1),      // 1 hour for others
        };
    }
}

class User extends Authenticatable
{
    use HasSocialAccounts;

    // ... model code
}

// Usage examples:
$user = User::find(1);

// Get all social accounts
$accounts = $user->socialAccounts;

// Get specific provider account
$googleAccount = $user->getSocialAccount('google');

// Check if user has provider
if ($user->hasSocialAccount('github')) {
    echo "User has GitHub account";
}

// Check if token is still valid
if ($user->hasValidSocialToken('google')) {
    echo "Google token is still valid";
}

// Get connected providers
$providers = $user->getConnectedProviders(); // ['google', 'github']

// Link new social account manually
$user->linkSocialAccount(
    'twitter',
    'twitter_user_id_123',
    'access_token_here',
    now()->addDays(30), // expires in 30 days
    ['name' => 'John Doe', 'avatar' => 'avatar_url']
);

// Unlink social account
$user->unlinkSocialAccount('facebook');

$token = KeyChainToken::find(1);

// Check token validity
$token->isValid();      // true if not expired
$token->isExpired();    // true if expired

// Get cached user data
$token->getName();      // User's name from provider
$token->getEmail();     // User's email from provider
$token->getAvatar();    // User's avatar URL from provider
$token->getNickname();  // User's nickname from provider

// Get the associated user/model
$user = $token->tokenable; // Returns User, Restaurant, Admin, etc.

use Danyseifeddine\Keychain\Http\Helpers\KeyChainHelper;

// Get all enabled providers
$providers = KeyChainHelper::getEnabledProviders();
// Returns: ['google', 'github', 'facebook']

// Check if a provider is enabled and configured
if (KeyChainHelper::isProviderReady('google')) {
    echo "Google OAuth is ready to use";
}

// Get status of all providers
$statuses = KeyChainHelper::getProviderStatuses();
/*
Returns:
[
    'google' => [
        'enabled' => true,
        'configured' => true,
        'name' => 'Google'
    ],
    'github' => [
        'enabled' => true,
        'configured' => false,  // Missing credentials
        'name' => 'Github'
    ]
]
*/

// Get all enabled guards
$guards = KeyChainHelper::getEnabledGuards();
/*
Returns:
[
    'web' => [
        'enabled' => true,
        'model' => 'App\Models\User',
        'table' => 'users',
        'name' => 'User',
        // ... other config
    ],
    'restaurant' => [
        'enabled' => true,
        'model' => 'App\Models\Restaurant',
        // ... other config
    ]
]
*/

// Get configuration for specific guard
$guardConfig = KeyChainHelper::getGuardConfig('restaurant');

// Check if guard is enabled
if (KeyChainHelper::isGuardEnabled('admin')) {
    echo "Admin guard is enabled";
}

// Get currently authenticated guard
$currentGuard = KeyChainHelper::getCurrentAuthenticatedGuard();
// Returns: 'web', 'restaurant', 'admin', or null

// Get currently authenticated user from any guard
$currentUser = KeyChainHelper::getCurrentAuthenticatedUser();
// Returns: User, Restaurant, Admin model, or null

// Get social authentication URLs for specific guard
$urls = KeyChainHelper::getSocialAuthUrls('restaurant');
/*
Returns:
[
    'google' => 'https://yourapp.com/auth/restaurant/google/redirect',
    'github' => 'https://yourapp.com/auth/restaurant/github/redirect'
]
*/

// Get URLs for default guard
$defaultUrls = KeyChainHelper::getSocialAuthUrls();

// Get URL for specific provider and guard
$googleRestaurantUrl = route('keychain.restaurant.redirect', 'google');

// Get user's social accounts (current authenticated user)
$accounts = KeyChainHelper::getUserSocialAccounts();

// Get specific user's accounts for specific guard
$accounts = KeyChainHelper::getUserSocialAccounts(123, 'restaurant');
/*
Returns:
[
    [
        'provider' => 'google',
        'name' => 'John Doe',
        'email' => '[email protected]',
        'avatar' => 'https://avatar-url.com',
        'connected_at' => Carbon instance,
        'is_valid' => true,
        'expires_at' => Carbon instance or null
    ],
    // ... more accounts
]
*/

// Check if user has any social accounts
if (KeyChainHelper::hasSocialAccounts(123, 'web')) {
    echo "User has connected social accounts";
}

// Clean up expired tokens (returns number of deleted tokens)
$cleanedUp = KeyChainHelper::cleanupExpiredTokens();
echo "Cleaned up {$cleanedUp} expired tokens";

// Get comprehensive authentication statistics
$stats = KeyChainHelper::getAuthStatistics();
/*
Returns:
[
    'total_tokens' => 150,
    'valid_tokens' => 120,
    'expired_tokens' => 30,
    'providers' => [
        'google' => 80,
        'github' => 45,
        'facebook' => 25
    ],
    'guards' => [
        'web' => 100,
        'restaurant' => 35,
        'admin' => 15
    ],
    'recent_activity' => [
        'last_7_days' => 25,
        'last_30_days' => 60
    ]
]
*/

class DashboardController extends Controller
{
    public function index()
    {
        $user = KeyChainHelper::getCurrentAuthenticatedUser();
        $guard = KeyChainHelper::getCurrentAuthenticatedGuard();
        $socialAccounts = KeyChainHelper::getUserSocialAccounts();
        $stats = KeyChainHelper::getAuthStatistics();

        return view('dashboard', compact('user', 'guard', 'socialAccounts', 'stats'));
    }

    public function socialStats()
    {
        // Admin-only social authentication statistics
        return response()->json([
            'statistics' => KeyChainHelper::getAuthStatistics(),
            'provider_statuses' => KeyChainHelper::getProviderStatuses(),
            'enabled_guards' => KeyChainHelper::getEnabledGuards(),
        ]);
    }
}

// app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
    // Clean up expired tokens daily at 2 AM
    $schedule->command('keychain cleanup --force')
             ->daily()
             ->at('02:00')
             ->withoutOverlapping();

    // Generate weekly statistics
    $schedule->command('keychain stats')
             ->weeklyOn(1, '09:00') // Mondays at 9 AM
             ->appendOutputTo(storage_path('logs/keychain-stats.log'));
}


// config/keychain.php

return [
    /*
    |--------------------------------------------------------------------------
    | Authentication Guards Configuration
    |--------------------------------------------------------------------------
    */
    'guards' => [
        'enabled' => true,                      // Enable multi-guard functionality
        'default_guard' => 'web',               // Default guard when none specified

        'available_guards' => [
            'web' => [
                'enabled' => true,
                'model' => App\Models\User::class,
                'table' => 'users',
                'name' => 'User',
                'redirect_after_auth' => '/dashboard',
                'middleware' => ['web'],
            ],
            // Add more guards as needed...
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Social Provider Services Configuration
    |--------------------------------------------------------------------------
    */
    'services' => [
        'google' => [
            'client_id' => env('KEYCHAIN_GOOGLE_CLIENT_ID'),
            'client_secret' => env('KEYCHAIN_GOOGLE_CLIENT_SECRET'),
            'redirect' => env('KEYCHAIN_GOOGLE_REDIRECT_URL', env('APP_URL') . '/auth/google/callback'),
        ],
        // Add more providers...
    ],

    /*
    |--------------------------------------------------------------------------
    | Enabled Social Providers
    |--------------------------------------------------------------------------
    */
    'providers' => [
        'google' => true,                       // Enable Google OAuth
        'github' => false,                      // Disable GitHub OAuth
        'facebook' => false,                    // Disable Facebook OAuth
        'twitter' => false,
        'linkedin' => false,
        'bitbucket' => false,
        'gitlab' => false,
    ],

    /*
    |--------------------------------------------------------------------------
    | Route Configuration
    |--------------------------------------------------------------------------
    */
    'routes' => [
        'enabled' => true,                      // Enable automatic route registration
        'prefix' => 'auth',                     // URL prefix (/auth/google/redirect)
        'middleware' => ['web'],                // Default middleware stack
        'guard_parameter' => 'guard',           // Route parameter for guard detection
    ],

    /*
    |--------------------------------------------------------------------------
    | User Management Configuration
    |--------------------------------------------------------------------------
    */
    'user' => [
        'auto_create' => true,                  // Auto-create users on first login
        'auto_link' => true,                    // Link accounts to existing users by email
        'update_on_login' => true,              // Update user data on each login

        'fillable_fields' => [
            'name' => 'name',                   // Map provider 'name' to model 'name'
            'email' => 'email',                 // Map provider 'email' to model 'email'
            'avatar' => 'avatar_url',           // Map provider 'avatar' to model 'avatar_url'
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Token Management Configuration
    |--------------------------------------------------------------------------
    */
    'tokens' => [
        'store_tokens' => true,                 // Store OAuth tokens in database
        'cleanup_expired' => true,              // Auto-cleanup expired tokens
        'cleanup_days' => 30,                   // Delete expired tokens after X days

        'expiration' => [
            'infinite' => false,                // Set true for tokens that never expire
            'default_expiry' => 3600,           // Default expiry in seconds (1 hour)

            // Custom expiry per provider (in seconds)
            'custom_expiry' => [
                'google' => 7200,               // 2 hours for Google
                'github' => 86400,              // 24 hours for GitHub
                'facebook' => 3600,             // 1 hour for Facebook
            ],
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Redirect Configuration
    |--------------------------------------------------------------------------
    */
    'redirects' => [
        'success' => '/dashboard',              // Default success redirect
        'failure' => '/login',                  // Default failure redirect
    ],

    /*
    |--------------------------------------------------------------------------
    | Advanced Configuration
    |--------------------------------------------------------------------------
    */
    'advanced' => [
        'debug_mode' => false,                  // Enable debug logging (NOT for production)
        'enable_custom_callbacks' => false,     // Enable complete callback override
        'dynamic_methods' => true,              // Enable magic method generation
        'method_prefixes' => ['create', 'update'], // Supported magic method prefixes
    ],
];

// config/keychain.php
'services' => [
    'discord' => [
        'client_id' => env('KEYCHAIN_DISCORD_CLIENT_ID'),
        'client_secret' => env('KEYCHAIN_DISCORD_CLIENT_SECRET'),
        'redirect' => env('KEYCHAIN_DISCORD_REDIRECT_URL', env('APP_URL') . '/auth/discord/callback'),
    ],
],

'providers' => [
    'discord' => true, // Enable Discord
],

// config/services.php or in a service provider
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
    $event->extendSocialite('discord', \SocialiteProviders\Discord\Provider::class);
});

// config/keychain.php
'routes' => [
    'middleware' => ['web', 'throttle:social-auth'],
],

// app/Http/Kernel.php
protected $middlewareGroups = [
    'web' => [
        // ... existing middleware
    ],
];

protected $namedMiddleware = [
    'throttle:social-auth' => \Illuminate\Routing\Middleware\ThrottleRequests::class . ':10,1', // 10 requests per minute
];

// config/keychain.php
'tokens' => [
    'cleanup_expired' => true,          // Auto-cleanup improves performance
    'cleanup_days' => 7,                // Shorter retention for better performance
],

'advanced' => [
    'debug_mode' => false,              // Always false in production
],

// config/keychain.php
'user' => [
    'auto_link' => false,               // Disable for higher security
    'update_on_login' => false,         // Prevent data overwriting
],

'tokens' => [
    'expiration' => [
        'infinite' => false,            // Always use token expiration
        'default_expiry' => 1800,       // 30 minutes for sensitive apps
    ],
},

// config/keychain.php
return [
    'guards' => [
        'enabled' => true,
        'available_guards' => [
            'web' => [
                'enabled' => true,
                'model' => App\Models\User::class,
                'table' => 'users',
                'name' => 'Customer',
                'redirect_after_auth' => '/customer/dashboard',
            ],
            'restaurant' => [
                'enabled' => true,
                'model' => App\Models\Restaurant::class,
                'table' => 'restaurants',
                'name' => 'Restaurant Owner',
                'redirect_after_auth' => '/restaurant/dashboard',
            ],
        ],
    ],
    'providers' => [
        'google' => true,
        'github' => true,
    ],
];

// app/Models/Restaurant.php


namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Danyseifeddine\Keychain\Traits\HasSocialAccounts;

class Restaurant extends Authenticatable
{
    use HasSocialAccounts;

    protected $fillable = [
        'name', 'email', 'password', 'website', 'description',
        'cuisine_type', 'phone', 'address', 'status'
    ];

    protected $hidden = ['password'];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'last_login_at' => 'datetime',
    ];
}

// app/Models/User.php (Customer)


namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Danyseifeddine\Keychain\Traits\HasSocialAccounts;

class User extends Authenticatable
{
    use HasSocialAccounts;

    protected $fillable = [
        'name', 'email', 'password', 'phone', 'avatar_url'
    ];
}

// app/Http/Controllers/Auth/SocialAuthController.php


namespace App\Http\Controllers\Auth;

use Danyseifeddine\Keychain\Http\Controllers\SocialAuthController as BaseController;
use App\Models\User;
use App\Models\Restaurant;
use Illuminate\Support\Facades\Mail;
use App\Mail\RestaurantWelcomeEmail;

class SocialAuthController extends BaseController
{
    /**
     * Custom customer (web guard) creation
     */
    protected function createWebUser($socialiteUser)
    {
        $user = User::create([
            'name' => $socialiteUser->getName(),
            'email' => $socialiteUser->getEmail(),
            'email_verified_at' => now(),
            'password' => bcrypt(str()->random(12)),
            'avatar_url' => $socialiteUser->getAvatar(),
        ]);

        // Send welcome email
        Mail::to($user)->send(new CustomerWelcomeEmail($user));

        return $user;
    }

    /**
     * Custom restaurant creation with business logic
     */
    protected function createRestaurantUser($socialiteUser)
    {
        $restaurant = Restaurant::create([
            'name' => $this->generateRestaurantName($socialiteUser),
            'email' => $socialiteUser->getEmail(),
            'email_verified_at' => now(),
            'password' => bcrypt(str()->random(12)),
            'status' => 'pending_approval', // Requires manual approval
            'cuisine_type' => 'not_specified',
            'description' => 'Restaurant registered via social authentication.',
        ]);

        // Send welcome email with setup instructions
        Mail::to($restaurant)->send(new RestaurantWelcomeEmail($restaurant));

        // Notify admins of new restaurant registration
        $this->notifyAdminsOfNewRestaurant($restaurant);

        return $restaurant;
    }

    /**
     * Update restaurant data on login
     */
    protected function updateRestaurantUser($restaurant, $socialiteUser)
    {
        $restaurant->update([
            'last_login_at' => now(),
            'social_avatar' => $socialiteUser->getAvatar(),
        ]);

        // Log the login for security
        activity()
            ->causedBy($restaurant)
            ->log('Restaurant logged in via ' . session('keychain_provider', 'social'));

        return $restaurant;
    }

    /**
     * Custom hooks
     */
    protected function afterAuthentication(string $provider, $socialiteUser, $token, $user): void
    {
        // Track authentication analytics
        AnalyticsService::track('social_auth', [
            'provider' => $provider,
            'guard' => session('keychain_guard'),
            'user_type' => get_class($user),
            'new_user' => $user->wasRecentlyCreated,
        ]);

        // Update user's last login
        $user->update(['last_login_at' => now()]);
    }

    /**
     * Generate a restaurant name from social data
     */
    private function generateRestaurantName($socialiteUser): string
    {
        $baseName = $socialiteUser->getName() ?? 'Restaurant';
        $suffix = Restaurant::where('name', 'like', $baseName . '%')->count() + 1;

        return $suffix > 1 ? "{$baseName} #{$suffix}" : $baseName;
    }

    /**
     * Notify admins of new restaurant registration
     */
    private function notifyAdminsOfNewRestaurant(Restaurant $restaurant): void
    {
        $admins = User::where('role', 'admin')->get();

        foreach ($admins as $admin) {
            Mail::to($admin)->send(new NewRestaurantNotification($restaurant));
        }
    }
}

// routes/web.php

use App\Http\Controllers\RestaurantController;
use App\Http\Controllers\CustomerController;
use Danyseifeddine\Keychain\Http\Helpers\KeyChainHelper;

// Multi-guard login page
Route::get('/login', function () {
    return view('auth.multi-login');
})->name('login');

// Customer routes (web guard)
Route::middleware(['auth:web'])->prefix('customer')->group(function () {
    Route::get('/dashboard', [CustomerController::class, 'dashboard'])->name('customer.dashboard');
    Route::get('/profile', [CustomerController::class, 'profile'])->name('customer.profile');
});

// Restaurant routes (restaurant guard)
Route::middleware(['auth:restaurant'])->prefix('restaurant')->group(function () {
    Route::get('/dashboard', [RestaurantController::class, 'dashboard'])->name('restaurant.dashboard');
    Route::get('/menu', [RestaurantController::class, 'menu'])->name('restaurant.menu');
    Route::get('/orders', [RestaurantController::class, 'orders'])->name('restaurant.orders');
    Route::get('/profile', [RestaurantController::class, 'profile'])->name('restaurant.profile');
});

// API endpoints for social account management
Route::middleware(['auth:web,restaurant'])->prefix('api')->group(function () {
    Route::get('/social-accounts', function () {
        return response()->json([
            'accounts' => KeyChainHelper::getUserSocialAccounts(),
            'guard' => KeyChainHelper::getCurrentAuthenticatedGuard(),
        ]);
    });

    Route::delete('/social-accounts/{provider}', function ($provider) {
        $user = KeyChainHelper::getCurrentAuthenticatedUser();
        $user->unlinkSocialAccount($provider);

        return response()->json(['message' => 'Account unlinked successfully']);
    });
});

// config/keychain.php
'routes' => [
    'enabled' => true, // Must be true
],

// Enable debug mode in config/keychain.php
'advanced' => [
    'debug_mode' => true,
],

// Check logs at storage/logs/laravel.log for:
// "KeyChain Debug - Guard Detection"
// "KeyChain Debug - Method Call"

// config/keychain.php
'tokens' => [
    'expiration' => [
        'infinite' => false, // Check this setting
        'default_expiry' => 3600, // In seconds
    ],
],

// Debug token creation
'advanced' => [
    'debug_mode' => true, // Check logs for expiration details
],

// config/keychain.php
'advanced' => [
    'dynamic_methods' => true, // Must be true
],

'guards' => [
    'enabled' => true, // Must be true for multi-guard
],

// In your published controller, add debugging:
public function __call($method, $parameters)
{
    \Log::info("Magic method called: {$method}", $parameters);
    return parent::__call($method, $parameters);
}

// config/keychain.php
'tokens' => [
    'cleanup_expired' => true,
    'cleanup_days' => 7, // Shorter retention
],

'advanced' => [
    'debug_mode' => false, // Always false in production
],
bash
# Publish everything (recommended for first time)
php artisan vendor:publish --provider="Danyseifeddine\Keychain\KeyChainServiceProvider"

# Or publish specific assets
php artisan vendor:publish --tag=keychain-config
php artisan vendor:publish --tag=keychain-migrations
php artisan vendor:publish --tag=keychain-controller
bash
php artisan migrate
blade
{{-- resources/views/auth/login.blade.php --}}
<div class="social-login">
    <h3>Login with Social Media</h3>

    @if(config('keychain.providers.google'))
        <a href="{{ route('keychain.redirect', 'google') }}" class="btn btn-primary">
            Continue with Google
        </a>
    @endif

    @if(config('keychain.providers.github'))
        <a href="{{ route('keychain.redirect', 'github') }}" class="btn btn-dark">
            Continue with GitHub
        </a>
    @endif

    @if(config('keychain.providers.facebook'))
        <a href="{{ route('keychain.redirect', 'facebook') }}" class="btn btn-primary">
            Continue with Facebook
        </a>
    @endif
</div>
blade
{{-- resources/views/auth/multi-login.blade.php --}}
<div class="multi-guard-login">
    <!-- User Login -->
    <div class="guard-section">
        <h4>Login as User</h4>
        <a href="{{ route('keychain.web.redirect', 'google') }}" class="btn btn-primary">
            Google (User)
        </a>
        <a href="{{ route('keychain.web.redirect', 'github') }}" class="btn btn-dark">
            GitHub (User)
        </a>
    </div>

    <!-- Restaurant Login -->
    <div class="guard-section">
        <h4>Login as Restaurant</h4>
        <a href="{{ route('keychain.restaurant.redirect', 'google') }}" class="btn btn-success">
            Google (Restaurant)
        </a>
        <a href="{{ route('keychain.restaurant.redirect', 'github') }}" class="btn btn-info">
            GitHub (Restaurant)
        </a>
    </div>

    <!-- Admin Login -->
    <div class="guard-section">
        <h4>Login as Admin</h4>
        <a href="{{ route('keychain.admin.redirect', 'google') }}" class="btn btn-warning">
            Google (Admin)
        </a>
        <a href="{{ route('keychain.admin.redirect', 'github') }}" class="btn btn-secondary">
            GitHub (Admin)
        </a>
    </div>
</div>
bash
# Publish configuration file
php artisan vendor:publish --tag=keychain-config

# Publish database migrations
php artisan vendor:publish --tag=keychain-migrations

# Publish customizable controller
php artisan vendor:publish --tag=keychain-controller

# Publish environment configuration stub
php artisan vendor:publish --tag=keychain-env

# Publish everything
php artisan vendor:publish --provider="Danyseifeddine\Keychain\KeyChainServiceProvider"
bash
php artisan vendor:publish --tag=keychain-controller
blade
{{-- Get current authenticated user across all guards --}}
@php
    $currentUser = KeyChainHelper::getCurrentAuthenticatedUser();
    $currentGuard = KeyChainHelper::getCurrentAuthenticatedGuard();
@endphp

@if($currentUser)
    <p>Welcome {{ $currentUser->name }} ({{ $currentGuard }} guard)</p>

    {{-- Show social accounts --}}
    @php
        $socialAccounts = KeyChainHelper::getUserSocialAccounts();
    @endphp

    @if($socialAccounts)
        <h5>Connected Accounts:</h5>
        @foreach($socialAccounts as $account)
            <div class="social-account">
                <img src="{{ $account['avatar'] }}" width="32" height="32">
                {{ ucfirst($account['provider']) }} - {{ $account['name'] }}
                @if(!$account['is_valid'])
                    <span class="badge badge-warning">Expired</span>
                @endif
            </div>
        @endforeach
    @endif
@endif

{{-- Show available providers --}}
@php
    $providerStatuses = KeyChainHelper::getProviderStatuses();
@endphp

<div class="available-providers">
    @foreach($providerStatuses as $provider => $status)
        @if($status['enabled'] && $status['configured'])
            <a href="{{ route('keychain.redirect', $provider) }}" class="btn btn-{{ $provider }}">
                Login with {{ $status['name'] }}
            </a>
        @endif
    @endforeach
</div>
bash
# Clear route cache
php artisan route:clear

# Clear config cache
php artisan config:clear

# Verify routes are registered
php artisan route:list --name=keychain
bash
# Check if controller exists
ls app/Http/Controllers/Auth/SocialAuthController.php

# Clear route cache to pick up new controller
php artisan route:clear

# Verify routes are using published controller
php artisan route:list --name=keychain
# Should show: Auth\SocialAuthController instead of Keychain\SocialAuthController
bash
# Re-run migrations
php artisan migrate:refresh

# Check if table exists
php artisan tinker
>>> Schema::hasTable('key_chain_tokens')

# Verify model relationships
>>> $user = User::first();
>>> $user->socialAccounts
bash
# Clean up expired tokens
php artisan keychain cleanup --force

# Optimize configuration
php artisan config:cache

# Check token count
php artisan keychain stats