feat: implement Google OAuth authentication; add GoogleAuthController, update user model, and configure services
This commit is contained in:
parent
b99876fb4c
commit
e66cea1d12
80
.agents/skills/socialite-development/SKILL.md
Normal file
80
.agents/skills/socialite-development/SKILL.md
Normal file
@ -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.
|
||||||
@ -8,6 +8,10 @@ APP_LOCALE=en
|
|||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=en
|
||||||
APP_FAKER_LOCALE=en_US
|
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_DRIVER=file
|
||||||
# APP_MAINTENANCE_STORE=database
|
# APP_MAINTENANCE_STORE=database
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ ## Foundational Context
|
|||||||
- laravel/fortify (FORTIFY) - v1
|
- laravel/fortify (FORTIFY) - v1
|
||||||
- laravel/framework (LARAVEL) - v13
|
- laravel/framework (LARAVEL) - v13
|
||||||
- laravel/prompts (PROMPTS) - v0
|
- laravel/prompts (PROMPTS) - v0
|
||||||
|
- laravel/socialite (SOCIALITE) - v5
|
||||||
- laravel/wayfinder (WAYFINDER) - v0
|
- laravel/wayfinder (WAYFINDER) - v0
|
||||||
- larastan/larastan (LARASTAN) - v3
|
- larastan/larastan (LARASTAN) - v3
|
||||||
- laravel/boost (BOOST) - v2
|
- laravel/boost (BOOST) - v2
|
||||||
|
|||||||
56
app/Http/Controllers/Auth/GoogleAuthController.php
Normal file
56
app/Http/Controllers/Auth/GoogleAuthController.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Redirect;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse as SymfonyRedirectResponse;
|
||||||
|
|
||||||
|
class GoogleAuthController extends Controller
|
||||||
|
{
|
||||||
|
public function redirect(): SymfonyRedirectResponse
|
||||||
|
{
|
||||||
|
return Socialite::driver('google')->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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,8 +17,8 @@
|
|||||||
use Laravel\Passkeys\PasskeyAuthenticatable;
|
use Laravel\Passkeys\PasskeyAuthenticatable;
|
||||||
use Spatie\Permission\Traits\HasRoles;
|
use Spatie\Permission\Traits\HasRoles;
|
||||||
|
|
||||||
#[Hidden(['password'])]
|
#[Hidden(['password', 'remember_token'])]
|
||||||
#[Guarded(['id', 'last_login_at'])]
|
#[Guarded(['id', 'last_login_at', 'remember_token'])]
|
||||||
#[Appends(['full_name'])]
|
#[Appends(['full_name'])]
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
"skills": [
|
"skills": [
|
||||||
"fortify-development",
|
"fortify-development",
|
||||||
"laravel-best-practices",
|
"laravel-best-practices",
|
||||||
|
"socialite-development",
|
||||||
"wayfinder-development",
|
"wayfinder-development",
|
||||||
"pest-testing",
|
"pest-testing",
|
||||||
"inertia-react-development",
|
"inertia-react-development",
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
"laravel/chisel": "^0.1.0",
|
"laravel/chisel": "^0.1.0",
|
||||||
"laravel/fortify": "^1.37.2",
|
"laravel/fortify": "^1.37.2",
|
||||||
"laravel/framework": "^13.17",
|
"laravel/framework": "^13.17",
|
||||||
|
"laravel/socialite": "^5.31",
|
||||||
"laravel/tinker": "^3.0",
|
"laravel/tinker": "^3.0",
|
||||||
"laravel/wayfinder": "^0.1.14",
|
"laravel/wayfinder": "^0.1.14",
|
||||||
"maatwebsite/excel": "^4.0",
|
"maatwebsite/excel": "^4.0",
|
||||||
|
|||||||
416
composer.lock
generated
416
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "379c97a9e3b22c91d1427f44e711a975",
|
"content-hash": "96c16aa645077a07df0727ce7f38e195",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
@ -813,6 +813,72 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-03-06T22:45:56+00:00"
|
"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",
|
"name": "fruitcake/php-cors",
|
||||||
"version": "v1.4.0",
|
"version": "v1.4.0",
|
||||||
@ -1966,6 +2032,78 @@
|
|||||||
},
|
},
|
||||||
"time": "2026-07-21T16:49:22+00:00"
|
"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",
|
"name": "laravel/tinker",
|
||||||
"version": "v3.0.2",
|
"version": "v3.0.2",
|
||||||
@ -2475,6 +2613,82 @@
|
|||||||
],
|
],
|
||||||
"time": "2026-07-09T11:49:27+00:00"
|
"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",
|
"name": "league/uri",
|
||||||
"version": "7.8.1",
|
"version": "7.8.1",
|
||||||
@ -3870,6 +4084,126 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-12-27T19:41:33+00:00"
|
"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",
|
"name": "phpstan/phpdoc-parser",
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
@ -6854,6 +7188,86 @@
|
|||||||
],
|
],
|
||||||
"time": "2026-04-10T16:19:22+00:00"
|
"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",
|
"name": "symfony/polyfill-php84",
|
||||||
"version": "v1.38.1",
|
"version": "v1.38.1",
|
||||||
|
|||||||
@ -35,4 +35,10 @@
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'google' => [
|
||||||
|
'client_id' => env('GOOGLE_CLIENT_ID'),
|
||||||
|
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
||||||
|
'redirect' => env('GOOGLE_REDIRECT_URI'),
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useFlashToast } from '@/hooks/use-flash-toast';
|
||||||
import AuthLayoutTemplate from '@/layouts/auth/auth-split-layout';
|
import AuthLayoutTemplate from '@/layouts/auth/auth-split-layout';
|
||||||
|
|
||||||
export default function AuthLayout({
|
export default function AuthLayout({
|
||||||
@ -9,6 +10,8 @@ export default function AuthLayout({
|
|||||||
description?: string;
|
description?: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
|
useFlashToast();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthLayoutTemplate title={title} description={description}>
|
<AuthLayoutTemplate title={title} description={description}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
import google from '@/routes/auth/google';
|
||||||
import { store } from '@/routes/login';
|
import { store } from '@/routes/login';
|
||||||
import { request } from '@/routes/password';
|
import { request } from '@/routes/password';
|
||||||
|
|
||||||
@ -105,10 +106,11 @@ export default function Login({ status, canResetPassword }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
asChild
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
|
<a href={google.redirect.url()}>
|
||||||
<svg className="h-4 w-4" viewBox="0 0 24 24">
|
<svg className="h-4 w-4" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
|
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
|
||||||
@ -128,6 +130,7 @@ export default function Login({ status, canResetPassword }: Props) {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Masuk dengan Google
|
Masuk dengan Google
|
||||||
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\Auth\GoogleAuthController;
|
||||||
use App\Http\Controllers\MediaUploadController;
|
use App\Http\Controllers\MediaUploadController;
|
||||||
use App\Http\Controllers\NotificationController;
|
use App\Http\Controllers\NotificationController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::inertia('/', 'welcome')->name('home');
|
Route::inertia('/', 'welcome')->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::middleware(['auth', 'verified'])->group(function () {
|
||||||
Route::inertia('dashboard', 'dashboard')->name('dashboard');
|
Route::inertia('dashboard', 'dashboard')->name('dashboard');
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user