diff --git a/.env.example b/.env.example
index 35db1dd..8f660c7 100644
--- a/.env.example
+++ b/.env.example
@@ -20,6 +20,14 @@ LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
+GOOGLE_CLIENT_ID=test
+GOOGLE_CLIENT_SECRET=test
+GOOGLE_REDIRECT_URL=http://localhost/auth/google/callback
+GITHUB_CLIENT_ID=test
+GITHUB_CLIENT_SECRET=test
+GITHUB_REDIRECT_URL=http://localhost/auth/github/callback
+
+
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
diff --git a/.gitignore b/.gitignore
index 83ec5b7..05243f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@
/.nova
/.phpunit.cache
/.zed
+/.pnpm-store
/auth.json
/node_modules
/public/build
@@ -26,3 +27,4 @@ components.d.ts
resources/js/routes
resources/js/actions
resources/js/wayfinder
+
diff --git a/app/Http/Controllers/SocialProviderController.php b/app/Http/Controllers/SocialProviderController.php
new file mode 100644
index 0000000..8a7ab58
--- /dev/null
+++ b/app/Http/Controllers/SocialProviderController.php
@@ -0,0 +1,116 @@
+redirect();
+ }
+
+ public function handleProviderCallback($provider, Request $request)
+ {
+ $socialUser = Socialite::driver($provider)->user();
+
+ try {
+ // If user is already authenticated, link the social account
+ if (Auth::check()) {
+ return $this->linkSocialAccount(Auth::user(), $provider, $socialUser);
+ }
+
+ // Check if user already exists with this social provider
+ $existingSocialUser = User::where([
+ 'provider' => $provider,
+ 'provider_id' => $socialUser->getId()
+ ])->first();
+
+ if ($existingSocialUser) {
+ // User exists with this social provider, log them in
+ $existingSocialUser->provider_token = $socialUser->token;
+ $existingSocialUser->save();
+
+ Auth::login($existingSocialUser);
+ return redirect()->route('dashboard');
+ }
+
+ // Check if email is already used by another account
+ $existingUser = User::where('email', $socialUser->getEmail())->first();
+
+ if ($existingUser) {
+ // Email exists but with different provider or username/password
+ if ($existingUser->provider && $existingUser->provider !== $provider) {
+ return redirect()->route('login')->withErrors([
+ 'email' => 'Cet email est déjà associé à un compte ' . $existingUser->provider
+ ]);
+ } else if (!$existingUser->provider) {
+ return redirect()->route('login')->withErrors([
+ 'email' => 'Cet email utilise une autre méthode de connexion (nom d\'utilisateur/mot de passe)'
+ ]);
+ }
+ }
+
+ // Create new user account
+ $user = User::create([
+ 'name' => $socialUser->getName(),
+ 'email' => $socialUser->getEmail(),
+ 'provider' => $provider,
+ 'provider_id' => $socialUser->getId(),
+ 'provider_token' => $socialUser->token,
+ 'email_verified_at' => now(),
+ 'profile_photo_path' => $socialUser->getAvatar(),
+ ]);
+
+ Auth::login($user);
+ return redirect()->route('dashboard');
+
+ } catch (\Exception $e) {
+ return redirect()->route('login')->withErrors([
+ 'email' => 'Une erreur est survenue lors de la connexion'
+ ]);
+ }
+ }
+
+ /**
+ * Link a social account to an existing authenticated user
+ */
+ private function linkSocialAccount(User $user, string $provider, $socialUser)
+ {
+ // Check if this social account is already linked to another user
+ $existingSocialUser = User::where([
+ 'provider' => $provider,
+ 'provider_id' => $socialUser->getId()
+ ])->first();
+
+ if ($existingSocialUser && $existingSocialUser->id !== $user->id) {
+ return redirect()->route('user.profile.index')->withErrors([
+ 'social' => 'Ce compte ' . $provider . ' est déjà lié à un autre utilisateur'
+ ]);
+ }
+
+ // Check if user already has this provider linked
+ if ($user->provider === $provider) {
+ return redirect()->route('user.profile.index')->withErrors([
+ 'social' => 'Ce compte ' . $provider . ' est déjà lié à votre profil'
+ ]);
+ }
+
+ // Update user with social provider information
+ $user->update([
+ 'provider' => $provider,
+ 'provider_id' => $socialUser->getId(),
+ 'provider_token' => $socialUser->token,
+ ]);
+
+ return redirect()->route('user.profile.index')->with('success',
+ 'Compte ' . $provider . ' lié avec succès à votre profil'
+ );
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
index 8d3d0a0..0372635 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -23,6 +23,11 @@ class User extends Authenticatable implements MustVerifyEmail
'name',
'email',
'password',
+ 'provider',
+ 'provider_id',
+ 'provider_token',
+ 'email_verified_at',
+ 'profile_photo_path',
];
/**
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 452e6b6..1bdde98 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,6 +2,7 @@
namespace App\Providers;
+use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@@ -19,6 +20,15 @@ public function register(): void
*/
public function boot(): void
{
- //
+ $this->loadAuthRoutes();
+ }
+
+ /**
+ * Load the auth routes.
+ */
+ private function loadAuthRoutes(): void
+ {
+ Route::middleware('web')
+ ->group(base_path('routes/auth.php'));
}
}
diff --git a/artisan b/artisan
old mode 100755
new mode 100644
diff --git a/composer.json b/composer.json
index 1a8ae63..25db0ee 100644
--- a/composer.json
+++ b/composer.json
@@ -13,6 +13,7 @@
"inertiajs/inertia-laravel": "^2.0",
"laravel/fortify": "^1.28",
"laravel/framework": "^12.0",
+ "laravel/socialite": "^5.23",
"laravel/tinker": "^2.10.1",
"laravel/wayfinder": "^0.1.6"
},
diff --git a/composer.lock b/composer.lock
index 695c83d..f6e7802 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "c7d01a894b29be9622b874f2d51920a8",
+ "content-hash": "5ab8c68bd17cbd549fd38d58c15db927",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -613,6 +613,69 @@
],
"time": "2025-03-06T22:45:56+00:00"
},
+ {
+ "name": "firebase/php-jwt",
+ "version": "v6.11.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/firebase/php-jwt.git",
+ "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
+ "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "guzzlehttp/guzzle": "^7.4",
+ "phpspec/prophecy-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.5",
+ "psr/cache": "^2.0||^3.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0"
+ },
+ "suggest": {
+ "ext-sodium": "Support EdDSA (Ed25519) signatures",
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "jwt",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/firebase/php-jwt/issues",
+ "source": "https://github.com/firebase/php-jwt/tree/v6.11.1"
+ },
+ "time": "2025-04-09T20:32:01+00:00"
+ },
{
"name": "fruitcake/php-cors",
"version": "v1.3.0",
@@ -1628,6 +1691,78 @@
},
"time": "2025-03-19T13:51:03+00:00"
},
+ {
+ "name": "laravel/socialite",
+ "version": "v5.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/socialite.git",
+ "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5",
+ "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "firebase/php-jwt": "^6.4",
+ "guzzlehttp/guzzle": "^6.0|^7.0",
+ "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "league/oauth1-client": "^1.11",
+ "php": "^7.2|^8.0",
+ "phpseclib/phpseclib": "^3.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.0",
+ "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0",
+ "phpstan/phpstan": "^1.12.23",
+ "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "aliases": {
+ "Socialite": "Laravel\\Socialite\\Facades\\Socialite"
+ },
+ "providers": [
+ "Laravel\\Socialite\\SocialiteServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Socialite\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "laravel",
+ "oauth"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/socialite/issues",
+ "source": "https://github.com/laravel/socialite"
+ },
+ "time": "2025-07-23T14:16:08+00:00"
+ },
{
"name": "laravel/tinker",
"version": "v2.10.1",
@@ -2133,6 +2268,82 @@
],
"time": "2024-09-21T08:32:55+00:00"
},
+ {
+ "name": "league/oauth1-client",
+ "version": "v1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/oauth1-client.git",
+ "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055",
+ "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-openssl": "*",
+ "guzzlehttp/guzzle": "^6.0|^7.0",
+ "guzzlehttp/psr7": "^1.7|^2.0",
+ "php": ">=7.1||>=8.0"
+ },
+ "require-dev": {
+ "ext-simplexml": "*",
+ "friendsofphp/php-cs-fixer": "^2.17",
+ "mockery/mockery": "^1.3.3",
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5||9.5"
+ },
+ "suggest": {
+ "ext-simplexml": "For decoding XML-based responses."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev",
+ "dev-develop": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\OAuth1\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Corlett",
+ "email": "bencorlett@me.com",
+ "homepage": "http://www.webcomm.com.au",
+ "role": "Developer"
+ }
+ ],
+ "description": "OAuth 1.0 Client Library",
+ "keywords": [
+ "Authentication",
+ "SSO",
+ "authorization",
+ "bitbucket",
+ "identity",
+ "idp",
+ "oauth",
+ "oauth1",
+ "single sign on",
+ "trello",
+ "tumblr",
+ "twitter"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/oauth1-client/issues",
+ "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0"
+ },
+ "time": "2024-12-10T19:59:05+00:00"
+ },
{
"name": "league/uri",
"version": "7.5.1",
@@ -2878,6 +3089,56 @@
},
"time": "2024-05-08T12:36:18+00:00"
},
+ {
+ "name": "paragonie/random_compat",
+ "version": "v9.99.100",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">= 7"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*|5.*",
+ "vimeo/psalm": "^1"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "polyfill",
+ "pseudorandom",
+ "random"
+ ],
+ "support": {
+ "email": "info@paragonie.com",
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "source": "https://github.com/paragonie/random_compat"
+ },
+ "time": "2020-10-15T08:29:30+00:00"
+ },
{
"name": "phpoption/phpoption",
"version": "1.9.3",
@@ -2953,6 +3214,116 @@
],
"time": "2024-07-20T21:41:07+00:00"
},
+ {
+ "name": "phpseclib/phpseclib",
+ "version": "3.0.46",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpseclib/phpseclib.git",
+ "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
+ "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
+ "shasum": ""
+ },
+ "require": {
+ "paragonie/constant_time_encoding": "^1|^2|^3",
+ "paragonie/random_compat": "^1.4|^2.0|^9.99.99",
+ "php": ">=5.6.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*"
+ },
+ "suggest": {
+ "ext-dom": "Install the DOM extension to load XML formatted public keys.",
+ "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
+ "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
+ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "phpseclib/bootstrap.php"
+ ],
+ "psr-4": {
+ "phpseclib3\\": "phpseclib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
+ "role": "Lead Developer"
+ },
+ {
+ "name": "Patrick Monnerat",
+ "email": "pm@datasphere.ch",
+ "role": "Developer"
+ },
+ {
+ "name": "Andreas Fischer",
+ "email": "bantu@phpbb.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Hans-Jürgen Petrich",
+ "email": "petrich@tronic-media.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "graham@alt-three.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
+ "homepage": "http://phpseclib.sourceforge.net",
+ "keywords": [
+ "BigInteger",
+ "aes",
+ "asn.1",
+ "asn1",
+ "blowfish",
+ "crypto",
+ "cryptography",
+ "encryption",
+ "rsa",
+ "security",
+ "sftp",
+ "signature",
+ "signing",
+ "ssh",
+ "twofish",
+ "x.509",
+ "x509"
+ ],
+ "support": {
+ "issues": "https://github.com/phpseclib/phpseclib/issues",
+ "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/terrafrost",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/phpseclib",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-26T16:29:55+00:00"
+ },
{
"name": "pragmarx/google2fa",
"version": "v8.0.3",
diff --git a/config/cors.php b/config/cors.php
new file mode 100644
index 0000000..8a39e6d
--- /dev/null
+++ b/config/cors.php
@@ -0,0 +1,34 @@
+ ['api/*', 'sanctum/csrf-cookie'],
+
+ 'allowed_methods' => ['*'],
+
+ 'allowed_origins' => ['*'],
+
+ 'allowed_origins_patterns' => [],
+
+ 'allowed_headers' => ['*'],
+
+ 'exposed_headers' => [],
+
+ 'max_age' => 0,
+
+ 'supports_credentials' => false,
+
+];
diff --git a/config/services.php b/config/services.php
index 6182e4b..bea264f 100644
--- a/config/services.php
+++ b/config/services.php
@@ -34,5 +34,15 @@
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
],
],
+ 'google' => [
+ 'client_id' => env('GOOGLE_CLIENT_ID'),
+ 'client_secret' => env('GOOGLE_CLIENT_SECRET'),
+ 'redirect' => env('GOOGLE_REDIRECT_URI'),
+ ],
+ 'github' => [
+ 'client_id' => env('GITHUB_CLIENT_ID'),
+ 'client_secret' => env('GITHUB_CLIENT_SECRET'),
+ 'redirect' => env('GITHUB_REDIRECT_URI'),
+ ],
];
diff --git a/database/migrations/2025_08_17_164746_add_social_provider_column.php b/database/migrations/2025_08_17_164746_add_social_provider_column.php
new file mode 100644
index 0000000..8c6b023
--- /dev/null
+++ b/database/migrations/2025_08_17_164746_add_social_provider_column.php
@@ -0,0 +1,34 @@
+string('provider')->nullable();
+ $table->string('provider_id')->nullable();
+ $table->string('provider_token')->nullable();
+ $table->string('password')->nullable()->change();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('provider');
+ $table->dropColumn('provider_id');
+ $table->dropColumn('provider_token');
+ $table->string('password')->change();
+ });
+ }
+};
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..555b243
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,27 @@
+services:
+ laravel.test:
+ build:
+ context: './vendor/laravel/sail/runtimes/8.4'
+ dockerfile: Dockerfile
+ args:
+ WWWGROUP: '${WWWGROUP}'
+ image: 'sail-8.4/app'
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ ports:
+ - '${APP_PORT:-80}:80'
+ - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
+ environment:
+ WWWUSER: '${WWWUSER}'
+ LARAVEL_SAIL: 1
+ XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
+ XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
+ IGNITION_LOCAL_SITES_PATH: '${PWD}'
+ volumes:
+ - '.:/var/www/html'
+ networks:
+ - sail
+ depends_on: { }
+networks:
+ sail:
+ driver: bridge
diff --git a/resources/js/pages/auth/login.vue b/resources/js/pages/auth/login.vue
index 79eb8f9..45e5ea4 100644
--- a/resources/js/pages/auth/login.vue
+++ b/resources/js/pages/auth/login.vue
@@ -1,8 +1,9 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/routes/auth.php b/routes/auth.php
new file mode 100644
index 0000000..9ca3ebd
--- /dev/null
+++ b/routes/auth.php
@@ -0,0 +1,12 @@
+group(function () {
+ Route::get('auth/{provider}/redirect', [SocialProviderController::class, 'redirectToProvider'])->name('auth.provider.redirect');
+});
+
+// Allow both guest and authenticated users to access the callback
+Route::get('auth/{provider}/callback', [SocialProviderController::class, 'handleProviderCallback'])->name('auth.provider.callback');
+