From e66cea1d1245e4d8c56dbe80cb347aded00bd381 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Fri, 4 Sep 2026 15:31:13 +0700 Subject: [PATCH] feat: implement Google OAuth authentication; add GoogleAuthController, update user model, and configure services --- .agents/skills/socialite-development/SKILL.md | 80 ++++ .env.example | 4 + AGENTS.md | 1 + .../Controllers/Auth/GoogleAuthController.php | 56 +++ app/Models/User.php | 4 +- boost.json | 1 + composer.json | 1 + composer.lock | 416 +++++++++++++++++- config/services.php | 6 + ...ber_token_and_google_id_to_users_table.php | 29 ++ resources/js/layouts/auth-layout.tsx | 3 + resources/js/pages/auth/login.tsx | 43 +- routes/web.php | 6 + 13 files changed, 627 insertions(+), 23 deletions(-) create mode 100644 .agents/skills/socialite-development/SKILL.md create mode 100644 app/Http/Controllers/Auth/GoogleAuthController.php create mode 100644 database/migrations/2026_09_04_082441_add_remember_token_and_google_id_to_users_table.php diff --git a/.agents/skills/socialite-development/SKILL.md b/.agents/skills/socialite-development/SKILL.md new file mode 100644 index 0000000..562e417 --- /dev/null +++ b/.agents/skills/socialite-development/SKILL.md @@ -0,0 +1,80 @@ +--- +name: socialite-development +description: "Manages OAuth social authentication with Laravel Socialite. Activate when adding social login providers; configuring OAuth redirect/callback flows; retrieving authenticated user details; customizing scopes or parameters; setting up community providers; testing with Socialite fakes; or when the user mentions social login, OAuth, Socialite, or third-party authentication." +license: MIT +metadata: + author: laravel +--- + +# Socialite Authentication + +## Documentation + +Use `search-docs` for detailed Socialite patterns and documentation (installation, configuration, routing, callbacks, testing, scopes, stateless auth). + +## Available Providers + +Built-in: `facebook`, `twitter`, `twitter-oauth-2`, `linkedin`, `linkedin-openid`, `google`, `github`, `gitlab`, `bitbucket`, `slack`, `slack-openid`, `twitch` + +Community: 150+ additional providers at [socialiteproviders.com](https://socialiteproviders.com). For provider-specific setup, use `WebFetch` on `https://socialiteproviders.com/{provider-name}`. + +Configuration key in `config/services.php` must match the driver name exactly — note the hyphenated keys: `twitter-oauth-2`, `linkedin-openid`, `slack-openid`. + +Twitter/X: Use `twitter-oauth-2` (OAuth 2.0) for new projects. The legacy `twitter` driver is OAuth 1.0. Driver names remain unchanged despite the platform rebrand. + +Community providers differ from built-in providers in the following ways: +- Installed via `composer require socialiteproviders/{name}` +- Must register via event listener — NOT auto-discovered like built-in providers +- Use `search-docs` for the registration pattern + +## Adding a Provider + +### 1. Configure the provider + +Add the provider's `client_id`, `client_secret`, and `redirect` to `config/services.php`. The config key must match the driver name exactly. + +### 2. Create redirect and callback routes + +Two routes are needed: one that calls `Socialite::driver('provider')->redirect()` to send the user to the OAuth provider, and one that calls `Socialite::driver('provider')->user()` to receive the callback and retrieve user details. + +### 3. Authenticate and store the user + +In the callback, use `updateOrCreate` to find or create a user record from the provider's response (`id`, `name`, `email`, `token`, `refreshToken`), then call `Auth::login()`. + +### 4. Customize the redirect (optional) + +- `scopes()` — merge additional scopes with the provider's defaults +- `setScopes()` — replace all scopes entirely +- `with()` — pass optional parameters (e.g., `['hd' => 'example.com']` for Google) +- `asBotUser()` — Slack only; generates a bot token (`xoxb-`) instead of a user token (`xoxp-`). Must be called before both `redirect()` and `user()`. Only the `token` property will be hydrated on the user object. +- `stateless()` — for API/SPA contexts where session state is not maintained + +### 5. Verify + +1. Config key matches driver name exactly (check the list above for hyphenated names) +2. `client_id`, `client_secret`, and `redirect` are all present +3. Redirect URL matches what is registered in the provider's OAuth dashboard +4. Callback route handles denied grants (when user declines authorization) + +Use `search-docs` for complete code examples of each step. + +## Additional Features + +Use `search-docs` for usage details on: `enablePKCE()`, `userFromToken($token)`, `userFromTokenAndSecret($token, $secret)` (OAuth 1.0), retrieving user details. + +User object: `getId()`, `getName()`, `getEmail()`, `getAvatar()`, `getNickname()`, `token`, `refreshToken`, `expiresIn`, `approvedScopes` + +## Testing + +Socialite provides `Socialite::fake()` for testing redirects and callbacks. Use `search-docs` for faking redirects, callback user data, custom token properties, and assertion methods. + +## Common Pitfalls + +- Config key must match driver name exactly — hyphenated drivers need hyphenated keys (`linkedin-openid`, `slack-openid`, `twitter-oauth-2`). Mismatch silently fails. +- Every provider needs `client_id`, `client_secret`, and `redirect` in `config/services.php`. Missing any one causes cryptic errors. +- `scopes()` merges with defaults; `setScopes()` replaces all scopes entirely. +- Missing `stateless()` in API/SPA contexts causes `InvalidStateException`. +- Redirect URL in `config/services.php` must exactly match the provider's OAuth dashboard (including trailing slashes and protocol). +- Do not pass `state`, `response_type`, `client_id`, `redirect_uri`, or `scope` via `with()` — these are reserved. +- Community providers require event listener registration via `SocialiteWasCalled`. +- `user()` throws when the user declines authorization. Always handle denied grants. diff --git a/.env.example b/.env.example index 40bb13b..8bebc24 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,10 @@ APP_LOCALE=en APP_FALLBACK_LOCALE=en APP_FAKER_LOCALE=en_US +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI="${APP_URL}/auth/google/callback" + APP_MAINTENANCE_DRIVER=file # APP_MAINTENANCE_STORE=database diff --git a/AGENTS.md b/AGENTS.md index 83606ff..d479249 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,7 @@ ## Foundational Context - laravel/fortify (FORTIFY) - v1 - laravel/framework (LARAVEL) - v13 - laravel/prompts (PROMPTS) - v0 +- laravel/socialite (SOCIALITE) - v5 - laravel/wayfinder (WAYFINDER) - v0 - larastan/larastan (LARASTAN) - v3 - laravel/boost (BOOST) - v2 diff --git a/app/Http/Controllers/Auth/GoogleAuthController.php b/app/Http/Controllers/Auth/GoogleAuthController.php new file mode 100644 index 0000000..9e39338 --- /dev/null +++ b/app/Http/Controllers/Auth/GoogleAuthController.php @@ -0,0 +1,56 @@ +redirect(); + } + + public function callback(Request $request): RedirectResponse + { + $googleUser = Socialite::driver('google')->user(); + + $user = User::where('email', $googleUser->getEmail())->first(); + + if (! $user) { + Inertia::flash('toast', [ + 'type' => 'error', + 'message' => 'Akun dengan email Google tersebut tidak ditemukan. Silakan hubungi administrator.', + ]); + + return Redirect::route('login'); + } + + if (! $user->is_active) { + Inertia::flash('toast', [ + 'type' => 'error', + 'message' => 'Akun Anda tidak aktif. Silakan hubungi administrator.', + ]); + + return Redirect::route('login'); + } + + if ($user->google_id !== $googleUser->getId()) { + $user->update(['google_id' => $googleUser->getId()]); + } + + Auth::login($user, remember: true); + + $request->session()->regenerate(); + + return Redirect::intended(route('dashboard', absolute: false)); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 556897e..54e04a9 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -17,8 +17,8 @@ use Laravel\Passkeys\PasskeyAuthenticatable; use Spatie\Permission\Traits\HasRoles; -#[Hidden(['password'])] -#[Guarded(['id', 'last_login_at'])] +#[Hidden(['password', 'remember_token'])] +#[Guarded(['id', 'last_login_at', 'remember_token'])] #[Appends(['full_name'])] class User extends Authenticatable { diff --git a/boost.json b/boost.json index df9f755..3713f5f 100644 --- a/boost.json +++ b/boost.json @@ -10,6 +10,7 @@ "skills": [ "fortify-development", "laravel-best-practices", + "socialite-development", "wayfinder-development", "pest-testing", "inertia-react-development", diff --git a/composer.json b/composer.json index 63522a7..2be27af 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "laravel/chisel": "^0.1.0", "laravel/fortify": "^1.37.2", "laravel/framework": "^13.17", + "laravel/socialite": "^5.31", "laravel/tinker": "^3.0", "laravel/wayfinder": "^0.1.14", "maatwebsite/excel": "^4.0", diff --git a/composer.lock b/composer.lock index 92d6c30..ad8f677 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": "379c97a9e3b22c91d1427f44e711a975", + "content-hash": "96c16aa645077a07df0727ce7f38e195", "packages": [ { "name": "bacon/bacon-qr-code", @@ -813,6 +813,72 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v7.1.0", + "source": { + "type": "git", + "url": "https://github.com/googleapis/php-jwt.git", + "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/b374a5d1a4f1f67fadc2165cdb284645945e2fc0", + "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpfastcache/phpfastcache": "^9.2", + "phpseclib/phpseclib": "~3.0", + "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", + "phpseclib/phpseclib": "Support PS256 (RSASSA-PSS) signatures" + }, + "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/googleapis/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/googleapis/php-jwt/issues", + "source": "https://github.com/googleapis/php-jwt/tree/v7.1.0" + }, + "time": "2026-06-11T17:54:14+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.4.0", @@ -1966,6 +2032,78 @@ }, "time": "2026-07-21T16:49:22+00:00" }, + { + "name": "laravel/socialite", + "version": "v5.31.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "f721b2cbec327ab820bd6aabea6ab211cfcc9f08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/f721b2cbec327ab820bd6aabea6ab211cfcc9f08", + "reference": "f721b2cbec327ab820bd6aabea6ab211cfcc9f08", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4|^7.0", + "guzzlehttp/guzzle": "^6.0|^7.0|^8.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "league/oauth1-client": "^1.11", + "php": "^8.1", + "phpseclib/phpseclib": "^4.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.18|^5.20|^6.47|^7.55|^8.36|^9.15|^10.8|^11.0", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5|^12.0" + }, + "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": "2026-08-31T13:49:19+00:00" + }, { "name": "laravel/tinker", "version": "v3.0.2", @@ -2475,6 +2613,82 @@ ], "time": "2026-07-09T11:49:27+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.8.1", @@ -3870,6 +4084,126 @@ ], "time": "2025-12-27T19:41:33+00:00" }, + { + "name": "phpseclib/phpseclib", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "bb7b959c8159957edae6f5084ebbac765d310e16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/bb7b959c8159957edae6f5084ebbac765d310e16", + "reference": "bb7b959c8159957edae6f5084ebbac765d310e16", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^2|^3", + "php": ">=8.1", + "symfony/polyfill-php82": "^1.26" + }, + "require-dev": { + "brianium/paratest": "^7.22", + "ext-xml": "*", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpunit/phpunit": "^13", + "squizlabs/php_codesniffer": "^3.7", + "vimeo/psalm": "*" + }, + "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-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": { + "phpseclib4\\": "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" + }, + { + "name": "Jack Worman", + "email": "jack.worman@gmail.com", + "homepage": "https://jackworman.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "https://phpseclib.com/", + "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/4.0.1" + }, + "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": "2026-08-26T12:15:13+00:00" + }, { "name": "phpstan/phpdoc-parser", "version": "2.3.3", @@ -6854,6 +7188,86 @@ ], "time": "2026-04-10T16:19:22+00:00" }, + { + "name": "symfony/polyfill-php82", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php82.git", + "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b", + "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php82\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php82/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T12:45:58+00:00" + }, { "name": "symfony/polyfill-php84", "version": "v1.38.1", diff --git a/config/services.php b/config/services.php index ec226a4..0a2d55b 100644 --- a/config/services.php +++ b/config/services.php @@ -35,4 +35,10 @@ ], ], + 'google' => [ + 'client_id' => env('GOOGLE_CLIENT_ID'), + 'client_secret' => env('GOOGLE_CLIENT_SECRET'), + 'redirect' => env('GOOGLE_REDIRECT_URI'), + ], + ]; diff --git a/database/migrations/2026_09_04_082441_add_remember_token_and_google_id_to_users_table.php b/database/migrations/2026_09_04_082441_add_remember_token_and_google_id_to_users_table.php new file mode 100644 index 0000000..19c8c41 --- /dev/null +++ b/database/migrations/2026_09_04_082441_add_remember_token_and_google_id_to_users_table.php @@ -0,0 +1,29 @@ +rememberToken(); + $table->string('google_id')->nullable()->unique()->after('email'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn(['remember_token', 'google_id']); + }); + } +}; diff --git a/resources/js/layouts/auth-layout.tsx b/resources/js/layouts/auth-layout.tsx index 94d22db..baa6641 100644 --- a/resources/js/layouts/auth-layout.tsx +++ b/resources/js/layouts/auth-layout.tsx @@ -1,3 +1,4 @@ +import { useFlashToast } from '@/hooks/use-flash-toast'; import AuthLayoutTemplate from '@/layouts/auth/auth-split-layout'; export default function AuthLayout({ @@ -9,6 +10,8 @@ export default function AuthLayout({ description?: string; children: React.ReactNode; }) { + useFlashToast(); + return ( {children} diff --git a/resources/js/pages/auth/login.tsx b/resources/js/pages/auth/login.tsx index 5c40115..379387c 100644 --- a/resources/js/pages/auth/login.tsx +++ b/resources/js/pages/auth/login.tsx @@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Separator } from '@/components/ui/separator'; import { Spinner } from '@/components/ui/spinner'; +import google from '@/routes/auth/google'; import { store } from '@/routes/login'; import { request } from '@/routes/password'; @@ -105,29 +106,31 @@ export default function Login({ status, canResetPassword }: Props) { diff --git a/routes/web.php b/routes/web.php index 08b8303..cb2d8a1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,11 +1,17 @@ name('home'); +Route::middleware('guest')->prefix('auth/google')->name('auth.google.')->group(function () { + Route::get('redirect', [GoogleAuthController::class, 'redirect'])->name('redirect'); + Route::get('callback', [GoogleAuthController::class, 'callback'])->name('callback'); +}); + Route::middleware(['auth', 'verified'])->group(function () { Route::inertia('dashboard', 'dashboard')->name('dashboard');