From c0c2e693669206d8b27abed8aef947d7e253efa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Viguier?= Date: Wed, 17 Jan 2024 11:10:34 +0100 Subject: [PATCH] Oauth is now available (#2190) --- .env.example | 44 +- app/Enum/OauthProvidersType.php | 24 + app/Http/Controllers/Oauth.php | 164 ++++ .../Components/Forms/Profile/Oauth.php | 80 ++ app/Livewire/Components/Modals/Login.php | 36 +- app/Livewire/DTO/OauthData.php | 18 + .../Builders/OauthCredentialBuilder.php | 14 + app/Models/OauthCredential.php | 66 ++ app/Models/User.php | 22 + app/Providers/EventServiceProvider.php | 18 + composer.json | 9 + composer.lock | 809 +++++++++++++++++- config/app.php | 1 + config/services.php | 62 ++ ..._124937_create_oauth_credentials_table.php | 55 ++ lang/cz/oauth.php | 12 + lang/de/oauth.php | 12 + lang/el/oauth.php | 12 + lang/en/oauth.php | 12 + lang/es/oauth.php | 12 + lang/fr/oauth.php | 12 + lang/hu/oauth.php | 12 + lang/it/oauth.php | 12 + lang/nl/oauth.php | 12 + lang/no/oauth.php | 12 + lang/pl/oauth.php | 12 + lang/pt/oauth.php | 12 + lang/ru/oauth.php | 12 + lang/sk/oauth.php | 12 + lang/sv/oauth.php | 12 + lang/vi/oauth.php | 12 + lang/zh_CN/oauth.php | 12 + lang/zh_TW/oauth.php | 12 + package-lock.json | 10 + package.json | 1 + resources/css/app.css | 1 + resources/css/fonts.css | 1 + .../livewire/forms/profile/oauth.blade.php | 39 + .../views/livewire/modals/login.blade.php | 28 +- .../modules/profile/second-factor.blade.php | 2 +- .../views/livewire/pages/profile.blade.php | 1 + routes/web-livewire.php | 7 + tailwind.config.js | 11 +- tests/Livewire/Forms/Profile/OauthTest.php | 81 ++ 44 files changed, 1806 insertions(+), 14 deletions(-) create mode 100644 app/Enum/OauthProvidersType.php create mode 100644 app/Http/Controllers/Oauth.php create mode 100644 app/Livewire/Components/Forms/Profile/Oauth.php create mode 100644 app/Livewire/DTO/OauthData.php create mode 100644 app/Models/Builders/OauthCredentialBuilder.php create mode 100644 app/Models/OauthCredential.php create mode 100644 database/migrations/2024_01_13_124937_create_oauth_credentials_table.php create mode 100644 lang/cz/oauth.php create mode 100644 lang/de/oauth.php create mode 100644 lang/el/oauth.php create mode 100644 lang/en/oauth.php create mode 100644 lang/es/oauth.php create mode 100644 lang/fr/oauth.php create mode 100644 lang/hu/oauth.php create mode 100644 lang/it/oauth.php create mode 100644 lang/nl/oauth.php create mode 100644 lang/no/oauth.php create mode 100644 lang/pl/oauth.php create mode 100644 lang/pt/oauth.php create mode 100644 lang/ru/oauth.php create mode 100644 lang/sk/oauth.php create mode 100644 lang/sv/oauth.php create mode 100644 lang/vi/oauth.php create mode 100644 lang/zh_CN/oauth.php create mode 100644 lang/zh_TW/oauth.php create mode 100644 resources/views/livewire/forms/profile/oauth.blade.php create mode 100644 tests/Livewire/Forms/Profile/OauthTest.php diff --git a/.env.example b/.env.example index 108e324240f..f83b47a4f4b 100644 --- a/.env.example +++ b/.env.example @@ -114,4 +114,46 @@ TRUSTED_PROXIES=null #SKIP_DIAGNOSTICS_CHECKS= VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" -VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" \ No newline at end of file +VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +# Oauth token data +# XXX_REDIRECT_URI should be left as default unless you know exactly what you do. + +# AMAZON_SIGNIN_CLIENT_ID= +# AMAZON_SIGNIN_SECRET= +# AMAZON_SIGNIN_REDIRECT_URI=/auth/amazon/redirect + +# https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple +# Note: the client secret used for "Sign In with Apple" is a JWT token that can have a maximum lifetime of 6 months. +# The article above explains how to generate the client secret on demand and you'll need to update this every 6 months. +# To generate the client secret for each request, see Generating A Client Secret For Sign In With Apple On Each Request. +# https://bannister.me/blog/generating-a-client-secret-for-sign-in-with-apple-on-each-request +# APPLE_CLIENT_ID= +# APPLE_CLIENT_SECRET= +# APPLE_REDIRECT_URI=/auth/apple/redirect + +# FACEBOOK_CLIENT_ID= +# FACEBOOK_CLIENT_SECRET= +# FACEBOOK_REDIRECT_URI=/auth/facebook/redirect + +# GITHUB_CLIENT_ID= +# GITHUB_CLIENT_SECRET= +# GITHUB_REDIRECT_URI=/auth/github/redirect + +# GOOGLE_CLIENT_ID= +# GOOGLE_CLIENT_SECRET= +# GOOGLE_REDIRECT_URI=/auth/google/redirect + +# MASTODON_DOMAIN=https://mastodon.social +# MASTODON_ID= +# MASTODON_SECRET= +# MASTODON_REDIRECT_URI=/auth/mastodon/redirect + +# MICROSOFT_CLIENT_ID= +# MICROSOFT_CLIENT_SECRET= +# MICROSOFT_REDIRECT_URI=/auth/microsoft/redirect + +# NEXTCLOUD_CLIENT_ID= +# NEXTCLOUD_CLIENT_SECRET= +# NEXTCLOUD_REDIRECT_URI=/auth/nextcloud/redirect +# NEXTCLOUD_BASE_URI= \ No newline at end of file diff --git a/app/Enum/OauthProvidersType.php b/app/Enum/OauthProvidersType.php new file mode 100644 index 00000000000..687c865c48e --- /dev/null +++ b/app/Enum/OauthProvidersType.php @@ -0,0 +1,24 @@ +validateProviderOrDie($provider); + + // We are already logged in: Registration operation + if (Auth::check()) { + return $this->registerOrDie($providerEnum); + } + + // Authentication operation + return $this->authenticateOrDie($providerEnum); + } + + /** + * Function called to authenticate a user to an Oauth server. + * + * @param string $provider + * + * @return HttpFoundationRedirectResponse + */ + public function authenticate(string $provider) + { + if (Auth::check()) { + throw new UnauthorizedException('User already authenticated.'); + } + + $providerEnum = $this->validateProviderOrDie($provider); + + return Socialite::driver($providerEnum->value)->redirect(); + } + + /** + * Add some security on registration. + * + * @param string $provider + * + * @return HttpFoundationRedirectResponse + */ + public function register(string $provider) + { + $providerEnum = $this->validateProviderOrDie($provider); + + Auth::user() ?? throw new UnauthenticatedException(); + if (!Request::hasValidSignature(false)) { + throw new UnauthorizedException('Registration attempted but not initialized.'); + } + + Session::put($providerEnum->value, self::OAUTH_REGISTER); + + return Socialite::driver($providerEnum->value)->redirect(); + } + + /** + * Authenticate and redirect. + * + * @param OauthProvidersType $provider + * + * @return RedirectResponse + */ + private function authenticateOrDie(OauthProvidersType $provider) + { + $user = Socialite::driver($provider->value)->user(); + + $credential = OauthCredential::query() + ->with(['user']) + ->where('token_id', '=', $user->getId()) + ->where('provider', '=', $provider) + ->first(); + + if ($credential === null) { + throw new UnauthorizedException('User not found!'); + } + + Auth::login($credential->user); + + return redirect(route('livewire-gallery')); + } + + /** + * Authenticate and redirect. + * + * @param OauthProvidersType $provider + * + * @return RedirectResponse + */ + private function registerOrDie(OauthProvidersType $provider) + { + if (Session::get($provider->value) !== self::OAUTH_REGISTER) { + throw new UnauthorizedException('Registration attempted but not authorized.'); + } + + $user = Socialite::driver($provider->value)->user(); + + /** @var User $authedUser */ + $authedUser = Auth::user(); + + $count_existing = OauthCredential::query() + ->where('provider', '=', $provider) + ->where('user_id', '=', $authedUser->id) + ->count(); + if ($count_existing > 0) { + throw new LycheeLogicException('Oauth credential for that provider already exists.'); + } + + $credential = OauthCredential::create([ + 'provider' => $provider, + 'user_id' => $authedUser->id, + 'token_id' => $user->getId(), + ]); + $credential->save(); + + return redirect(route('profile')); + } +} \ No newline at end of file diff --git a/app/Livewire/Components/Forms/Profile/Oauth.php b/app/Livewire/Components/Forms/Profile/Oauth.php new file mode 100644 index 00000000000..268deddfc8f --- /dev/null +++ b/app/Livewire/Components/Forms/Profile/Oauth.php @@ -0,0 +1,80 @@ +authorize(UserPolicy::CAN_EDIT, [User::class]); + + return view('livewire.forms.profile.oauth'); + } + + public function clear(string $provider): void + { + $this->authorize(UserPolicy::CAN_EDIT, [User::class]); + $providerEnum = OauthProvidersType::from($provider); + + /** @var User $user */ + $user = Auth::user() ?? throw new UnauthenticatedException(); + $user->oauthCredentials()->where('provider', '=', $providerEnum)->delete(); + } + + /** + * Return computed property for OauthData. + * + * @return array + */ + public function getOauthDataProperty(): array + { + $oauthData = []; + + /** @var User $user */ + $user = Auth::user() ?? throw new UnauthenticatedException(); + + $credentials = $user->oauthCredentials()->get(); + + foreach (OauthProvidersType::cases() as $provider) { + $client_id = config('services.' . $provider->value . '.client_id'); + if ($client_id === null || $client_id === '') { + continue; + } + + // We create a signed route for 5 minutes + $route = URL::signedRoute( + name: 'oauth-register', + parameters: ['provider' => $provider->value], + expiration: now()->addMinutes(5), + absolute: false); + + $oauthData[$provider->value] = new OauthData( + providerType: $provider->value, + isEnabled: $credentials->search(fn (OauthCredential $c) => $c->provider === $provider) !== false, + registrationRoute: $route, + ); + } + + return $oauthData; + } +} \ No newline at end of file diff --git a/app/Livewire/Components/Modals/Login.php b/app/Livewire/Components/Modals/Login.php index baf9b878018..fc91ab93523 100644 --- a/app/Livewire/Components/Modals/Login.php +++ b/app/Livewire/Components/Modals/Login.php @@ -2,12 +2,14 @@ namespace App\Livewire\Components\Modals; +use App\Enum\OauthProvidersType; use App\Exceptions\Internal\QueryBuilderException; use App\Http\RuleSets\LoginRuleSet; use App\Metadata\Versions\FileVersion; use App\Metadata\Versions\GitHubVersion; use App\Metadata\Versions\InstalledVersion; use App\Models\Configs; +use App\Models\User; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Auth; @@ -22,7 +24,6 @@ */ class Login extends Component { - #[Locked] public bool $can_use_2fa = false; #[Locked] public bool $is_new_release_available = false; #[Locked] public bool $is_git_update_available = false; #[Locked] public ?string $version = null; @@ -46,8 +47,6 @@ public function render(): View */ public function mount(): void { - $this->can_use_2fa = WebAuthnCredential::query()->whereNull('disabled_at')->count() > 0; - if (!Configs::getValueAsBool('hide_version_number')) { $this->version = resolve(InstalledVersion::class)->getVersion()->toString(); } @@ -96,4 +95,35 @@ public function submit(): void $this->addError('wrongLogin', 'Wrong login or password.'); Log::error(__METHOD__ . ':' . __LINE__ . ' User (' . $data['username'] . ') has tried to log in from ' . request()->ip()); } + + /** + * Check whether any user has a 2FA credential set. + * + * @return bool + */ + public function getCanUse2faProperty(): bool + { + return WebAuthnCredential::query()->whereNull('disabled_at')->count() > 0; + } + + /** + * List the Oauth providers which are enabled. + * + * @return array + */ + public function getAvailableOauthProperty(): array + { + $oauthAvailable = []; + + foreach (OauthProvidersType::cases() as $oauthProvider) { + $client_id = config('services.' . $oauthProvider->value . '.client_id'); + if ($client_id === null || $client_id === '') { + continue; + } + + $oauthAvailable[] = $oauthProvider->value; + } + + return $oauthAvailable; + } } diff --git a/app/Livewire/DTO/OauthData.php b/app/Livewire/DTO/OauthData.php new file mode 100644 index 00000000000..97182064293 --- /dev/null +++ b/app/Livewire/DTO/OauthData.php @@ -0,0 +1,18 @@ + + */ +class OauthCredentialBuilder extends FixedQueryBuilder +{ +} \ No newline at end of file diff --git a/app/Models/OauthCredential.php b/app/Models/OauthCredential.php new file mode 100644 index 00000000000..1435d848609 --- /dev/null +++ b/app/Models/OauthCredential.php @@ -0,0 +1,66 @@ + 'datetime', + 'updated_at' => 'datetime', + 'user_id' => 'integer', + 'provider' => OauthProvidersType::class, + ]; + + protected $hidden = [ + 'token_id', + ]; + + /** + * Return the relationship between a Photo and its Album. + * + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + /** + * @param $query + * + * @return OauthCredentialBuilder + */ + public function newEloquentBuilder($query): OauthCredentialBuilder + { + return new OauthCredentialBuilder($query); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 6b48e3dc748..14d58498b0a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -41,6 +41,7 @@ * @property string|null $token * @property string|null $remember_token * @property Collection $albums + * @property Collection $oauthCredentials * @property DatabaseNotificationCollection|DatabaseNotification[] $notifications * @property Collection $shared * @property Collection $photos @@ -105,6 +106,17 @@ class User extends Authenticatable implements WebAuthnAuthenticatable 'may_edit_own_settings' => 'boolean', ]; + protected $hidden = [ + 'amazon_id', + 'apple_id', + 'facebook_id', + 'github_id', + 'google_id', + 'mastodon_id', + 'microsoft_id', + 'nextcloud_id', + ]; + protected function _toArray(): array { return parent::toArray(); @@ -157,6 +169,16 @@ public function shared(): BelongsToMany ); } + /** + * Return the Oauth credentials owned by the user. + * + * @return HasMany + */ + public function oauthCredentials(): HasMany + { + return $this->hasMany(OauthCredential::class, 'user_id', 'id'); + } + /** * Used by Larapass. * diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 66c5af89ea0..283b6ecd584 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -4,6 +4,14 @@ use Illuminate\Auth\Events\Registered; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; +use SocialiteProviders\Amazon\AmazonExtendSocialite; +use SocialiteProviders\Apple\AppleExtendSocialite; +use SocialiteProviders\Facebook\FacebookExtendSocialite; +use SocialiteProviders\GitHub\GitHubExtendSocialite; +use SocialiteProviders\Google\GoogleExtendSocialite; +use SocialiteProviders\Manager\SocialiteWasCalled; +use SocialiteProviders\Microsoft\MicrosoftExtendSocialite; +use SocialiteProviders\Nextcloud\NextcloudExtendSocialite; class EventServiceProvider extends ServiceProvider { @@ -16,6 +24,16 @@ class EventServiceProvider extends ServiceProvider Registered::class => [ // SendEmailVerificationNotification::class, ], + SocialiteWasCalled::class => [ + AmazonExtendSocialite::class . '@handle', + AppleExtendSocialite::class . '@handle', + FacebookExtendSocialite::class . '@handle', + GitHubExtendSocialite::class . '@handle', + GoogleExtendSocialite::class . '@handle', + // Mastodon is provided directly. + MicrosoftExtendSocialite::class . '@handle', + NextcloudExtendSocialite::class . '@handle', + ], ]; /** diff --git a/composer.json b/composer.json index d9bc0fadbd3..db759e7203a 100644 --- a/composer.json +++ b/composer.json @@ -53,6 +53,7 @@ "laminas/laminas-text": "^2.9", "laragear/webauthn": "^1.2.0", "laravel/framework": "^10.0", + "laravel/socialite": "^5.11", "livewire/livewire": "^3.0", "lychee-org/nestedset": "^8.0", "lychee-org/php-exif": "^1.0.0", @@ -61,6 +62,14 @@ "php-ffmpeg/php-ffmpeg": "^1.0", "php-http/guzzle7-adapter": "^1.0", "php-http/message": "^1.12", + "revolution/socialite-mastodon": "^1.4", + "socialiteproviders/amazon": "^4.1", + "socialiteproviders/apple": "^5.6", + "socialiteproviders/facebook": "^4.1", + "socialiteproviders/github": "^4.1", + "socialiteproviders/google": "^4.1", + "socialiteproviders/microsoft": "^4.2", + "socialiteproviders/nextcloud": "^4.0", "spatie/guzzle-rate-limiter-middleware": "^2.0", "spatie/laravel-feed": "^4.0", "spatie/laravel-image-optimizer": "^1.6.2", diff --git a/composer.lock b/composer.lock index c4d4267a9c8..9413ef07b8a 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": "4e0a59023e0ef37787f242d7b0d54c54", + "content-hash": "c66f10a5224314dca039c06c1f071b35", "packages": [ { "name": "bepsvpt/secure-headers", @@ -1036,6 +1036,69 @@ }, "time": "2023-08-08T05:53:35+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.10.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff", + "shasum": "" + }, + "require": { + "php": "^7.4||^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.5||^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^1.0||^2.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.0" + }, + "time": "2023-12-01T16:26:39+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.3.0", @@ -2509,6 +2572,213 @@ }, "time": "2023-11-08T14:08:06+00:00" }, + { + "name": "laravel/socialite", + "version": "v5.11.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "4f6a8af6f3f7c18da03d19842dd0514315501c10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/4f6a8af6f3f7c18da03d19842dd0514315501c10", + "reference": "4f6a8af6f3f7c18da03d19842dd0514315501c10", + "shasum": "" + }, + "require": { + "ext-json": "*", + "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" + }, + "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": "2023-12-02T18:22:36+00:00" + }, + { + "name": "lcobucci/clock", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "6f28b826ea01306b07980cb8320ab30b966cd715" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/6f28b826ea01306b07980cb8320ab30b966cd715", + "reference": "6f28b826ea01306b07980cb8320ab30b966cd715", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.27", + "lcobucci/coding-standard": "^11.0.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.25", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^10.2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-11-17T17:00:27+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "5.2.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/0ba88aed12c04bd2ed9924f500673f32b67a6211", + "reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-sodium": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "psr/clock": "^1.0" + }, + "require-dev": { + "infection/infection": "^0.27.0", + "lcobucci/clock": "^3.0", + "lcobucci/coding-standard": "^11.0", + "phpbench/phpbench": "^1.2.9", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^10.2.6" + }, + "suggest": { + "lcobucci/clock": ">= 3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "support": { + "issues": "https://github.com/lcobucci/jwt/issues", + "source": "https://github.com/lcobucci/jwt/tree/5.2.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-11-20T21:17:42+00:00" + }, { "name": "league/commonmark", "version": "2.4.1", @@ -2903,6 +3173,82 @@ ], "time": "2023-10-17T14:13:20+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.3.5", @@ -4979,6 +5325,467 @@ ], "time": "2023-11-08T05:53:05+00:00" }, + { + "name": "revolution/socialite-mastodon", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/kawax/socialite-mastodon.git", + "reference": "7e926091af7b81b0f3948ddc1b9379d074584515" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kawax/socialite-mastodon/zipball/7e926091af7b81b0f3948ddc1b9379d074584515", + "reference": "7e926091af7b81b0f3948ddc1b9379d074584515", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel/socialite": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0||^8.0", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Revolution\\Socialite\\Mastodon\\MastodonServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Revolution\\Socialite\\Mastodon\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "kawax", + "email": "kawaxbiz@gmail.com" + } + ], + "description": "Socialite for Mastodon", + "keywords": [ + "laravel", + "mastodon", + "socialite" + ], + "support": { + "issues": "https://github.com/kawax/socialite-mastodon/issues", + "source": "https://github.com/kawax/socialite-mastodon/tree/1.4.0" + }, + "time": "2023-01-29T01:06:31+00:00" + }, + { + "name": "socialiteproviders/amazon", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Amazon.git", + "reference": "717c8c78d42fdffced7ccba218092384ce473e1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Amazon/zipball/717c8c78d42fdffced7ccba218092384ce473e1d", + "reference": "717c8c78d42fdffced7ccba218092384ce473e1d", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2 || ^8.0", + "socialiteproviders/manager": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Amazon\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "atymic", + "email": "atymicq@gmail.com", + "homepage": "https://atymic.dev" + } + ], + "description": "Amazon OAuth2 Provider for Laravel Socialite", + "support": { + "source": "https://github.com/SocialiteProviders/Amazon/tree/4.1.0" + }, + "time": "2020-12-01T23:10:59+00:00" + }, + { + "name": "socialiteproviders/apple", + "version": "5.6.1", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Apple.git", + "reference": "e00ff7c06e4df297aaeace4e454b2054d6bebe95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Apple/zipball/e00ff7c06e4df297aaeace4e454b2054d6bebe95", + "reference": "e00ff7c06e4df297aaeace4e454b2054d6bebe95", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "firebase/php-jwt": "^6.8", + "lcobucci/clock": "^2.0 || ^3.0", + "lcobucci/jwt": "^4.1.5 || ^5.0.0", + "php": "^8.0", + "socialiteproviders/manager": "^4.4" + }, + "suggest": { + "ahilmurugesan/socialite-apple-helper": "Automatic Apple client key generation and management." + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Apple\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ahilan", + "email": "ahilmurugesan@gmail.com", + "role": "Developer" + }, + { + "name": "Vamsi Krishna V", + "email": "vamsi@vonectech.com", + "homepage": "https://vonectech.com/", + "role": "Farmer" + } + ], + "description": "Apple OAuth2 Provider for Laravel Socialite", + "keywords": [ + "apple", + "apple client key", + "apple sign in", + "client key generator", + "client key refresh", + "laravel", + "laravel apple", + "laravel socialite", + "oauth", + "provider", + "sign in with apple", + "socialite", + "socialite apple" + ], + "support": { + "docs": "https://socialiteproviders.com/apple", + "issues": "https://github.com/socialiteproviders/providers/issues", + "source": "https://github.com/socialiteproviders/providers" + }, + "time": "2023-12-06T14:43:17+00:00" + }, + { + "name": "socialiteproviders/facebook", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Facebook.git", + "reference": "9b94a9334b5d0f61de8f5a20928d63d4d8f4e00d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Facebook/zipball/9b94a9334b5d0f61de8f5a20928d63d4d8f4e00d", + "reference": "9b94a9334b5d0f61de8f5a20928d63d4d8f4e00d", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2 || ^8.0", + "socialiteproviders/manager": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Facebook\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oleksandr Prypkhan (Alex Wells)", + "email": "autaut03@googlemail.com" + } + ], + "description": "Facebook (facebook.com) OAuth2 Provider for Laravel Socialite", + "support": { + "source": "https://github.com/SocialiteProviders/Facebook/tree/4.1.0" + }, + "time": "2020-12-01T23:10:59+00:00" + }, + { + "name": "socialiteproviders/github", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/GitHub.git", + "reference": "25fc481721d74b829b43bff3519e7103d5339e19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/GitHub/zipball/25fc481721d74b829b43bff3519e7103d5339e19", + "reference": "25fc481721d74b829b43bff3519e7103d5339e19", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2 || ^8.0", + "socialiteproviders/manager": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\GitHub\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anton Komarev", + "email": "ell@cybercog.su" + } + ], + "description": "GitHub OAuth2 Provider for Laravel Socialite", + "support": { + "source": "https://github.com/SocialiteProviders/GitHub/tree/4.1.0" + }, + "time": "2020-12-01T23:10:59+00:00" + }, + { + "name": "socialiteproviders/google", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Google-Plus.git", + "reference": "1cb8f6fb2c0dd0fc8b34e95f69865663fdf0b401" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Google-Plus/zipball/1cb8f6fb2c0dd0fc8b34e95f69865663fdf0b401", + "reference": "1cb8f6fb2c0dd0fc8b34e95f69865663fdf0b401", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2 || ^8.0", + "socialiteproviders/manager": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Google\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "xstoop", + "email": "myenglishnameisx@gmail.com" + } + ], + "description": "Google OAuth2 Provider for Laravel Socialite", + "support": { + "source": "https://github.com/SocialiteProviders/Google-Plus/tree/4.1.0" + }, + "time": "2020-12-01T23:10:59+00:00" + }, + { + "name": "socialiteproviders/manager", + "version": "v4.4.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Manager.git", + "reference": "df5e45b53d918ec3d689f014d98a6c838b98ed96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/df5e45b53d918ec3d689f014d98a6c838b98ed96", + "reference": "df5e45b53d918ec3d689f014d98a6c838b98ed96", + "shasum": "" + }, + "require": { + "illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0", + "laravel/socialite": "~5.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "phpunit/phpunit": "^6.0 || ^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "SocialiteProviders\\Manager\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "SocialiteProviders\\Manager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andy Wendt", + "email": "andy@awendt.com" + }, + { + "name": "Anton Komarev", + "email": "a.komarev@cybercog.su" + }, + { + "name": "Miguel Piedrafita", + "email": "soy@miguelpiedrafita.com" + }, + { + "name": "atymic", + "email": "atymicq@gmail.com", + "homepage": "https://atymic.dev" + } + ], + "description": "Easily add new or override built-in providers in Laravel Socialite.", + "homepage": "https://socialiteproviders.com", + "keywords": [ + "laravel", + "manager", + "oauth", + "providers", + "socialite" + ], + "support": { + "issues": "https://github.com/socialiteproviders/manager/issues", + "source": "https://github.com/socialiteproviders/manager" + }, + "time": "2023-08-27T23:46:34+00:00" + }, + { + "name": "socialiteproviders/microsoft", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Microsoft.git", + "reference": "19bc79810d7319c466f38552546b2c233b634059" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Microsoft/zipball/19bc79810d7319c466f38552546b2c233b634059", + "reference": "19bc79810d7319c466f38552546b2c233b634059", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0", + "socialiteproviders/manager": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Microsoft\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Faust", + "email": "hello@brianfaust.de" + } + ], + "description": "Microsoft OAuth2 Provider for Laravel Socialite", + "keywords": [ + "laravel", + "microsoft", + "oauth", + "provider", + "socialite" + ], + "support": { + "docs": "https://socialiteproviders.com/microsoft", + "issues": "https://github.com/socialiteproviders/providers/issues", + "source": "https://github.com/socialiteproviders/providers" + }, + "time": "2023-03-02T09:58:36+00:00" + }, + { + "name": "socialiteproviders/nextcloud", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Nextcloud.git", + "reference": "94b3cef64410994bdd480230a7aa78294564cc44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Nextcloud/zipball/94b3cef64410994bdd480230a7aa78294564cc44", + "reference": "94b3cef64410994bdd480230a7aa78294564cc44", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2 || ^8.0", + "socialiteproviders/manager": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Nextcloud\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Artur Shaik", + "email": "artur@shaik.link" + } + ], + "description": "Nextcloud OAuth2 Provider for Laravel Socialite", + "support": { + "source": "https://github.com/SocialiteProviders/Nextcloud/tree/4.0.0" + }, + "time": "2021-02-27T23:17:54+00:00" + }, { "name": "spatie/guzzle-rate-limiter-middleware", "version": "2.0.1", diff --git a/config/app.php b/config/app.php index 53bbecf9bc7..8a4546c7d4a 100644 --- a/config/app.php +++ b/config/app.php @@ -252,6 +252,7 @@ function renv(string $cst, ?string $default = null): string * Package Service Providers... */ + \SocialiteProviders\Manager\ServiceProvider::class, // Barryvdh\Debugbar\ServiceProvider::class, /* diff --git a/config/services.php b/config/services.php index dff256b7506..6265f1464cb 100644 --- a/config/services.php +++ b/config/services.php @@ -28,4 +28,66 @@ 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], + + /* + |-------------------------------------------------------------------------- + | Oauth services + |-------------------------------------------------------------------------- + */ + 'amazon' => [ + 'client_id' => env('AMAZON_SIGNIN_CLIENT_ID'), + 'client_secret' => env('AMAZON_SIGNIN_SECRET'), + 'redirect' => env('AMAZON_SIGNIN_REDIRECT_URI', '/auth/amazon/redirect'), + ], + + // https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple + // Note: the client secret used for "Sign In with Apple" is a JWT token that can have a maximum lifetime of 6 months. + // The article above explains how to generate the client secret on demand and you'll need to update this every 6 months. + // To generate the client secret for each request, see Generating A Client Secret For Sign In With Apple On Each Request. + // https://bannister.me/blog/generating-a-client-secret-for-sign-in-with-apple-on-each-request + 'apple' => [ + 'client_id' => env('APPLE_CLIENT_ID'), + 'client_secret' => env('APPLE_CLIENT_SECRET'), + 'redirect' => env('APPLE_REDIRECT_URI', '/auth/apple/redirect'), + ], + + 'facebook' => [ + 'client_id' => env('FACEBOOK_CLIENT_ID'), + 'client_secret' => env('FACEBOOK_CLIENT_SECRET'), + 'redirect' => env('FACEBOOK_REDIRECT_URI', '/auth/facebook/redirect'), + ], + + 'github' => [ + 'client_id' => env('GITHUB_CLIENT_ID'), + 'client_secret' => env('GITHUB_CLIENT_SECRET'), + 'redirect' => env('GITHUB_REDIRECT_URI', '/auth/github/redirect'), + ], + + 'google' => [ + 'client_id' => env('GOOGLE_CLIENT_ID'), + 'client_secret' => env('GOOGLE_CLIENT_SECRET'), + 'redirect' => env('GOOGLE_REDIRECT_URI', '/auth/google/redirect'), + ], + + 'mastodon' => [ + 'domain' => env('MASTODON_DOMAIN'), + 'client_id' => env('MASTODON_ID'), + 'client_secret' => env('MASTODON_SECRET'), + 'redirect' => env('MASTODON_REDIRECT_URI', '/auth/mastodon/redirect'), + // 'read', 'write', 'follow' + 'scope' => ['read'], + ], + + 'microsoft' => [ + 'client_id' => env('MICROSOFT_CLIENT_ID'), + 'client_secret' => env('MICROSOFT_CLIENT_SECRET'), + 'redirect' => env('MICROSOFT_REDIRECT_URI', '/auth/microsoft/redirect'), + ], + + 'nextcloud' => [ + 'client_id' => env('NEXTCLOUD_CLIENT_ID'), + 'client_secret' => env('NEXTCLOUD_CLIENT_SECRET'), + 'redirect' => env('NEXTCLOUD_REDIRECT_URI', '/auth/nextcloud/redirect'), + 'instance_uri' => env('NEXTCLOUD_BASE_URI'), + ], ]; diff --git a/database/migrations/2024_01_13_124937_create_oauth_credentials_table.php b/database/migrations/2024_01_13_124937_create_oauth_credentials_table.php new file mode 100644 index 00000000000..6b1480f4989 --- /dev/null +++ b/database/migrations/2024_01_13_124937_create_oauth_credentials_table.php @@ -0,0 +1,55 @@ +bigIncrements('id'); + + // User associated with the access capabilities + // If null we consider the album public + $table->unsignedInteger(self::USER_ID)->nullable()->default(null); + + $table->string(self::PROVIDER, 20); + $table->string(self::TOKEN_ID); + + $table->dateTime(self::CREATED_AT_COL_NAME, self::DATETIME_PRECISION)->nullable(); + $table->dateTime(self::UPDATED_AT_COL_NAME, self::DATETIME_PRECISION)->nullable(); + + $table->index([self::USER_ID]); // for credentials which are own by the currently authenticated user + $table->index([self::TOKEN_ID]); + $table->unique([self::TOKEN_ID]); + $table->index([self::TOKEN_ID, self::PROVIDER]); + $table->unique([self::PROVIDER, self::USER_ID]); + $table->foreign(self::USER_ID)->references('id')->on('users')->cascadeOnUpdate()->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists(self::TABLE_NAME); + } +}; diff --git a/lang/cz/oauth.php b/lang/cz/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/cz/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/de/oauth.php b/lang/de/oauth.php new file mode 100644 index 00000000000..418355fa27b --- /dev/null +++ b/lang/de/oauth.php @@ -0,0 +1,12 @@ + 'Oauth ist nicht verfügbar.', + 'SET_UP_CREDENTIALS' => 'Richten Sie die Zugangsdaten in Ihrer .env ein', + 'SET_UP_OAUTH' => 'Oauth-Authentifizierung einrichten', + 'SET_UP' => '%s einrichten', + 'TOKEN_REGISTERED' => 'Token %s registriert.', + 'RESET' => 'zurücksetzen', +]; \ No newline at end of file diff --git a/lang/el/oauth.php b/lang/el/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/el/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/en/oauth.php b/lang/en/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/en/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/es/oauth.php b/lang/es/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/es/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/fr/oauth.php b/lang/fr/oauth.php new file mode 100644 index 00000000000..8d4454dc1ec --- /dev/null +++ b/lang/fr/oauth.php @@ -0,0 +1,12 @@ + 'L’authentication via Oauth n’est pas disponible', + 'SET_UP_CREDENTIALS' => 'Définissez vos identifiants dans le fichier .env', + 'SET_UP_OAUTH' => 'Utiliser l’authentication Oauth', + 'SET_UP' => 'Connectez-vous avec %s', + 'TOKEN_REGISTERED' => '%s connecté.', + 'RESET' => 'oublier', +]; \ No newline at end of file diff --git a/lang/hu/oauth.php b/lang/hu/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/hu/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/it/oauth.php b/lang/it/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/it/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/nl/oauth.php b/lang/nl/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/nl/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/no/oauth.php b/lang/no/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/no/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/pl/oauth.php b/lang/pl/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/pl/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/pt/oauth.php b/lang/pt/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/pt/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/ru/oauth.php b/lang/ru/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/ru/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/sk/oauth.php b/lang/sk/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/sk/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/sv/oauth.php b/lang/sv/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/sv/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/vi/oauth.php b/lang/vi/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/vi/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/zh_CN/oauth.php b/lang/zh_CN/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/zh_CN/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/lang/zh_TW/oauth.php b/lang/zh_TW/oauth.php new file mode 100644 index 00000000000..d338d47c26c --- /dev/null +++ b/lang/zh_TW/oauth.php @@ -0,0 +1,12 @@ + 'Oauth is not available.', + 'SET_UP_CREDENTIALS' => 'Set up the credentials in your .env', + 'SET_UP_OAUTH' => 'Set up Oauth authentication', + 'SET_UP' => 'Set up %s', + 'TOKEN_REGISTERED' => '%s token registered.', + 'RESET' => 'reset', +]; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b6a9c1c45a0..59b700ebbc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "@fortawesome/fontawesome-free": "^6.5.1", "@types/justified-layout": "^4.1.4", "autoprefixer": "^10.4.16", "justified-layout": "^4.1.0", @@ -521,6 +522,15 @@ "node": ">=12" } }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.1.tgz", + "integrity": "sha512-CNy5vSwN3fsUStPRLX7fUYojyuzoEMSXPl7zSLJ8TgtRfjv24LOnOWKT2zYwaHZCJGkdyRnTmstR0P+Ah503Gw==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", diff --git a/package.json b/package.json index a6c0cc15e98..a2abf69637f 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@types/qrcode": "^1.5.5" }, "dependencies": { + "@fortawesome/fontawesome-free": "^6.5.1", "@types/justified-layout": "^4.1.4", "autoprefixer": "^10.4.16", "justified-layout": "^4.1.0", diff --git a/resources/css/app.css b/resources/css/app.css index bfd906e0990..2ddfe5e908c 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,5 +1,6 @@ @import "./photoDescription.css"; @import "./fonts.css"; +@import "@fortawesome/fontawesome-free/css/all.css"; @tailwind base; /* also contains normalize */ diff --git a/resources/css/fonts.css b/resources/css/fonts.css index 8a82b2cbcae..50376ff5e74 100644 --- a/resources/css/fonts.css +++ b/resources/css/fonts.css @@ -64,5 +64,6 @@ } #facebook:before { + font-family: "socials"; content: "\ea91"; } diff --git a/resources/views/livewire/forms/profile/oauth.blade.php b/resources/views/livewire/forms/profile/oauth.blade.php new file mode 100644 index 00000000000..3b739c3ec3f --- /dev/null +++ b/resources/views/livewire/forms/profile/oauth.blade.php @@ -0,0 +1,39 @@ +
+ @if(count($this->oauthData) === 0) + @if (Auth::user()->may_administrate === true) +
+

{{ __('oauth.NOT_AVAILABLE') }}

+

{{ __('oauth.SET_UP_CREDENTIALS') }}

+
+ @endif + @else +
+

{{ __('oauth.SET_UP_OAUTH') }}

+
+ @foreach ($this->oauthData as $oauthData) +
+ $oauthData->isEnabled, + "fa-brands fa-apple" => $oauthData->providerType === 'apple', + "fa-brands fa-amazon" => $oauthData->providerType === 'amazon', + "fa-brands fa-facebook" => $oauthData->providerType === 'facebook', + "fa-brands fa-github" => $oauthData->providerType === 'github', + "fa-brands fa-google" => $oauthData->providerType === 'google', + "fa-brands fa-mastodon" => $oauthData->providerType === 'mastodon', + "fa-brands fa-microsoft" => $oauthData->providerType === 'microsoft', + "fa-solid fa-cloud" => $oauthData->providerType === 'nextcloud', + ])> + @if($oauthData->isEnabled) + + {{ sprintf(__('oauth.TOKEN_REGISTERED'), ucfirst($oauthData->providerType)) }}({{ __('oauth.RESET') }}) + + @else + + {{ sprintf(__('oauth.SET_UP'), ucfirst($oauthData->providerType)) }} + + @endif +
+ @endforeach + @endif +
diff --git a/resources/views/livewire/modals/login.blade.php b/resources/views/livewire/modals/login.blade.php index 58fe6ca6971..c367cfce492 100644 --- a/resources/views/livewire/modals/login.blade.php +++ b/resources/views/livewire/modals/login.blade.php @@ -1,8 +1,30 @@
- @if($can_use_2fa) - {{ __('lychee.U2F_LOGIN') }} - @endif +
diff --git a/resources/views/livewire/modules/profile/second-factor.blade.php b/resources/views/livewire/modules/profile/second-factor.blade.php index 8ae6b7f9e4e..bf0ce9c64ba 100644 --- a/resources/views/livewire/modules/profile/second-factor.blade.php +++ b/resources/views/livewire/modules/profile/second-factor.blade.php @@ -10,7 +10,7 @@ --}}

