adds social login
This commit is contained in:
@@ -7,6 +7,17 @@ APP_URL=http://localhost
|
||||
ASSET_URL="${APP_URL}"
|
||||
APP_PORT=8000
|
||||
SELF_HOSTED=true
|
||||
REGISTRATION_ENABLED=true
|
||||
|
||||
ENABLED_LOGIN_PROVIDERS=
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
LINKEDIN_CLIENT_ID=
|
||||
LINKEDIN_CLIENT_SECRET=
|
||||
FACEBOOK_CLIENT_ID=
|
||||
FACEBOOK_CLIENT_SECRET=
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Exception;
|
||||
use App\Models\User;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ConnectedAccount;
|
||||
use App\Models\ConnectedAccountVerification;
|
||||
use App\Notifications\VerifyConnectedAccountNotification;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
|
||||
class SocialLoginController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Redirect the user to the GitHub authentication page.
|
||||
*
|
||||
*/
|
||||
public function redirectToProvider(string $provider)
|
||||
{
|
||||
$this->validateProvider($provider);
|
||||
|
||||
return Socialite::driver($provider)->redirect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the user information from GitHub.
|
||||
*
|
||||
*/
|
||||
public function handleProviderCallback(string $provider)
|
||||
{
|
||||
$this->validateProvider($provider);
|
||||
|
||||
$providerUser = Socialite::driver($provider)->user();
|
||||
|
||||
// check if this account is already linked
|
||||
$connected_account = ConnectedAccount::firstOrNew([
|
||||
'provider' => $provider,
|
||||
'provider_id' => $providerUser->id
|
||||
], [
|
||||
'token' => $providerUser->token,
|
||||
'secret' => $providerUser->tokenSecret,
|
||||
'refresh_token' => $providerUser->refreshToken,
|
||||
'expires_at' => $providerUser->expiresIn
|
||||
]);
|
||||
|
||||
// already linked, let's go login
|
||||
if ($connected_account->exists) {
|
||||
Auth::login($connected_account->user);
|
||||
|
||||
return redirect(route('dashboard'));
|
||||
}
|
||||
|
||||
// new user, let's create one
|
||||
if (!$user = User::where('email', $providerUser->email)->first()) {
|
||||
|
||||
$user = User::create([
|
||||
'name' => $providerUser->name,
|
||||
'email' => $providerUser->email,
|
||||
'email_verified_at' => now()
|
||||
]);
|
||||
|
||||
$connected_account->user_id = $user->id;
|
||||
$connected_account->save();
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return redirect(route('dashboard'));
|
||||
}
|
||||
|
||||
// email exists already, send verification link
|
||||
$verification = ConnectedAccountVerification::updateOrCreate([
|
||||
'email' => $providerUser->email,
|
||||
'provider' => $provider,
|
||||
'verified_at' => null
|
||||
], [
|
||||
'provider_id' => $providerUser->id,
|
||||
'connected_account' => $connected_account
|
||||
]);
|
||||
|
||||
$user->notify(new VerifyConnectedAccountNotification($verification->id));
|
||||
|
||||
return redirect(route('login'))
|
||||
->with('status', __('Account already exists. Check your email for a link to login.'));
|
||||
}
|
||||
|
||||
protected function validateProvider($provider): void
|
||||
{
|
||||
if (!in_array($provider, explode(',', config('services.enabled_login_providers')))) {
|
||||
|
||||
throw new Exception('Please provide a valid social provider.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ConnectedAccount;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\ConnectedAccountVerification;
|
||||
|
||||
class VerifyConnectedAccountController extends Controller
|
||||
{
|
||||
|
||||
public function __invoke(string $verification_id)
|
||||
{
|
||||
|
||||
$verification = ConnectedAccountVerification::findOrFail($verification_id);
|
||||
|
||||
if (!$verification->verified_at) {
|
||||
|
||||
// mark request as verified
|
||||
$verification->verified_at = now();
|
||||
$verification->save();
|
||||
|
||||
// mark user as verified
|
||||
$user = User::where('email', $verification->email)->firstOrFail();
|
||||
$user->email_verified_at = now();
|
||||
$user->save();
|
||||
|
||||
// add connected account
|
||||
$user->connectedAccounts()->create([
|
||||
...$verification->connected_account,
|
||||
...[
|
||||
'provider' => $verification->provider,
|
||||
'provider_id' => $verification->provider_id,
|
||||
]
|
||||
]);
|
||||
|
||||
Auth::login($user);
|
||||
}
|
||||
|
||||
return redirect(route('dashboard'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasTimestamps;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ConnectedAccount extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasTimestamps;
|
||||
use HasUuids;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'provider',
|
||||
'provider_id',
|
||||
'token',
|
||||
'secret',
|
||||
'refresh_token',
|
||||
'expires_at',
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
'user'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'created_at' => 'datetime',
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user of the connected account.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ConnectedAccountVerification extends Model
|
||||
{
|
||||
|
||||
use HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'provider',
|
||||
'provider_id',
|
||||
'email',
|
||||
'connected_account'
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'verified_at' => 'datetime',
|
||||
'connected_account' => 'json'
|
||||
];
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -2,8 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
|
||||
use App\Traits\HasConnectedAccounts;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Laravel\Jetstream\HasProfilePhoto;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
@@ -13,8 +12,9 @@ use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
|
||||
class User extends Authenticatable
|
||||
class User extends Authenticatable implements MustVerifyEmail
|
||||
{
|
||||
use HasApiTokens;
|
||||
use HasFactory;
|
||||
@@ -23,6 +23,7 @@ class User extends Authenticatable
|
||||
use TwoFactorAuthenticatable;
|
||||
use HasUuids;
|
||||
use HasRelationships;
|
||||
use HasConnectedAccounts;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\ConnectedAccountVerification;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class VerifyConnectedAccountNotification extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $verification_id
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*/
|
||||
public function toMail(object $notifiable): MailMessage
|
||||
{
|
||||
$verification = ConnectedAccountVerification::find($this->verification_id);
|
||||
$provider = config("services.$verification->provider.name");
|
||||
|
||||
return (new MailMessage)
|
||||
->greeting('Welcome back!')
|
||||
->subject("Connect your $provider account with Investbrain")
|
||||
->line("You recently attempted to log into an existing Investbrain account using $provider. To safeguard your Investbrain account, please confirm this was you by pressing the 'Connect $provider' button below:")
|
||||
->action("Connect $provider", route('verify_connected_account', ['verification_id' => $this->verification_id]))
|
||||
->line('If you do not recognize this activity, we recommend [changing your password]('.route('profile.show').') as soon as possible. Otherwise, you can disregard this message.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Models\ConnectedAccount;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property Collection $connectedAccounts
|
||||
*/
|
||||
trait HasConnectedAccounts
|
||||
{
|
||||
/**
|
||||
* Determine if the user owns the given connected account.
|
||||
*/
|
||||
public function ownsConnectedAccount(mixed $connectedAccount): bool
|
||||
{
|
||||
return $this->id == optional($connectedAccount)->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user has a specific account type.
|
||||
*/
|
||||
public function hasTokenFor(string $provider): bool
|
||||
{
|
||||
return $this->connectedAccounts->contains('provider', Str::lower($provider));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to retrieve the token for a given provider.
|
||||
*/
|
||||
public function getTokenFor(string $provider, mixed $default = null): mixed
|
||||
{
|
||||
if ($this->hasTokenFor($provider)) {
|
||||
return $this->connectedAccounts
|
||||
->where('provider', Str::lower($provider))
|
||||
->first()
|
||||
->token;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find a connected account that belongs to the user,
|
||||
* for the given provider and ID.
|
||||
*/
|
||||
public function getConnectedAccountFor(string $provider, string $id): mixed
|
||||
{
|
||||
return $this->connectedAccounts
|
||||
->where('provider', $provider)
|
||||
->where('provider_id', $id)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the connected accounts belonging to the user.
|
||||
*/
|
||||
public function connectedAccounts(): HasMany
|
||||
{
|
||||
return $this->hasMany(ConnectedAccount::class);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
"laravel/framework": "^11.9",
|
||||
"laravel/jetstream": "^5.1",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/socialite": "^5.16",
|
||||
"laravel/tinker": "^2.9",
|
||||
"livewire/livewire": "^3.5",
|
||||
"livewire/volt": "^1.6",
|
||||
|
||||
Generated
+372
-1
@@ -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": "0f0957a51f038034d44aa347d5b97635",
|
||||
"content-hash": "7fcc707e9612c64b40469d52efdb75e6",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@@ -968,6 +968,69 @@
|
||||
},
|
||||
"time": "2024-09-13T01:29:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"version": "v6.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/firebase/php-jwt.git",
|
||||
"reference": "500501c2ce893c824c801da135d02661199f60c5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5",
|
||||
"reference": "500501c2ce893c824c801da135d02661199f60c5",
|
||||
"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.10.1"
|
||||
},
|
||||
"time": "2024-05-18T18:05:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fruitcake/php-cors",
|
||||
"version": "v1.3.0",
|
||||
@@ -2274,6 +2337,78 @@
|
||||
},
|
||||
"time": "2024-09-23T13:33:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/socialite",
|
||||
"version": "v5.16.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/socialite.git",
|
||||
"reference": "40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/socialite/zipball/40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf",
|
||||
"reference": "40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf",
|
||||
"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",
|
||||
"illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||
"league/oauth1-client": "^1.10.1",
|
||||
"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",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^8.0|^9.3|^10.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Socialite\\SocialiteServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Socialite": "Laravel\\Socialite\\Facades\\Socialite"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": "2024-09-03T09:46:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/tinker",
|
||||
"version": "v2.10.0",
|
||||
@@ -2716,6 +2851,82 @@
|
||||
],
|
||||
"time": "2024-09-21T08:32:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth1-client",
|
||||
"version": "v1.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth1-client.git",
|
||||
"reference": "d6365b901b5c287dd41f143033315e2f777e1167"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167",
|
||||
"reference": "d6365b901b5c287dd41f143033315e2f777e1167",
|
||||
"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.10.1"
|
||||
},
|
||||
"time": "2022-04-15T14:02:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "livewire/livewire",
|
||||
"version": "v3.5.11",
|
||||
@@ -3760,6 +3971,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": "phpoffice/phpspreadsheet",
|
||||
"version": "1.29.2",
|
||||
@@ -3940,6 +4201,116 @@
|
||||
],
|
||||
"time": "2024-07-20T21:41:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.42",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db92f1b1987b12b13f248fe76c3a52cadb67bb98",
|
||||
"reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98",
|
||||
"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.42"
|
||||
},
|
||||
"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": "2024-09-16T03:06:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/google2fa",
|
||||
"version": "v8.0.3",
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ return [
|
||||
*/
|
||||
|
||||
'features' => [
|
||||
Features::registration(),
|
||||
env('REGISTRATION_ENABLED', true) ? Features::registration() : null,
|
||||
Features::resetPasswords(),
|
||||
Features::emailVerification(),
|
||||
Features::updateProfileInformation(),
|
||||
|
||||
@@ -35,4 +35,38 @@ return [
|
||||
],
|
||||
],
|
||||
|
||||
'github' => [
|
||||
'client_id' => env('GITHUB_CLIENT_ID'),
|
||||
'client_secret' => env('GITHUB_CLIENT_SECRET'),
|
||||
'redirect' => '/auth/github/callback',
|
||||
'logo' => 'github-icon',
|
||||
'color' => '#393939',
|
||||
'name' => 'GitHub'
|
||||
],
|
||||
|
||||
'google' => [
|
||||
'client_id' => env('GOOGLE_CLIENT_ID'),
|
||||
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
||||
'redirect' => '/auth/google/callback',
|
||||
'color' => '#4285F4',
|
||||
'name' => 'Google'
|
||||
],
|
||||
|
||||
'facebook' => [
|
||||
'client_id' => env('FACEBOOK_CLIENT_ID'),
|
||||
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
|
||||
'redirect' => '/auth/facebook/callback',
|
||||
'color' => '#0165E1',
|
||||
'name' => 'Facebook'
|
||||
],
|
||||
|
||||
'linkedin-openid' => [
|
||||
'client_id' => env('LINKEDIN_CLIENT_ID'),
|
||||
'client_secret' => env('LINKEDIN_CLIENT_SECRET'),
|
||||
'redirect' => '/auth/linkedin-openid/callback',
|
||||
'color' => '#0a66c2',
|
||||
'name' => 'Linkedin'
|
||||
],
|
||||
|
||||
'enabled_login_providers' => env('ENABLED_LOGIN_PROVIDERS', [])
|
||||
];
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('password')->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::create('connected_accounts', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignIdFor(User::class, 'user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('provider');
|
||||
$table->string('provider_id');
|
||||
$table->string('token', 1000);
|
||||
$table->string('secret')->nullable(); // OAuth1
|
||||
$table->string('refresh_token', 1000)->nullable(); // OAuth2
|
||||
$table->dateTime('expires_at')->nullable(); // OAuth2
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['user_id', 'id']);
|
||||
$table->index(['provider', 'provider_id']);
|
||||
});
|
||||
|
||||
Schema::create('connected_account_verifications', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('email');
|
||||
$table->string('provider');
|
||||
$table->string('provider_id');
|
||||
$table->json('connected_account');
|
||||
$table->timestamps();
|
||||
$table->timestamp('verified_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
|
||||
Schema::dropIfExists('connected_account_verifications');
|
||||
|
||||
Schema::dropIfExists('connected_accounts');
|
||||
}
|
||||
};
|
||||
+3
-1
@@ -51,6 +51,7 @@
|
||||
"Token Name": "Token Name",
|
||||
"Permissions": "Permissions",
|
||||
"Profile Information": "Profile Information",
|
||||
"Account already exists. Check your email for a link to login.": "Account already exists. Check your email for a link to login.",
|
||||
"Update your account\\'s profile information and email address.": "Update your account\\'s profile information and email address.",
|
||||
"Photo": "Photo",
|
||||
"Select A New Photo": "Select A New Photo",
|
||||
@@ -80,7 +81,8 @@
|
||||
"I agree to the :terms_of_service and :privacy_policy": "I agree to the :terms_of_service and :privacy_policy",
|
||||
"Terms of Service": "Terms of Service",
|
||||
"Privacy Policy": "Privacy Notice",
|
||||
"Need to register?": "Need to register?",
|
||||
"Sign up with email": "Sign up with email",
|
||||
"Login with": "Login with",
|
||||
"Already registered?": "Already registered?",
|
||||
"Reset Password": "Reset Password",
|
||||
"Please confirm access to your account by entering the authentication code provided by your authenticator application.": "Please confirm access to your account by entering the authentication code provided by your authenticator application.",
|
||||
|
||||
+3
-1
@@ -51,6 +51,7 @@
|
||||
"Token Name": "Nombre del Token",
|
||||
"Permissions": "Permisos",
|
||||
"Profile Information": "Información del Perfil",
|
||||
"Account already exists. Check your email for a link to login.": "La cuenta ya existe. Revisa tu correo electrónico para un enlace de inicio de sesión.",
|
||||
"Update your account's profile information and email address.": "Actualiza la información de perfil y la dirección de correo electrónico de tu cuenta.",
|
||||
"Photo": "Foto",
|
||||
"Select A New Photo": "Seleccionar una Nueva Foto",
|
||||
@@ -80,7 +81,8 @@
|
||||
"I agree to the :terms_of_service and :privacy_policy": "Acepto los :terms_of_service y la :privacy_policy",
|
||||
"Terms of Service": "Términos de Servicio",
|
||||
"Privacy Policy": "Aviso de Privacidad",
|
||||
"Need to register?": "¿Necesitas registrarte?",
|
||||
"Sign up with email": "Regístrate con correo electrónico",
|
||||
"Login with": "Iniciar sesión con",
|
||||
"Already registered?": "¿Ya estás registrado?",
|
||||
"Reset Password": "Restablecer Contraseña",
|
||||
"Please confirm access to your account by entering the authentication code provided by your authenticator application.": "Por favor, confirma el acceso a tu cuenta ingresando el código de autenticación proporcionado por tu aplicación de autenticación.",
|
||||
|
||||
@@ -47,15 +47,34 @@
|
||||
|
||||
</div>
|
||||
|
||||
<x-section-border />
|
||||
@if (\Laravel\Fortify\Features::enabled('registration'))
|
||||
|
||||
<div class="">
|
||||
<x-section-border />
|
||||
|
||||
<x-button link="{{ route('register') }}" class="btn-sm btn-block btn-outline btn-secondary" >
|
||||
{{ __('Need to register?') }}
|
||||
</x-button>
|
||||
<div class="">
|
||||
|
||||
</div>
|
||||
@foreach(explode(',', config('services.enabled_login_providers')) as $provider)
|
||||
<x-button
|
||||
link="{{ route('oauth.redirect', ['provider' => $provider]) }}"
|
||||
class="btn-sm btn-block my-1"
|
||||
style='background-color: {{ config("services.$provider.color") }}'
|
||||
no-wire-navigate
|
||||
>
|
||||
@include("components.$provider-icon")
|
||||
|
||||
{{ __('Login with') }} {{ config("services.$provider.name") }}
|
||||
</x-button>
|
||||
@endforeach
|
||||
|
||||
<x-button
|
||||
link="{{ route('register') }}"
|
||||
class="btn-sm btn-block btn-outline btn-secondary my-1"
|
||||
>
|
||||
{{ __('Sign up with email') }}
|
||||
</x-button>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
</x-authentication-card>
|
||||
</x-guest-layout>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
<svg viewBox="0 0 90 90" aria-hidden="true" class="size-6 fill-current">
|
||||
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M72,12L18,12C14.685,12 12,14.685 12,18L12,72C12,75.315 14.685,78 18,78L48,78L48,51L39,51L39,42L48,42L48,37.167C48,28.017 52.458,24 60.063,24C63.705,24 65.631,24.27 66.543,24.393L66.543,33L61.356,33C58.128,33 57,34.704 57,38.154L57,42L66.462,42L65.178,51L57,51L57,78L72,78C75.315,78 78,75.315 78,72L78,18C78,14.685 75.312,12 72,12Z" />
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 472 B |
@@ -1 +1,3 @@
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" class="size-6 fill-current"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.463 2 11.97c0 4.404 2.865 8.14 6.839 9.458.5.092.682-.216.682-.48 0-.236-.008-.864-.013-1.695-2.782.602-3.369-1.337-3.369-1.337-.454-1.151-1.11-1.458-1.11-1.458-.908-.618.069-.606.069-.606 1.003.07 1.531 1.027 1.531 1.027.892 1.524 2.341 1.084 2.91.828.092-.643.35-1.083.636-1.332-2.22-.251-4.555-1.107-4.555-4.927 0-1.088.39-1.979 1.029-2.675-.103-.252-.446-1.266.098-2.638 0 0 .84-.268 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.29 2.747-1.022 2.747-1.022.546 1.372.202 2.386.1 2.638.64.696 1.028 1.587 1.028 2.675 0 3.83-2.339 4.673-4.566 4.92.359.307.678.915.678 1.846 0 1.332-.012 2.407-.012 2.734 0 .267.18.577.688.48 3.97-1.32 6.833-5.054 6.833-9.458C22 6.463 17.522 2 12 2Z"></path></svg>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" class="size-6 fill-current">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.463 2 11.97c0 4.404 2.865 8.14 6.839 9.458.5.092.682-.216.682-.48 0-.236-.008-.864-.013-1.695-2.782.602-3.369-1.337-3.369-1.337-.454-1.151-1.11-1.458-1.11-1.458-.908-.618.069-.606.069-.606 1.003.07 1.531 1.027 1.531 1.027.892 1.524 2.341 1.084 2.91.828.092-.643.35-1.083.636-1.332-2.22-.251-4.555-1.107-4.555-4.927 0-1.088.39-1.979 1.029-2.675-.103-.252-.446-1.266.098-2.638 0 0 .84-.268 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.29 2.747-1.022 2.747-1.022.546 1.372.202 2.386.1 2.638.64.696 1.028 1.587 1.028 2.675 0 3.83-2.339 4.673-4.566 4.92.359.307.678.915.678 1.846 0 1.332-.012 2.407-.012 2.734 0 .267.18.577.688.48 3.97-1.32 6.833-5.054 6.833-9.458C22 6.463 17.522 2 12 2Z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 871 B |
@@ -0,0 +1,6 @@
|
||||
|
||||
<svg viewBox="0 0 52 52" aria-hidden="true" class="size-6 fill-current">
|
||||
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.996,48C13.313,48 2.992,37.684 2.992,25C2.992,12.316 13.313,2 25.996,2C31.742,2 37.242,4.129 41.488,7.996L42.262,8.703L34.676,16.289L33.973,15.688C31.746,13.781 28.914,12.73 25.996,12.73C19.23,12.73 13.723,18.234 13.723,25C13.723,31.766 19.23,37.27 25.996,37.27C30.875,37.27 34.73,34.777 36.547,30.531L24.996,30.531L24.996,20.176L47.547,20.207L47.715,21C48.891,26.582 47.949,34.793 43.184,40.668C39.238,45.531 33.457,48 25.996,48Z" />
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 582 B |
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 48 48" aria-hidden="true" class="size-6 fill-current">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M42,37C42,39.762 39.762,42 37,42L11,42C8.239,42 6,39.762 6,37L6,11C6,8.238 8.239,6 11,6L37,6C39.762,6 42,8.238 42,11L42,37ZM36,36L35.999,26.274C35.999,21.25 33.316,19 29.738,19C26.85,19 25.721,20.5 25,21.616L25,19L20,19L20,36L25,36L25,27C25,26.511 24.957,25.543 25.101,25.193C25.495,24.215 26.307,23.203 27.808,23.203C29.775,23.203 31,24.703 31,26.901L31,36L36,36ZM12,19L12,36L17,36L17,19L12,19ZM14.485,17C16.035,17 17,15.887 17,14.499C16.972,13.08 16.035,12 14.514,12C12.995,12 12,13.08 12,14.499C12,15.888 12.965,17 14.457,17L14.485,17Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 676 B |
+10
-2
@@ -4,7 +4,9 @@ use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\HoldingController;
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\PortfolioController;
|
||||
use App\Http\Controllers\SocialLoginController;
|
||||
use App\Http\Controllers\TransactionController;
|
||||
use App\Http\Controllers\VerifyConnectedAccountController;
|
||||
use Laravel\Jetstream\Http\Controllers\Livewire\PrivacyPolicyController;
|
||||
use Laravel\Jetstream\Http\Controllers\Livewire\TermsOfServiceController;
|
||||
|
||||
@@ -21,10 +23,10 @@ Route::get('/test', function () {
|
||||
//
|
||||
});
|
||||
|
||||
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'])->group(function () {
|
||||
Route::middleware(['auth:sanctum', config('jetstream.auth_session')])->group(function () {
|
||||
|
||||
Route::get('/dashboard', [DashboardController::class, 'show'])->name('dashboard');
|
||||
Route::view('/import-export', 'import-export')->name('import-export');
|
||||
Route::view('/import-export', 'import-export')->name('import-export')->middleware('verified');
|
||||
|
||||
Route::get('/portfolio/create', [PortfolioController::class, 'create'])->name('portfolio.create');
|
||||
Route::get('/portfolio/{portfolio}', [PortfolioController::class, 'show'])->name('portfolio.show');
|
||||
@@ -37,3 +39,9 @@ Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified']
|
||||
// overwrites jetstream routes
|
||||
Route::get('/terms', [TermsOfServiceController::class, 'show'])->name('terms.show');
|
||||
Route::get('/privacy', [PrivacyPolicyController::class, 'show'])->name('policy.show');
|
||||
|
||||
// social login routes
|
||||
Route::get('auth/verify/{verification_id}', VerifyConnectedAccountController::class)->name('verify_connected_account');
|
||||
|
||||
Route::get('auth/{provider}', [SocialLoginController::class, 'redirectToProvider'])->name('oauth.redirect');
|
||||
Route::get('auth/{provider}/callback', [SocialLoginController::class, 'handleProviderCallback']);
|
||||
|
||||
Reference in New Issue
Block a user