7a137 google auth (#520)
* google oauth * fix lint * cleanup debug * Oauth changes, alert message, email validation * fix exception and inline return condition * fix google oauth * UI fixes * fix provider user --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
5049ba7fb1
commit
7ac8503201
|
|
@ -84,5 +84,6 @@ CADDY_AUTHORIZED_IPS=
|
||||||
GOOGLE_CLIENT_ID=
|
GOOGLE_CLIENT_ID=
|
||||||
GOOGLE_CLIENT_SECRET=
|
GOOGLE_CLIENT_SECRET=
|
||||||
GOOGLE_REDIRECT_URL=http://localhost:3000/settings/connections/callback/google
|
GOOGLE_REDIRECT_URL=http://localhost:3000/settings/connections/callback/google
|
||||||
|
GOOGLE_AUTH_REDIRECT_URL=http://localhost:3000/oauth/google/callback
|
||||||
|
|
||||||
GOOGLE_FONTS_API_KEY=
|
GOOGLE_FONTS_API_KEY=
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Exceptions\EmailTakenException;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Integrations\OAuth\OAuthProviderService;
|
||||||
use App\Models\OAuthProvider;
|
use App\Models\OAuthProvider;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\Workspace;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Laravel\Socialite\Facades\Socialite;
|
|
||||||
|
|
||||||
class OAuthController extends Controller
|
class OAuthController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -31,11 +31,11 @@ class OAuthController extends Controller
|
||||||
* @param string $provider
|
* @param string $provider
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function redirect($provider)
|
public function redirect(OAuthProviderService $provider)
|
||||||
{
|
{
|
||||||
return [
|
return response()->json([
|
||||||
'url' => Socialite::driver($provider)->stateless()->redirect()->getTargetUrl(),
|
'url' => $provider->getDriver()->setRedirectUrl(config('services.google.auth_redirect'))->getRedirectUrl()
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -44,69 +44,103 @@ class OAuthController extends Controller
|
||||||
* @param string $driver
|
* @param string $driver
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function handleCallback($provider)
|
public function handleCallback(OAuthProviderService $provider)
|
||||||
{
|
{
|
||||||
$user = Socialite::driver($provider)->stateless()->user();
|
try {
|
||||||
$user = $this->findOrCreateUser($provider, $user);
|
$driverUser = $provider->getDriver()->setRedirectUrl(config('services.google.auth_redirect'))->getUser();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->error([
|
||||||
|
"message" => "OAuth service failed to authenticate: " . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$user = $this->findOrCreateUser($provider, $driverUser);
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
return $this->error([
|
||||||
|
"message" => "User not found."
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->has_registered) {
|
||||||
|
return $this->error([
|
||||||
|
"message" => "This email is already registered. Please sign in with your password."
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$this->guard()->setToken(
|
$this->guard()->setToken(
|
||||||
$token = $this->guard()->login($user)
|
$token = $this->guard()->login($user)
|
||||||
);
|
);
|
||||||
|
|
||||||
return view('oauth/callback', [
|
return response()->json([
|
||||||
'token' => $token,
|
'token' => $token,
|
||||||
'token_type' => 'bearer',
|
'token_type' => 'bearer',
|
||||||
'expires_in' => $this->guard()->getPayload()->get('exp') - time(),
|
'expires_in' => $this->guard()->getPayload()->get('exp') - time(),
|
||||||
|
'new_user' => $user->new_user
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $provider
|
* @p aram \Laravel\Socialite\Contracts\User $socialiteUser
|
||||||
* @param \Laravel\Socialite\Contracts\User $sUser
|
* @return \App\Models\User | null
|
||||||
* @return \App\Models\User
|
|
||||||
*/
|
*/
|
||||||
protected function findOrCreateUser($provider, $user)
|
protected function findOrCreateUser($provider, $socialiteUser)
|
||||||
{
|
{
|
||||||
$oauthProvider = OAuthProvider::where('provider', $provider)
|
$oauthProvider = OAuthProvider::where('provider', $provider)
|
||||||
->where('provider_user_id', $user->getId())
|
->where('provider_user_id', $socialiteUser->getId())
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($oauthProvider) {
|
if ($oauthProvider) {
|
||||||
$oauthProvider->update([
|
$oauthProvider->update([
|
||||||
'access_token' => $user->token,
|
'access_token' => $socialiteUser->token,
|
||||||
'refresh_token' => $user->refreshToken,
|
'refresh_token' => $socialiteUser->refreshToken,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $oauthProvider->user;
|
return $oauthProvider->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (User::where('email', $user->getEmail())->exists()) {
|
|
||||||
throw new EmailTakenException();
|
if (!$provider->getDriver()->canCreateUser()) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->createUser($provider, $user);
|
$email = strtolower($socialiteUser->getEmail());
|
||||||
|
$user = User::whereEmail($email)->first();
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
$user->has_registered = true;
|
||||||
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $provider
|
|
||||||
* @param \Laravel\Socialite\Contracts\User $sUser
|
|
||||||
* @return \App\Models\User
|
|
||||||
*/
|
|
||||||
protected function createUser($provider, $sUser)
|
|
||||||
{
|
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
'name' => $sUser->getName(),
|
'name' => $socialiteUser->getName(),
|
||||||
'email' => $sUser->getEmail(),
|
'email' => $email,
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user->oauthProviders()->create([
|
// Create and sync workspace
|
||||||
'provider' => $provider,
|
$workspace = Workspace::create([
|
||||||
'provider_user_id' => $sUser->getId(),
|
'name' => 'My Workspace',
|
||||||
'access_token' => $sUser->token,
|
'icon' => '🧪',
|
||||||
'refresh_token' => $sUser->refreshToken,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$user->workspaces()->sync([
|
||||||
|
$workspace->id => [
|
||||||
|
'role' => User::ROLE_ADMIN,
|
||||||
|
],
|
||||||
|
], false);
|
||||||
|
$user->new_user = true;
|
||||||
|
|
||||||
|
OAuthProvider::create(
|
||||||
|
[
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'provider' => $provider,
|
||||||
|
'provider_user_id' => $socialiteUser->getId(),
|
||||||
|
'access_token' => $socialiteUser->token,
|
||||||
|
'refresh_token' => $socialiteUser->refreshToken,
|
||||||
|
'name' => $socialiteUser->getName(),
|
||||||
|
'email' => $socialiteUser->getEmail(),
|
||||||
|
]
|
||||||
|
);
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ class FontsController extends Controller
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
return \Cache::remember('google_fonts', 60 * 60, function () {
|
return \Cache::remember('google_fonts', 60 * 60, function () {
|
||||||
$url = "https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key=" . config('services.google_fonts_api_key');
|
$url = "https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key=" . config('services.google.fonts_api_key');
|
||||||
$response = Http::get($url);
|
$response = Http::get($url);
|
||||||
if ($response->successful()) {
|
if ($response->successful()) {
|
||||||
$fonts = collect($response->json()['items'])->filter(function ($font) {
|
$fonts = collect($response->json()['items'])->filter(function ($font) {
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,7 @@ use Laravel\Socialite\Contracts\User;
|
||||||
interface OAuthDriver
|
interface OAuthDriver
|
||||||
{
|
{
|
||||||
public function getRedirectUrl(): string;
|
public function getRedirectUrl(): string;
|
||||||
|
public function setRedirectUrl($url): self;
|
||||||
public function getUser(): User;
|
public function getUser(): User;
|
||||||
|
public function canCreateUser(): bool;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ use Laravel\Socialite\Two\GoogleProvider;
|
||||||
|
|
||||||
class OAuthGoogleDriver implements OAuthDriver
|
class OAuthGoogleDriver implements OAuthDriver
|
||||||
{
|
{
|
||||||
|
private ?string $redirectUrl = null;
|
||||||
|
|
||||||
protected GoogleProvider $provider;
|
protected GoogleProvider $provider;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
|
|
@ -22,6 +24,7 @@ class OAuthGoogleDriver implements OAuthDriver
|
||||||
return $this->provider
|
return $this->provider
|
||||||
->scopes([Sheets::DRIVE_FILE])
|
->scopes([Sheets::DRIVE_FILE])
|
||||||
->stateless()
|
->stateless()
|
||||||
|
->redirectUrl($this->redirectUrl ?? config('services.google.redirect'))
|
||||||
->with([
|
->with([
|
||||||
'access_type' => 'offline',
|
'access_type' => 'offline',
|
||||||
'prompt' => 'consent select_account'
|
'prompt' => 'consent select_account'
|
||||||
|
|
@ -34,6 +37,19 @@ class OAuthGoogleDriver implements OAuthDriver
|
||||||
{
|
{
|
||||||
return $this->provider
|
return $this->provider
|
||||||
->stateless()
|
->stateless()
|
||||||
|
->redirectUrl($this->redirectUrl ?? config('services.google.redirect'))
|
||||||
->user();
|
->user();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function canCreateUser(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRedirectUrl($url): OAuthDriver
|
||||||
|
{
|
||||||
|
$this->redirectUrl = $url;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Remember Me -->
|
<!-- Remember Me -->
|
||||||
<div class="relative flex items-center my-5">
|
<div class="relative flex items-start mt-5">
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
v-model="remember"
|
v-model="remember"
|
||||||
class="w-full md:w-1/2"
|
class="w-full md:w-1/2"
|
||||||
|
|
@ -52,15 +52,28 @@
|
||||||
|
|
||||||
<!-- Submit Button -->
|
<!-- Submit Button -->
|
||||||
<v-button
|
<v-button
|
||||||
dusk="btn_login"
|
class="w-full flex"
|
||||||
:loading="form.busy || loading"
|
:loading="form.busy || loading"
|
||||||
>
|
>
|
||||||
Log in to continue
|
Log in to continue
|
||||||
</v-button>
|
</v-button>
|
||||||
|
|
||||||
|
<v-button
|
||||||
|
native-type="button"
|
||||||
|
color="white"
|
||||||
|
class="space-x-4 mt-4 flex items-center w-full"
|
||||||
|
:loading="false"
|
||||||
|
@click.prevent="signInwithGoogle"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="devicon:google"
|
||||||
|
class="w-4 h-4 -mt-1"
|
||||||
|
/>
|
||||||
|
<span class="mx-2">Sign in with Google</span>
|
||||||
|
</v-button>
|
||||||
<p
|
<p
|
||||||
v-if="!appStore.selfHosted"
|
v-if="!appStore.selfHosted"
|
||||||
class="text-gray-500 mt-4"
|
class="text-gray-500 text-sm text-center mt-4"
|
||||||
>
|
>
|
||||||
Don't have an account?
|
Don't have an account?
|
||||||
<a
|
<a
|
||||||
|
|
@ -106,6 +119,7 @@ export default {
|
||||||
authStore: useAuthStore(),
|
authStore: useAuthStore(),
|
||||||
formsStore: useFormsStore(),
|
formsStore: useFormsStore(),
|
||||||
workspaceStore: useWorkspacesStore(),
|
workspaceStore: useWorkspacesStore(),
|
||||||
|
providersStore: useOAuthProvidersStore()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -119,6 +133,8 @@ export default {
|
||||||
showForgotModal: false,
|
showForgotModal: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
computed: {},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
login() {
|
login() {
|
||||||
// Submit the form.
|
// Submit the form.
|
||||||
|
|
@ -170,6 +186,9 @@ export default {
|
||||||
router.push({ name: "home" })
|
router.push({ name: "home" })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
signInwithGoogle() {
|
||||||
|
this.providersStore.guestConnect('google', true)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<form
|
<form
|
||||||
class="mt-4"
|
|
||||||
@submit.prevent="register"
|
@submit.prevent="register"
|
||||||
@keydown="form.onKeydown($event)"
|
@keydown="form.onKeydown($event)"
|
||||||
>
|
>
|
||||||
|
|
@ -56,6 +55,7 @@
|
||||||
<checkbox-input
|
<checkbox-input
|
||||||
:form="form"
|
:form="form"
|
||||||
name="agree_terms"
|
name="agree_terms"
|
||||||
|
class="mb-3"
|
||||||
:required="true"
|
:required="true"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="{ name: 'terms-conditions' }"
|
:to="{ name: 'terms-conditions' }"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
class="underline"
|
||||||
>
|
>
|
||||||
Terms and conditions
|
Terms and conditions
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
@ -70,6 +71,7 @@
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="{ name: 'privacy-policy' }"
|
:to="{ name: 'privacy-policy' }"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
class="underline"
|
||||||
>
|
>
|
||||||
Privacy policy
|
Privacy policy
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
@ -78,16 +80,33 @@
|
||||||
</checkbox-input>
|
</checkbox-input>
|
||||||
|
|
||||||
<!-- Submit Button -->
|
<!-- Submit Button -->
|
||||||
<v-button :loading="form.busy">
|
<v-button
|
||||||
Create an account
|
class="w-full mt-4"
|
||||||
|
:loading="form.busy"
|
||||||
|
>
|
||||||
|
Create account
|
||||||
</v-button>
|
</v-button>
|
||||||
|
|
||||||
<p class="text-gray-500 mt-4">
|
<p class="text-gray-600/50 text-sm text-center my-4">
|
||||||
|
Or
|
||||||
|
</p>
|
||||||
|
<v-button
|
||||||
|
native-type="buttom"
|
||||||
|
color="white"
|
||||||
|
class="space-x-4 flex items-center w-full"
|
||||||
|
:loading="false"
|
||||||
|
@click.prevent="signInwithGoogle"
|
||||||
|
>
|
||||||
|
<Icon name="devicon:google" />
|
||||||
|
<span class="mx-2">Sign in with Google</span>
|
||||||
|
</v-button>
|
||||||
|
|
||||||
|
<p class="text-gray-500 mt-4 text-sm text-center">
|
||||||
Already have an account?
|
Already have an account?
|
||||||
<a
|
<a
|
||||||
v-if="isQuick"
|
v-if="isQuick"
|
||||||
href="#"
|
href="#"
|
||||||
class="font-semibold ml-1"
|
class="font-medium ml-1"
|
||||||
@click.prevent="$emit('openLogin')"
|
@click.prevent="$emit('openLogin')"
|
||||||
>Log In</a>
|
>Log In</a>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
|
|
@ -123,6 +142,7 @@ export default {
|
||||||
authStore: useAuthStore(),
|
authStore: useAuthStore(),
|
||||||
formsStore: useFormsStore(),
|
formsStore: useFormsStore(),
|
||||||
workspaceStore: useWorkspacesStore(),
|
workspaceStore: useWorkspacesStore(),
|
||||||
|
providersStore: useOAuthProvidersStore(),
|
||||||
logEvent: useAmplitude().logEvent,
|
logEvent: useAmplitude().logEvent,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -241,6 +261,9 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
signInwithGoogle() {
|
||||||
|
this.providersStore.guestConnect('google', true)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ export default defineNuxtRouteMiddleware((from, to, next) => {
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
if (runtimeConfig.public?.selfHosted) {
|
if (runtimeConfig.public?.selfHosted) {
|
||||||
console.log('in')
|
|
||||||
if (from.name === 'register' && route.query?.email && route.query?.invite_token) {
|
if (from.name === 'register' && route.query?.email && route.query?.invite_token) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@
|
||||||
<h2 class="font-semibold text-2xl">
|
<h2 class="font-semibold text-2xl">
|
||||||
Login to OpnForm
|
Login to OpnForm
|
||||||
</h2>
|
</h2>
|
||||||
<small>Welcome back! Please enter your details.</small>
|
<p class="text-sm text-gray-500">
|
||||||
|
Welcome back! Please enter your details.
|
||||||
|
</p>
|
||||||
|
|
||||||
<login-form />
|
<login-form />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-grow mt-6 mb-10">
|
||||||
|
<div class="w-full md:w-2/3 md:mx-auto md:max-w-md px-4">
|
||||||
|
<div class="m-10" v-if="loading">
|
||||||
|
<h3 class="my-6 text-center">
|
||||||
|
Please wait...
|
||||||
|
</h3>
|
||||||
|
<Loader class="h-6 w-6 mx-auto m-10" />
|
||||||
|
</div>
|
||||||
|
<div class="m-6 flex flex-col items-center space-y-4" v-else>
|
||||||
|
<p class="text-center"> Unable to sign it at the moment. </p>
|
||||||
|
<v-button
|
||||||
|
:to="{ name: 'login' }"
|
||||||
|
>Back to login</v-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const workspacesStore = useWorkspacesStore()
|
||||||
|
const formsStore = useFormsStore()
|
||||||
|
const logEvent = useAmplitude().logEvent
|
||||||
|
const loading = ref(true);
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
alias: [
|
||||||
|
'/oauth/:provider/callback'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleCallback() {
|
||||||
|
|
||||||
|
const code = route.query.code
|
||||||
|
const provider = route.params.provider
|
||||||
|
opnFetch(`/oauth/${provider}/callback`, {
|
||||||
|
method: 'POST',
|
||||||
|
params: {
|
||||||
|
code
|
||||||
|
}
|
||||||
|
}).then(async (data) => {
|
||||||
|
authStore.setToken(data.token)
|
||||||
|
const [userDataResponse, workspacesResponse] = await Promise.all([
|
||||||
|
opnFetch("user"),
|
||||||
|
fetchAllWorkspaces(),
|
||||||
|
])
|
||||||
|
authStore.setUser(userDataResponse)
|
||||||
|
workspacesStore.set(workspacesResponse.data.value)
|
||||||
|
|
||||||
|
// Load forms
|
||||||
|
formsStore.loadAll(workspacesStore.currentId)
|
||||||
|
if (!data.new_user) {
|
||||||
|
logEvent("login", { source: provider })
|
||||||
|
try {
|
||||||
|
useGtm().trackEvent({
|
||||||
|
event: 'login',
|
||||||
|
source: provider
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
router.push({ name: "home" })
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
logEvent("register", { source: provider })
|
||||||
|
router.push({ name: "forms-create" })
|
||||||
|
useAlert().success("Success! You're now registered with your Google account! Welcome to OpnForm.")
|
||||||
|
try {
|
||||||
|
useGtm().trackEvent({
|
||||||
|
event: 'register',
|
||||||
|
source: provider
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
useAlert().error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
useAlert().error(error.response._data.message)
|
||||||
|
loading.value = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
handleCallback()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
@ -10,7 +10,9 @@
|
||||||
<h2 class="font-semibold text-2xl">
|
<h2 class="font-semibold text-2xl">
|
||||||
Create an account
|
Create an account
|
||||||
</h2>
|
</h2>
|
||||||
<small>Sign up in less than 2 minutes.</small>
|
<p class="text-gray-500 text-sm">
|
||||||
|
Sign up in less than 2 minutes.
|
||||||
|
</p>
|
||||||
<template v-if="!appStore.selfHosted || isInvited">
|
<template v-if="!appStore.selfHosted || isInvited">
|
||||||
<register-form />
|
<register-form />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,33 @@ export const useOAuthProvidersStore = defineStore("oauth_providers", () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const guestConnect = (service, redirect = false) => {
|
||||||
|
contentStore.resetState()
|
||||||
|
contentStore.startLoading()
|
||||||
|
|
||||||
|
const intention = new URL(window.location.href).pathname
|
||||||
|
|
||||||
|
opnFetch(`/oauth/connect/${service}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
...redirect ? { intention } : {},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
window.location.href = data.url
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
try {
|
||||||
|
alert.error(error.data.message)
|
||||||
|
} catch (e) {
|
||||||
|
alert.error("An error occurred while connecting an account")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
contentStore.stopLoading()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const providers = computed(() => contentStore.getAll.value)
|
const providers = computed(() => contentStore.getAll.value)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -69,6 +96,7 @@ export const useOAuthProvidersStore = defineStore("oauth_providers", () => {
|
||||||
getService,
|
getService,
|
||||||
fetchOAuthProviders,
|
fetchOAuthProviders,
|
||||||
providers,
|
providers,
|
||||||
connect
|
connect,
|
||||||
|
guestConnect
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,9 @@ return [
|
||||||
'client_id' => env('GOOGLE_CLIENT_ID'),
|
'client_id' => env('GOOGLE_CLIENT_ID'),
|
||||||
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
||||||
'redirect' => env('GOOGLE_REDIRECT_URL'),
|
'redirect' => env('GOOGLE_REDIRECT_URL'),
|
||||||
|
'auth_redirect' => env('GOOGLE_AUTH_REDIRECT_URL'),
|
||||||
|
|
||||||
|
'fonts_api_key' => env('GOOGLE_FONTS_API_KEY'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'google_fonts_api_key' => env('GOOGLE_FONTS_API_KEY'),
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -269,8 +269,9 @@ Route::group(['middleware' => 'guest:api'], function () {
|
||||||
Route::post('email/verify/{user}', [VerificationController::class, 'verify'])->name('verification.verify');
|
Route::post('email/verify/{user}', [VerificationController::class, 'verify'])->name('verification.verify');
|
||||||
Route::post('email/resend', [VerificationController::class, 'resend']);
|
Route::post('email/resend', [VerificationController::class, 'resend']);
|
||||||
|
|
||||||
Route::post('oauth/{driver}', [OAuthController::class, 'redirect']);
|
Route::post('oauth/{provider}', [OAuthController::class, 'redirect']);
|
||||||
Route::get('oauth/{driver}/callback', [OAuthController::class, 'handleCallback'])->name('oauth.callback');
|
Route::post('oauth/connect/{provider}', [OAuthController::class, 'redirect'])->name('oauth.redirect');
|
||||||
|
Route::post('oauth/{provider}/callback', [OAuthController::class, 'handleCallback'])->name('oauth.callback');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group(['prefix' => 'appsumo'], function () {
|
Route::group(['prefix' => 'appsumo'], function () {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue