Skip to content

Commit

Permalink
add recaptcha to gql
Browse files Browse the repository at this point in the history
  • Loading branch information
zlayine committed Apr 17, 2024
1 parent 437198b commit c9f6e61
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 19 deletions.
9 changes: 6 additions & 3 deletions resources/js/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ export class AuthApi {
return ApiService.sendPlatformRequest(data, '/multi-tenant');
}

static async login(email: string, password: string) {
static async login(email: string, password: string, recaptcha?: string) {
const data = {
query: queries.Login,
variables: {
email,
password,
recaptcha,
},
};

Expand All @@ -35,23 +36,25 @@ export class AuthApi {
return AuthApi.sendPlatfromRequest(data);
}

static async register(email: string, password: string) {
static async register(email: string, password: string, recaptcha?: string) {
const data = {
query: mutations.RegisterUser,
variables: {
email,
password,
recaptcha,
},
};

return AuthApi.sendPlatfromRequest(data);
}

static async requestPasswordReset(email: string) {
static async requestPasswordReset(email: string, recaptcha?: string) {
const data = {
query: mutations.RequestPasswordReset,
variables: {
email,
recaptcha,
},
};

Expand Down
9 changes: 6 additions & 3 deletions resources/js/components/pages/auth/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ const onCaptchaExpired = () => {
};
const loadCaptchaScript = async () => {
if (!hasCaptcha) return;
if (!hasCaptcha) {
return;
}
if (!document.getElementById('recaptcha-script')) {
const script = document.createElement('script');
script.type = 'text/javascript';
Expand All @@ -133,14 +135,15 @@ const verifyCaptcha = () => {
captchaRef.value.execute();
};
const login = async () => {
const login = async (recaptcha?: string) => {
if (!(await isValid())) return;
isLoading.value = true;
try {
if (!(await appStore.login(email.value, password.value))) {
if (!(await appStore.login(email.value, password.value, recaptcha))) {
snackbar.error({ title: 'Invalid username or password' });
isLoading.value = false;
return;
}
if (await appStore.init()) {
Expand Down
5 changes: 3 additions & 2 deletions resources/js/components/pages/auth/Register.vue
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,16 @@ const verifyCaptcha = () => {
if (!hasCaptcha) {
return register();
}
captchaRef.value.execute();
};
const register = async () => {
const register = async (recaptcha?: string) => {
if (!(await isValid())) return;
isLoading.value = true;
try {
const res = await AuthApi.register(email.value, password.value);
const res = await AuthApi.register(email.value, password.value, recaptcha);
if (res.data.RegisterUser.id) {
snackbar.success({
title: 'Account created successfully!',
Expand Down
51 changes: 48 additions & 3 deletions resources/js/components/pages/auth/RequestResetPassword.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div class="bg-white px-4 py-8 shadow sm:rounded-lg sm:px-10">
<Form ref="formRef" class="space-y-6" :validation-schema="validation" @submit="requestReset">
<Form ref="formRef" class="space-y-6" :validation-schema="validation" @submit="verifyCaptcha">
<p class="text-sm text-center">
Enter your registered email below to receive password reset instructions
</p>
Expand All @@ -16,6 +16,17 @@
label="Email address"
name="email"
input-class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary sm:text-sm sm:leading-6"
@focus="loadCaptchaScript"
/>
<vue-recaptcha
v-if="hasCaptcha"
ref="captchaRef"
:style="{ visibility: isCaptchaBadgeVisible ? 'visible' : 'hidden' }"
size="invisible"
:load-recaptcha-script="false"
:sitekey="reCaptchaSiteKey"
@verify="requestReset"
@expired="onCaptchaExpired"
/>
<div>
<div class="flex items-center justify-start mb-4">
Expand Down Expand Up @@ -51,13 +62,18 @@ import EnjinLogo from '~/components/EnjinLogo.vue';
import { AuthApi } from '~/api/auth';
import snackbar from '~/util/snackbar';
import { ArrowLeftIcon } from '@heroicons/vue/20/solid';
import { VueRecaptcha } from 'vue-recaptcha';
const router = useRouter();
const appStore = useAppStore();
const isLoading = ref(false);
const email = ref('');
const formRef = ref();
const captchaRef = ref();
const isCaptchaBadgeVisible = ref(false);
const reCaptchaSiteKey = window.bootstrap?.captcha_key || 'null';
const hasCaptcha = window.bootstrap?.captcha_key?.length > 0;
const validation = yup.object().shape({
email: yup.string().email('Email is not valid').required('Email is required'),
Expand All @@ -72,12 +88,41 @@ const isValid = async () => {
return formRef.value.getMeta().valid;
};
const requestReset = async () => {
const onCaptchaExpired = () => {
captchaRef.value.reset();
};
const loadCaptchaScript = async () => {
if (!hasCaptcha) {
return;
}
if (!document.getElementById('recaptcha-script')) {
const script = document.createElement('script');
script.type = 'text/javascript';
script.id = 'recaptcha-script';
script.async = true;
script.defer = true;
script.src = 'https://www.google.com/recaptcha/api.js?onload=vueRecaptchaApiLoaded&render=explicit&hl=:1';
document.getElementsByTagName('head')[0].appendChild(script);
}
isCaptchaBadgeVisible.value = true;
};
const verifyCaptcha = () => {
if (!hasCaptcha) {
return requestReset();
}
captchaRef.value.execute();
};
const requestReset = async (recaptcha?: string) => {
if (!(await isValid())) return;
isLoading.value = true;
try {
await AuthApi.requestPasswordReset(email.value);
await AuthApi.requestPasswordReset(email.value, recaptcha);
} catch {
// do nothing
}
Expand Down
4 changes: 2 additions & 2 deletions resources/js/graphql/mutation/auth/RegisterUser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default `mutation RegisterUser($email: String!, $password: String!) {
RegisterUser(email: $email, password: $password) {
export default `mutation RegisterUser($email: String!, $password: String!, $recaptcha: String!) {
RegisterUser(email: $email, password: $password, recaptchaResponse: $recaptcha) {
id
email
isVerified
Expand Down
4 changes: 2 additions & 2 deletions resources/js/graphql/mutation/auth/RequestPasswordReset.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default `mutation RequestPasswordReset($email: String!) {
RequestPasswordReset(email: $email)
export default `mutation RequestPasswordReset($email: String!, $recaptcha: String!) {
RequestPasswordReset(email: $email, recaptchaResponse: $recaptcha)
}`;
4 changes: 2 additions & 2 deletions resources/js/graphql/query/auth/Login.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default `query Login($email: String!, $password: String!) {
Login(email: $email, password: $password)
export default `query Login($email: String!, $password: String!, $recaptcha: String!) {
Login(email: $email, password: $password, recaptchaResponse: $recaptcha) {
}`;
4 changes: 2 additions & 2 deletions resources/js/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ export const useAppStore = defineStore('app', {

return true;
},
async login(email: string, password: string) {
const res = await AuthApi.login(email, password);
async login(email: string, password: string, recaptcha?: string) {
const res = await AuthApi.login(email, password, recaptcha);
if (!res.data.Login) {
if (res.errors.length) {
throw res.errors;
Expand Down

0 comments on commit c9f6e61

Please sign in to comment.