Page MenuHomePhorge

D4549.1775341596.diff
No OneTemporary

Authored By
Unknown
Size
28 KB
Referenced Files
None
Subscribers
None

D4549.1775341596.diff

diff --git a/src/app/Http/AuthSession.php b/src/app/Http/AuthSession.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/AuthSession.php
@@ -0,0 +1,326 @@
+<?php
+
+namespace App\Http;
+
+use Illuminate\Contracts\Session\Session as SessionInterface;
+use Illuminate\Support\Facades\Cache;
+
+class AuthSession implements SessionInterface
+{
+ /** @var string $prefix The cache key prefix */
+ protected string $prefix;
+
+ /** @var int $ttl The cache TTL in seconds */
+ protected int $ttl;
+
+ /** @var array $data The session data */
+ protected array $data = [];
+
+ /**
+ * Constructor.
+ *
+ * @param string $prefix
+ * @param int $ttl
+ */
+ public function __construct(string $prefix = '', int $ttl = 600)
+ {
+ $this->prefix = $prefix;
+ $this->ttl = $ttl;
+ }
+
+ /**
+ * Get the name of the session.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Set the name of the session.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setName($name)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Get the current session ID.
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Set the session ID.
+ *
+ * @param string $id
+ * @return void
+ */
+ public function setId($id)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Start the session, reading the data from a handler.
+ *
+ * @return bool
+ */
+ public function start()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Save the session data to storage.
+ *
+ * @return void
+ */
+ public function save($prefix = null)
+ {
+ foreach ($this->data as $key => $value) {
+ Cache::put($prefix . ':' . $key, $value, $this->ttl);
+ }
+ }
+
+ /**
+ * Get all of the session data.
+ *
+ * @return array
+ */
+ public function all()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Checks if a key exists.
+ *
+ * @param string|array $key
+ * @return bool
+ */
+ public function exists($key)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Checks if a key is present and not null.
+ *
+ * @param string|array $key
+ * @return bool
+ */
+ public function has($key)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Get an item from the session.
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ if (array_key_exists($key, $this->data)) {
+ return $this->data[$key];
+ }
+
+ if (strlen($this->prefix)) {
+ return Cache::get($this->prefix . ':' . $key) ?? $default;
+ }
+
+ return $default;
+ }
+
+ /**
+ * Get the value of a given key and then forget it.
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function pull($key, $default = null)
+ {
+ if (array_key_exists($key, $this->data)) {
+ $value = $this->data[$key];
+ unset($this->data[$key]);
+ return $value;
+ }
+
+ if (strlen($this->prefix)) {
+ return Cache::pull($this->prefix . ':' . $key) ?? $default;
+ }
+
+ return $default;
+ }
+
+ /**
+ * Put a key / value pair or array of key / value pairs in the session.
+ *
+ * @param string|array $key
+ * @param mixed $value
+ * @return void
+ */
+ public function put($key, $value = null)
+ {
+ $this->data[$key] = $value;
+ }
+
+ /**
+ * Get the CSRF token value.
+ *
+ * @return string
+ */
+ public function token()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Regenerate the CSRF token value.
+ *
+ * @return void
+ */
+ public function regenerateToken()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Remove an item from the session, returning its value.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function remove($key)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Remove one or many items from the session.
+ *
+ * @param string|array $keys
+ * @return void
+ */
+ public function forget($keys)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Remove all of the items from the session.
+ *
+ * @return void
+ */
+ public function flush()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Flush the session data and regenerate the ID.
+ *
+ * @return bool
+ */
+ public function invalidate()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Generate a new session identifier.
+ *
+ * @param bool $destroy
+ * @return bool
+ */
+ public function regenerate($destroy = false)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Generate a new session ID for the session.
+ *
+ * @param bool $destroy
+ * @return bool
+ */
+ public function migrate($destroy = false)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Determine if the session has been started.
+ *
+ * @return bool
+ */
+ public function isStarted()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Get the previous URL from the session.
+ *
+ * @return string|null
+ */
+ public function previousUrl()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Set the "previous" URL in the session.
+ *
+ * @param string $url
+ * @return void
+ */
+ public function setPreviousUrl($url)
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Get the session handler instance.
+ *
+ * @return \SessionHandlerInterface
+ */
+ public function getHandler()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Determine if the session handler needs a request.
+ *
+ * @return bool
+ */
+ public function handlerNeedsRequest()
+ {
+ throw new \Exception("Not implemented");
+ }
+
+ /**
+ * Set the request on the handler instance.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return void
+ */
+ public function setRequestOnHandler($request)
+ {
+ throw new \Exception("Not implemented");
+ }
+}
diff --git a/src/app/Http/Controllers/API/AuthController.php b/src/app/Http/Controllers/API/AuthController.php
--- a/src/app/Http/Controllers/API/AuthController.php
+++ b/src/app/Http/Controllers/API/AuthController.php
@@ -2,6 +2,7 @@
namespace App\Http\Controllers\API;
+use App\Http\AuthSession;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Http\Request;
@@ -9,6 +10,7 @@
use Illuminate\Support\Facades\Validator;
use Laravel\Passport\TokenRepository;
use Laravel\Passport\RefreshTokenRepository;
+use Laravel\Socialite\Facades\Socialite;
class AuthController extends Controller
{
@@ -127,6 +129,165 @@
]);
}
+ /**
+ * OAuth callback. Handles return from the provider's site.
+ *
+ * @param string $provider OAuth provider
+ *
+ * @return \Illuminate\View\View
+ */
+ public function oAuthCallback(string $provider)
+ {
+ $env = \App\Utils::uiEnv();
+ $request = request();
+
+ if ($error = $request->input('error')) {
+ \Log::warning(ucfirst($provider) . " OAuth error ({$error}): " . $request->input('error_description'));
+ // TODO: Display a localized error instead the OAuth error code from the provider
+ $env['userError'] = $error;
+ return view($env['view'])->with('env', $env);
+ }
+
+ // Use our own session handler. Normally we don't use sessions, but we need
+ // it here, as not all OAuth drivers support stateless(), e.g. Twitter v2
+ $request->setLaravelSession($session = new AuthSession($request->input('state')));
+
+ try {
+ \config(["services.{$provider}.redirect" => \App\Utils::serviceUrl("oauth/callback/{$provider}")]);
+
+ $oauth_user = Socialite::driver($provider)->user();
+ } catch (\Throwable $error) {
+ \Log::error($error); // FIXME: warning?
+ // TODO: A better (localized) error message
+ $env['userError'] = 'External authentication failed!';
+ return view($env['view'])->with('env', $env);
+ }
+
+ // \Log::info(print_r($oauth_user, true));
+
+ if ($login_property = \config("services.{$provider}.login_property")) {
+ $email = strtolower((string) $oauth_user->{$login_property});
+ if (!strpos($email, '@')) {
+ // FIXME: Note that an email address built this way is not a valid address
+ $email .= '@' . $provider;
+ }
+ } else {
+ $email = $oauth_user->getEmail();
+ if (!$email) {
+ // TODO: localized error
+ $env['userError'] = 'External authentication failed!';
+ return view($env['view'])->with('env', $env);
+ }
+ }
+
+ // TODO: Sanity check on $email: max length, chars, make sure it's a valid email?
+
+ $user = User::where('email', $email)->first();
+
+ if (!$user) {
+ $user = new User();
+ $user->email = $email;
+ $user->role = User::ROLE_GUEST;
+ }
+
+ if ($user->role !== User::ROLE_GUEST) {
+ \Log::warning(ucfirst($provider) . " OAuth error: Local user");
+ // TODO: A better (localized) error message
+ $env['userError'] = 'External authentication is not for local users!';
+ return view($env['view'])->with('env', $env);
+ }
+
+ // Set fake password so we can use logonResponse() below
+ // TODO: Find a way to create tokens without this hassle with a password
+ $user->password = $password = bin2hex(random_bytes(16));
+ $user->save();
+
+ $settings = [
+ 'oauth_provider' => $provider,
+ 'oauth_name' => $oauth_user->getName(),
+ 'oauth_id' => $oauth_user->getId(),
+ // 'oauth_token' => $oauth_user->token,
+ // 'oauth_refresh_token' => $oauth_user->refreshToken,
+ // 'oauth_expires_in' => $oauth_user->expiresIn,
+ ];
+
+ // $user->setSettings($settings);
+
+ // Log-in the user, get tokens
+ $response = self::logonResponse($user, $password);
+ $response = $response->getData(true);
+
+ // TODO: Direct the user to the page that was requested initially
+ $response['redirect'] = 'dashboard';
+
+ $env['userResponse'] = $response;
+
+ // The logonResponse() call above will reset the current request
+ // reference making an issue with assets, etc.
+ // We have to reset it to the original request we're handling here.
+ \app('url')->setRequest($request);
+
+ return view($env['view'])->with('env', $env);
+ }
+
+ /**
+ * OAuth configuration for the client-side.
+ *
+ * @return array List of available providers
+ */
+ public static function oAuthConfig(): array
+ {
+ $providers = [
+ 'facebook' => !empty(\config('services.facebook.client_id')),
+ 'github' => !empty(\config('services.github.client_id')),
+ 'google' => !empty(\config('services.google.client_id')),
+ 'twitter' => !empty(\config('services.twitter.client_id')),
+ // TODO: For providers like keycloak we'd need another approach. Consider:
+ // 1. What if you want to use more than one Keycloak-based service?
+ // 2. The button label should probably not be "Keycloak" in any case.
+ // 3. The button icon should be configurable
+ 'keycloak' => !empty(\config('services.keycloak.client_id')),
+ ];
+
+ return array_keys(array_filter($providers));
+ }
+
+ /**
+ * Creates an redirect URL to the OAuth provider's site.
+ *
+ * @param string $provider OAuth provider
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function oAuthRedirect(string $provider)
+ {
+ // Use our own session handler. Normally we don't use sessions, but we need
+ // it here, as not all OAuth drivers support stateless(), e.g. Twitter v2
+ request()->setLaravelSession($session = new AuthSession());
+
+ try {
+ \config(["services.{$provider}.redirect" => \App\Utils::serviceUrl("oauth/callback/{$provider}")]);
+
+ $url = Socialite::driver($provider)->redirect()->getTargetUrl();
+
+ // Save the session data to the cache
+ if (preg_match('/[?&]state=([^&]+)/', $url, $matches)) {
+ $session->save($matches[1]);
+ } else {
+ // error
+ }
+ } catch (\Throwable $error) {
+ \Log::error($error);
+ // TODO: Better error (localized) message
+ return response()->json(['status' => 'error', 'message' => "Redirect to OAuth provider failed"], 500);
+ }
+
+ return response()->json([
+ 'status' => 'success',
+ 'redirect' => $url,
+ ]);
+ }
+
/**
* Refresh a token.
*
diff --git a/src/app/Http/Controllers/API/V4/UsersController.php b/src/app/Http/Controllers/API/V4/UsersController.php
--- a/src/app/Http/Controllers/API/V4/UsersController.php
+++ b/src/app/Http/Controllers/API/V4/UsersController.php
@@ -164,6 +164,10 @@
]
);
+ if ($user->role === User::ROLE_GUEST) {
+ return [];
+ }
+
// Check if the user is a controller of his wallet
$isController = $user->canDelete($user);
@@ -351,17 +355,28 @@
{
$response = array_merge($user->toArray(), self::objectState($user));
- $wallet = $user->wallet();
-
- // IsLocked flag to lock the user to the Wallet page only
- $response['isLocked'] = (!$user->isActive() && ($plan = $wallet->plan()) && $plan->mode == Plan::MODE_MANDATE);
-
// Settings
$response['settings'] = [];
foreach ($user->settings()->whereIn('key', self::USER_SETTINGS)->get() as $item) {
$response['settings'][$item->key] = $item->value;
}
+ if ($user->role === User::ROLE_GUEST) {
+ // TODO: For now we set some required properties to empty/dummy value
+ // This will very likely change in the future
+ $response['wallets'] = [];
+ $response['accounts'] = [];
+ $response['statusInfo'] = ['skus' => [], 'process' => [], 'isDone' => true];
+ $response['isGuest'] = true;
+
+ return $response;
+ }
+
+ $wallet = $user->wallet();
+
+ // IsLocked flag to lock the user to the Wallet page only
+ $response['isLocked'] = (!$user->isActive() && ($plan = $wallet->plan()) && $plan->mode == Plan::MODE_MANDATE);
+
// Status info
$response['statusInfo'] = self::statusInfo($user);
diff --git a/src/app/Http/Kernel.php b/src/app/Http/Kernel.php
--- a/src/app/Http/Kernel.php
+++ b/src/app/Http/Kernel.php
@@ -49,6 +49,10 @@
// 'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
+
+ 'session' => [
+ \Illuminate\Session\Middleware\StartSession::class,
+ ],
];
/**
diff --git a/src/app/Observers/UserObserver.php b/src/app/Observers/UserObserver.php
--- a/src/app/Observers/UserObserver.php
+++ b/src/app/Observers/UserObserver.php
@@ -61,6 +61,11 @@
// Note: This is a single multi-insert query
$user->settings()->insert(array_values($settings));
+ // Don't need wallet, nor jobs for an external user
+ if ($user->role === User::ROLE_GUEST) {
+ return;
+ }
+
$user->wallets()->create();
// Create user record in the backend (LDAP and IMAP)
diff --git a/src/app/Providers/EventServiceProvider.php b/src/app/Providers/EventServiceProvider.php
--- a/src/app/Providers/EventServiceProvider.php
+++ b/src/app/Providers/EventServiceProvider.php
@@ -2,9 +2,6 @@
namespace App\Providers;
-use Illuminate\Support\Facades\Event;
-use Illuminate\Auth\Events\Registered;
-use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
@@ -12,11 +9,12 @@
/**
* The event listener mappings for the application.
*
- * @var array<class-string, array<int, class-string>>
+ * @var array<class-string, array<int, string>>
*/
protected $listen = [
- Registered::class => [
- SendEmailVerificationNotification::class,
+ \SocialiteProviders\Manager\SocialiteWasCalled::class => [
+ // Extra providers
+ \SocialiteProviders\Keycloak\KeycloakExtendSocialite::class . '@handle',
],
];
diff --git a/src/app/User.php b/src/app/User.php
--- a/src/app/User.php
+++ b/src/app/User.php
@@ -61,6 +61,10 @@
// a restricted user
public const STATUS_RESTRICTED = 1 << 7;
+ public const ROLE_ADMIN = 'admin';
+ public const ROLE_GUEST = 'guest';
+ public const ROLE_RESELLER = 'reseller';
+
/** @var int The allowed states for this object used in StatusPropertyTrait */
private int $allowed_states = self::STATUS_NEW |
self::STATUS_ACTIVE |
diff --git a/src/app/Utils.php b/src/app/Utils.php
--- a/src/app/Utils.php
+++ b/src/app/Utils.php
@@ -486,6 +486,8 @@
$env['languages'] = \App\Http\Controllers\ContentController::locales();
$env['menu'] = \App\Http\Controllers\ContentController::menu();
+ $env['oauthProviders'] = \App\Http\Controllers\API\AuthController::oAuthConfig();
+
return $env;
}
diff --git a/src/composer.json b/src/composer.json
--- a/src/composer.json
+++ b/src/composer.json
@@ -25,17 +25,19 @@
"laravel/horizon": "^5.9",
"laravel/octane": "^2.0",
"laravel/passport": "^11.3",
+ "laravel/socialite": "^5.8",
"laravel/tinker": "^2.8",
+ "lcobucci/jwt": "^5.0",
"league/flysystem-aws-s3-v3": "^3.0",
"mlocati/spf-lib": "^3.1",
"mollie/laravel-mollie": "^2.22",
"pear/crypt_gpg": "^1.6.6",
"predis/predis": "^2.0",
"sabre/vobject": "^4.5",
+ "socialiteproviders/keycloak": "^5.3",
"spatie/laravel-translatable": "^6.5",
"spomky-labs/otphp": "~10.0.0",
- "stripe/stripe-php": "^10.7",
- "lcobucci/jwt": "^5.0"
+ "stripe/stripe-php": "^10.7"
},
"require-dev": {
"code-lts/doctum": "^5.5.1",
diff --git a/src/config/services.php b/src/config/services.php
--- a/src/config/services.php
+++ b/src/config/services.php
@@ -34,6 +34,17 @@
'secret' => env('SPARKPOST_SECRET'),
],
+ 'openexchangerates' => [
+ 'api_key' => env('OPENEXCHANGERATES_API_KEY', null),
+ ],
+
+
+ /*
+ |--------------------------------------------------------------------------
+ | Payment Providers
+ |--------------------------------------------------------------------------
+ */
+
'payment_provider' => env('PAYMENT_PROVIDER', 'mollie'),
'mollie' => [
@@ -52,9 +63,11 @@
'api_verify_tls' => env('COINBASE_VERIFY_TLS', true),
],
- 'openexchangerates' => [
- 'api_key' => env('OPENEXCHANGERATES_API_KEY', null),
- ],
+ /*
+ |--------------------------------------------------------------------------
+ | Kolab Services
+ |--------------------------------------------------------------------------
+ */
'dav' => [
'uri' => env('DAV_URI', 'https://proxy/'),
@@ -70,5 +83,43 @@
'webmail' => [
'uri' => env('WEBMAIL_URI', 'http://roundcube/roundcubemail/'),
- ]
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | OAuth Providers
+ |--------------------------------------------------------------------------
+ */
+
+ 'facebook' => [
+ 'client_id' => env('FACEBOOK_CLIENT_ID'),
+ 'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
+ 'login_property' => 'nickname',
+ ],
+
+ 'github' => [
+ 'client_id' => env('GITHUB_CLIENT_ID'),
+ 'client_secret' => env('GITHUB_CLIENT_SECRET'),
+ 'login_property' => 'nickname',
+ ],
+
+ 'google' => [
+ 'client_id' => env('GOOGLE_CLIENT_ID'),
+ 'client_secret' => env('GOOGLE_CLIENT_SECRET'),
+ ],
+
+ 'keycloak' => [
+ 'client_id' => env('KEYCLOAK_CLIENT_ID'),
+ 'client_secret' => env('KEYCLOAK_CLIENT_SECRET'),
+ 'base_url' => env('KEYCLOAK_BASE_URL'), // Specify your keycloak server URL here
+ 'realms' => env('KEYCLOAK_REALM') // Specify your keycloak realm
+ ],
+
+ 'twitter' => [
+ 'client_id' => env('TWITTER_CLIENT_ID'),
+ 'client_secret' => env('TWITTER_CLIENT_SECRET'),
+ 'oauth' => 2, // enable the twitter-oauth-2 driver
+ 'login_property' => 'nickname',
+ ],
+
];
diff --git a/src/resources/vue/Dashboard.vue b/src/resources/vue/Dashboard.vue
--- a/src/resources/vue/Dashboard.vue
+++ b/src/resources/vue/Dashboard.vue
@@ -3,7 +3,7 @@
<status-component :status="status" @status-update="statusUpdate"></status-component>
<div id="dashboard-nav">
- <router-link class="card link-settings" :to="{ name: 'settings' }">
+ <router-link v-if="!$root.authInfo.isGuest" class="card link-settings" :to="{ name: 'settings' }">
<svg-icon icon="user-gear"></svg-icon><span>{{ $t('dashboard.myaccount') }}</span>
</router-link>
<router-link v-if="status.enableDomains" class="card link-domains" :to="{ name: 'domains' }">
@@ -37,7 +37,7 @@
<router-link v-if="status.enableSettings" class="card link-policies" :to="{ name: 'policies' }">
<svg-icon icon="shield-halved"></svg-icon><span>{{ $t('dashboard.policies') }}</span>
</router-link>
- <a v-if="webmailURL" class="card link-webmail" :href="webmailURL">
+ <a v-if="webmailURL && !$root.authInfo.isGuest" class="card link-webmail" :href="webmailURL">
<svg-icon icon="envelope"></svg-icon><span>{{ $t('dashboard.webmail') }}</span>
</a>
<router-link v-if="status.enableCompanionapps" class="card link-companionapp" :to="{ name: 'companions' }">
diff --git a/src/resources/vue/Login.vue b/src/resources/vue/Login.vue
--- a/src/resources/vue/Login.vue
+++ b/src/resources/vue/Login.vue
@@ -40,11 +40,13 @@
<router-link v-if="$root.isUser && $root.hasRoute('password-reset')" :to="{ name: 'password-reset' }" id="forgot-password">{{ $t('login.forgot_password') }}</router-link>
<a v-if="webmailURL && $root.isUser" :href="webmailURL" id="webmail">{{ $t('login.webmail') }}</a>
</div>
+ <o-auth-providers id="logon-oauth" class="mt-4"></o-auth-providers>
</div>
</template>
<script>
import { library } from '@fortawesome/fontawesome-svg-core'
+ import OAuthProviders from './Widgets/OAuthProviders'
library.add(
require('@fortawesome/free-solid-svg-icons/faKey').definition,
@@ -53,6 +55,9 @@
)
export default {
+ components: {
+ OAuthProviders
+ },
props: {
dashboard: { type: Boolean, default: true }
},
diff --git a/src/resources/vue/Page.vue b/src/resources/vue/Page.vue
--- a/src/resources/vue/Page.vue
+++ b/src/resources/vue/Page.vue
@@ -10,7 +10,27 @@
}
},
mounted() {
- let page = this.$root.pageName()
+ const page = this.$root.pageName()
+ const path = this.$route.path
+
+ // Returning from a OAuth provider site
+ if (path && path.startsWith('/oauth/callback')) {
+ if (window.config.userResponse) {
+ // User successfully OAuth-enticated
+ this.$root.loginUser(window.config.userResponse)
+ return
+ } else {
+ // Error
+ if (window.config.userError) {
+ this.content = '<p class="alert alert-danger">' + window.config.userError + '</p>'
+ delete window.config.userError
+ }
+
+ // TODO: Possibly go to the logon page or the original page
+ }
+
+ return
+ }
// Redirect / to /dashboard, if root page is not defined
if (page == '404' && this.$route.path == '/') {
@@ -22,7 +42,12 @@
.then(response => {
this.content = response.data
})
- .catch(this.$root.errorHandler)
+ .catch(error => {
+ // Skip the error handler if the route changed in meantime i.e. we're already on another page
+ if (this.$route.path == path) {
+ this.$root.errorHandler(error)
+ }
+ })
},
methods: {
clickHandler(event) {
diff --git a/src/resources/vue/Widgets/OAuthProviders.vue b/src/resources/vue/Widgets/OAuthProviders.vue
new file mode 100644
--- /dev/null
+++ b/src/resources/vue/Widgets/OAuthProviders.vue
@@ -0,0 +1,57 @@
+<template>
+ <div v-if="oauthProviders.length" class="oauth-providers">
+ <btn v-for="provider in oauthProviders" :key="provider" @click="click(provider)"
+ :class="`btn-outline-secondary oauth-${provider}`"
+ :icon="['fab', providerName(provider)]"
+ :is-loading="isLoading == provider"
+ >
+ {{ providerName(provider, true) }}
+ </btn>
+ </div>
+</template>
+
+<script>
+ import { library } from '@fortawesome/fontawesome-svg-core'
+
+ library.add(
+ require('@fortawesome/free-brands-svg-icons/faFacebook').definition,
+ require('@fortawesome/free-brands-svg-icons/faGithub').definition,
+ require('@fortawesome/free-brands-svg-icons/faGoogle').definition,
+ require('@fortawesome/free-brands-svg-icons/faTwitter').definition
+ )
+
+ export default {
+ data() {
+ return {
+ isLoading: null,
+ oauthProviders: window.config.oauthProviders
+ }
+ },
+ methods: {
+ click(provider) {
+ this.isLoading = provider
+
+ axios.post('/api/auth/oauth/' + provider)
+ .then(response => {
+ location.href = response.data.redirect
+
+ // Browser needs some time to jump to load the requested page
+ // let's wait a few seconds before we hide the loading icon
+ setTimeout(() => { this.isLoading = null }, 3000)
+ })
+ .catch(() => {
+ this.isLoading = null
+ })
+ },
+ providerName(provider, uc) {
+ let name = provider.split('-')[0]
+
+ if (uc) {
+ name = name.charAt(0).toUpperCase() + name.slice(1)
+ }
+
+ return name
+ }
+ }
+ }
+</script>
diff --git a/src/routes/api.php b/src/routes/api.php
--- a/src/routes/api.php
+++ b/src/routes/api.php
@@ -22,6 +22,7 @@
],
function () {
Route::post('login', [API\AuthController::class, 'login']);
+ Route::post('oauth/{provider}', API\AuthController::class . '@oAuthRedirect');
Route::group(
['middleware' => 'auth:api'],
diff --git a/src/routes/web.php b/src/routes/web.php
--- a/src/routes/web.php
+++ b/src/routes/web.php
@@ -54,5 +54,7 @@
'as' => 'tokens.destroy',
]);
});
+
+ Route::get('callback/{provider}', Controllers\API\AuthController::class . '@oAuthCallback');
}
);

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 4, 10:26 PM (13 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18831425
Default Alt Text
D4549.1775341596.diff (28 KB)

Event Timeline