- + {{ __('lychee.U2F_CREDENTIALS') }}

diff --git a/resources/views/livewire/pages/profile.blade.php b/resources/views/livewire/pages/profile.blade.php index e6a06161004..f7171882e41 100644 --- a/resources/views/livewire/pages/profile.blade.php +++ b/resources/views/livewire/pages/profile.blade.php @@ -6,6 +6,7 @@
+ @if($are_notification_active)

diff --git a/routes/web-livewire.php b/routes/web-livewire.php index c470b0f2112..f1e4c07fb2d 100644 --- a/routes/web-livewire.php +++ b/routes/web-livewire.php @@ -2,6 +2,8 @@ namespace App\Livewire\Components\Pages; +use App\Enum\OauthProvidersType; +use App\Http\Controllers\Oauth; use App\Livewire\Components\Pages\Gallery\Album; use App\Livewire\Components\Pages\Gallery\Albums; use App\Livewire\Components\Pages\Gallery\Search; @@ -22,6 +24,11 @@ ->group(function () { Route::prefix(config('app.livewire') === true ? '' : 'livewire') ->group(function () { + // Oauth routes. + Route::get('/auth/{provider}/redirect', [Oauth::class, 'redirected'])->whereIn('provider', OauthProvidersType::values()); + Route::get('/auth/{provider}/authenticate', [Oauth::class, 'authenticate'])->name('oauth-authenticate')->whereIn('provider', OauthProvidersType::values()); + Route::get('/auth/{provider}/register', [Oauth::class, 'register'])->name('oauth-register')->whereIn('provider', OauthProvidersType::values()); + Route::get('/landing', Landing::class)->name('landing'); Route::get('/all-settings', AllSettings::class)->name('all-settings'); Route::get('/settings', Settings::class)->name('settings'); diff --git a/tailwind.config.js b/tailwind.config.js index 213434906d8..8d88fe68586 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -29,7 +29,7 @@ module.exports = { }, fontSize: { '3xs': ['0.55rem', '0.7rem'], - '2xs' : ['0.65rem', '0.8rem'], + '2xs': ['0.65rem', '0.8rem'], }, colors: { primary: { @@ -96,8 +96,8 @@ module.exports = { '100%': { 'background-position-x': '-100px' } }, moveUp: { - '0%': {'transform': 'translateY(80px)'}, - '100%': {'transform': 'translateY(0)'} + '0%': { 'transform': 'translateY(80px)' }, + '100%': { 'transform': 'translateY(0)' } }, zoomIn: { '0%': { @@ -188,7 +188,7 @@ module.exports = { 'landingIntroPopIn': 'popIn 2s forwards ease-in-out', 'landingIntroFadeOut': 'fadeOut 2s 2s forwards ease-in-out', // delayed by 2s 'landingSlidesPopIn': 'popIn 2s 3s forwards ease-in-out', // delayed by 2s - 'ladningAnimateDown': 'animateDown 1s 3.1s forwards ease-in-out', + 'ladningAnimateDown': 'animateDown 1s 3.1s forwards ease-in-out', 'ladningAnimateUp': 'animateUp 1s 3.1s forwards ease-in-out', 'delayedFadeOut': 'fadeOut 2s 2s forwards ease-in-out' }, @@ -200,5 +200,6 @@ module.exports = { }, }, }, - plugins: [], + plugins: [ + ], } \ No newline at end of file diff --git a/tests/Livewire/Forms/Profile/OauthTest.php b/tests/Livewire/Forms/Profile/OauthTest.php new file mode 100644 index 00000000000..0546adb26e9 --- /dev/null +++ b/tests/Livewire/Forms/Profile/OauthTest.php @@ -0,0 +1,81 @@ +userMayUpload1)->test($this->component) + ->assertViewIs('livewire.forms.profile.oauth') + ->assertSet('oauthData.amazon.isEnabled', false) + ->assertCount('oauthData', 2); + } + + public function testLoggedInOauthListingWithOauth(): void + { + Config::set('services.amazon.client_id', 'FAKE'); + Config::set('services.github.client_id', 'FAKE'); + + OauthCredential::create([ + 'provider' => OauthProvidersType::AMAZON, + 'token_id' => 'something', + 'user_id' => $this->userMayUpload1->id])->save(); + + Livewire::actingAs($this->userMayUpload1)->test($this->component) + ->assertViewIs('livewire.forms.profile.oauth') + ->assertSet('oauthData.amazon.isEnabled', true) + ->assertSet('oauthData.github.isEnabled', false) + ->assertCount('oauthData', 2); + } + + public function testLoggedInOauthListingWithOauthClear(): void + { + Config::set('services.amazon.client_id', 'FAKE'); + Config::set('services.github.client_id', 'FAKE'); + + OauthCredential::create([ + 'provider' => OauthProvidersType::AMAZON, + 'token_id' => 'something', + 'user_id' => $this->userMayUpload1->id])->save(); + + Livewire::actingAs($this->userMayUpload1)->test($this->component) + ->assertViewIs('livewire.forms.profile.oauth') + ->assertSet('oauthData.amazon.isEnabled', true) + ->assertSet('oauthData.github.isEnabled', false) + ->assertCount('oauthData', 2) + ->call('clear', 'amazon') + ->assertSet('oauthData.amazon.isEnabled', false); + } + + public function testLoggedOutOauthListingFails(): void + { + Config::set('services.amazon.client_id', 'FAKE'); + Config::set('services.github.client_id', 'FAKE'); + + Livewire::test($this->component) + ->assertForbidden(); + } +}