- added 2fa authentication during login. see resources/js/Pages/Auth/login.vue
All checks were successful
CI Pipeline / japa-tests (push) Successful in 1m2s
All checks were successful
CI Pipeline / japa-tests (push) Successful in 1m2s
- added validate() method inside app/Srvices/TwoFactorProvider.ts - added twoFactorChallenge() method inside app/Controllers/Http/Auth/AuthController.ts for logging in via 2fa-code
This commit is contained in:
parent
b2dce0259a
commit
f828ca4491
|
@ -1,9 +1,13 @@
|
||||||
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
// import User from 'App/Models/User';
|
import User from 'App/Models/User';
|
||||||
// import Hash from '@ioc:Adonis/Core/Hash';
|
// import Hash from '@ioc:Adonis/Core/Hash';
|
||||||
// import InvalidCredentialException from 'App/Exceptions/InvalidCredentialException';
|
// import InvalidCredentialException from 'App/Exceptions/InvalidCredentialException';
|
||||||
import AuthValidator from 'App/Validators/AuthValidator';
|
import AuthValidator from 'App/Validators/AuthValidator';
|
||||||
|
|
||||||
|
import TwoFactorAuthProvider from 'App/Services/TwoFactorAuthProvider';
|
||||||
|
// import { LoginState } from 'Contracts/enums';
|
||||||
|
// import { StatusCodes } from 'http-status-codes';
|
||||||
|
|
||||||
export default class AuthController {
|
export default class AuthController {
|
||||||
// login function
|
// login function
|
||||||
public async login({ request, response, auth, session }: HttpContextContract) {
|
public async login({ request, response, auth, session }: HttpContextContract) {
|
||||||
|
@ -12,24 +16,30 @@ export default class AuthController {
|
||||||
// });
|
// });
|
||||||
await request.validate(AuthValidator);
|
await request.validate(AuthValidator);
|
||||||
|
|
||||||
const plainPassword = await request.input('password');
|
// const plainPassword = await request.input('password');
|
||||||
const email = await request.input('email');
|
// const email = await request.input('email');
|
||||||
// grab uid and password values off request body
|
// grab uid and password values off request body
|
||||||
// const { email, password } = request.only(['email', 'password'])
|
const { email, password } = request.only(['email', 'password']);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// attempt to verify credential and login user
|
// // attempt to verify credential and login user
|
||||||
await auth.use('web').attempt(email, plainPassword);
|
// await auth.use('web').attempt(email, plainPassword);
|
||||||
|
|
||||||
// const user = await auth.use('web').verifyCredentials(email, plainPassword);
|
const user = await auth.use('web').verifyCredentials(email, password);
|
||||||
// if (user.isTwoFactorEnabled) {
|
if (user.isTwoFactorEnabled) {
|
||||||
// // session.put("login.id", user.id);
|
// session.put("login.id", user.id);
|
||||||
// // return view.render("pages/two-factor-challenge");
|
// return view.render("pages/two-factor-challenge");
|
||||||
// }
|
|
||||||
|
|
||||||
// session.forget('login.id');
|
session.flash('user_id', user.id);
|
||||||
// session.regenerate();
|
return response.redirect().back();
|
||||||
// await auth.login(user);
|
|
||||||
|
// let state = LoginState.STATE_VALIDATED;
|
||||||
|
// return response.status(StatusCodes.OK).json({
|
||||||
|
// state: state,
|
||||||
|
// new_user_id: user.id,
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
await auth.login(user);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// if login fails, return vague form message and redirect back
|
// if login fails, return vague form message and redirect back
|
||||||
session.flash('message', 'Your username, email, or password is incorrect');
|
session.flash('message', 'Your username, email, or password is incorrect');
|
||||||
|
@ -40,6 +50,32 @@ export default class AuthController {
|
||||||
response.redirect('/apps/dashboard');
|
response.redirect('/apps/dashboard');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async twoFactorChallenge({ request, session, auth, response }) {
|
||||||
|
const { code, recoveryCode, login_id } = request.only(['code', 'recoveryCode', 'login_id']);
|
||||||
|
// const user = await User.query().where('id', session.get('login.id')).firstOrFail();
|
||||||
|
const user = await User.query().where('id', login_id).firstOrFail();
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
const isValid = await TwoFactorAuthProvider.validate(user, code);
|
||||||
|
if (isValid) {
|
||||||
|
// login user and redirect to dashboard
|
||||||
|
await auth.login(user);
|
||||||
|
response.redirect('/apps/dashboard');
|
||||||
|
} else {
|
||||||
|
session.flash('message', 'Your tow factor code is incorrect');
|
||||||
|
return response.redirect().back();
|
||||||
|
}
|
||||||
|
} else if (recoveryCode) {
|
||||||
|
const codes = user?.twoFactorRecoveryCodes ?? [];
|
||||||
|
if (codes.includes(recoveryCode)) {
|
||||||
|
user.twoFactorRecoveryCodes = codes.filter((c) => c !== recoveryCode);
|
||||||
|
await user.save();
|
||||||
|
await auth.login(user);
|
||||||
|
response.redirect('/apps/dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// logout function
|
// logout function
|
||||||
public async logout({ auth, response }: HttpContextContract) {
|
public async logout({ auth, response }: HttpContextContract) {
|
||||||
// await auth.logout();
|
// await auth.logout();
|
||||||
|
|
|
@ -105,7 +105,7 @@ class TwoFactorAuthProvider {
|
||||||
public async enable(user: User, token: string): Promise<boolean> {
|
public async enable(user: User, token: string): Promise<boolean> {
|
||||||
const isValid = verifyToken(user.twoFactorSecret as string, token, 1);
|
const isValid = verifyToken(user.twoFactorSecret as string, token, 1);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
user.state = TotpState.STATE_ENABLED;
|
user.state = TotpState.STATE_ENABLED;
|
||||||
if (await user.save()) {
|
if (await user.save()) {
|
||||||
|
@ -113,6 +113,14 @@ class TwoFactorAuthProvider {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async validate(user: User, token: string): Promise<boolean> {
|
||||||
|
const isValid = verifyToken(user.twoFactorSecret as string, token, 1);
|
||||||
|
if (isValid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new TwoFactorAuthProvider();
|
export default new TwoFactorAuthProvider();
|
||||||
|
|
|
@ -186,3 +186,9 @@ export interface Identifier {
|
||||||
updated_at: string; //'2023-03-09T09:48:28.000Z'
|
updated_at: string; //'2023-03-09T09:48:28.000Z'
|
||||||
value: string; //'10.24341/tethys.209'
|
value: string; //'10.24341/tethys.209'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export enum LoginState {
|
||||||
|
// STATE_DISABLED = 0,
|
||||||
|
// STATE_VALIDATED = 1,
|
||||||
|
// STATE_2FA_AUTHENTICATED = 1,
|
||||||
|
// }
|
|
@ -2,7 +2,7 @@
|
||||||
// import { Head, Link, useForm, usePage } from '@inertiajs/inertia-vue3';
|
// import { Head, Link, useForm, usePage } from '@inertiajs/inertia-vue3';
|
||||||
import { Head, Link, useForm, usePage } from '@inertiajs/vue3';
|
import { Head, Link, useForm, usePage } from '@inertiajs/vue3';
|
||||||
import { ComputedRef } from 'vue';
|
import { ComputedRef } from 'vue';
|
||||||
import { mdiAccountKey, mdiPlus, mdiSquareEditOutline, mdiTrashCan, mdiAlertBoxOutline } from '@mdi/js';
|
import { mdiAccountKey, mdiPlus, mdiSquareEditOutline, mdiAlertBoxOutline } from '@mdi/js';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||||
import SectionMain from '@/Components/SectionMain.vue';
|
import SectionMain from '@/Components/SectionMain.vue';
|
||||||
|
|
|
@ -1,37 +1,7 @@
|
||||||
<!-- <template>
|
|
||||||
<div>
|
|
||||||
<Link href="/app">Home</Link>
|
|
||||||
<br />
|
|
||||||
<h1>Login</h1>
|
|
||||||
<Head>
|
|
||||||
<title>About - My app</title>
|
|
||||||
<meta
|
|
||||||
head-key="description"
|
|
||||||
name="description"
|
|
||||||
content="This is a page specific description"
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import AuthLayout from '@/Layouts/Auth.vue';
|
|
||||||
import LayoutGuest from '@/Layouts/LayoutGuest.vue';
|
|
||||||
export default {
|
|
||||||
layout: AuthLayout,
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
// import { Head, Link } from '@inertiajs/vue3';
|
|
||||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
|
||||||
import FormField from '@/Components/FormField.vue';
|
|
||||||
import FormControl from '@/Components/FormControl.vue';
|
|
||||||
</script> -->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LayoutGuest>
|
<LayoutGuest>
|
||||||
|
|
||||||
<Head title="Login" />
|
<Head title="Login" />
|
||||||
|
|
||||||
<!-- <SectionFullScreen v-slot="{ cardClass }" :bg="'greenBlue'"> -->
|
<!-- <SectionFullScreen v-slot="{ cardClass }" :bg="'greenBlue'"> -->
|
||||||
|
@ -40,8 +10,9 @@ import FormControl from '@/Components/FormControl.vue';
|
||||||
<img src="/logo.svg" class="h-10 mr-4" alt="Windster Logo" />
|
<img src="/logo.svg" class="h-10 mr-4" alt="Windster Logo" />
|
||||||
<!-- <span class="self-center text-2xl font-bold whitespace-nowrap">Tethys</span> -->
|
<!-- <span class="self-center text-2xl font-bold whitespace-nowrap">Tethys</span> -->
|
||||||
</a>
|
</a>
|
||||||
<!-- Card -->
|
|
||||||
<CardBox :class="cardClass" form @submit.prevent="submit">
|
<CardBox v-if="isTwoFactorAuthNeeded == false" :class="cardClass" form
|
||||||
|
@submit.prevent="submit">
|
||||||
<FormValidationErrors v-bind:errors="errors" />
|
<FormValidationErrors v-bind:errors="errors" />
|
||||||
|
|
||||||
<NotificationBarInCard v-if="status" color="info">
|
<NotificationBarInCard v-if="status" color="info">
|
||||||
|
@ -49,18 +20,13 @@ import FormControl from '@/Components/FormControl.vue';
|
||||||
</NotificationBarInCard>
|
</NotificationBarInCard>
|
||||||
|
|
||||||
<FormField label="Email" label-for="email" help="Please enter your email">
|
<FormField label="Email" label-for="email" help="Please enter your email">
|
||||||
<FormControl v-model="form.email" :icon="mdiAccount" id="email" autocomplete="email" type="email" required />
|
<FormControl v-model="form.email" :icon="mdiAccount" id="email" autocomplete="email" type="email"
|
||||||
|
required />
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField label="Password" label-for="password" help="Please enter your password">
|
<FormField label="Password" label-for="password" help="Please enter your password">
|
||||||
<FormControl
|
<FormControl v-model="form.password" :icon="mdiAsterisk" type="password" id="password"
|
||||||
v-model="form.password"
|
autocomplete="current-password" required />
|
||||||
:icon="mdiAsterisk"
|
|
||||||
type="password"
|
|
||||||
id="password"
|
|
||||||
autocomplete="current-password"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormCheckRadioGroup v-model="form.remember" name="remember" :options="{ remember: 'Remember' }" />
|
<FormCheckRadioGroup v-model="form.remember" name="remember" :options="{ remember: 'Remember' }" />
|
||||||
|
@ -79,36 +45,62 @@ import FormControl from '@/Components/FormControl.vue';
|
||||||
<BaseDivider />
|
<BaseDivider />
|
||||||
|
|
||||||
<!-- buttons -->
|
<!-- buttons -->
|
||||||
<BaseLevel>
|
|
||||||
<BaseButtons>
|
<BaseButtons>
|
||||||
<!-- <BaseButton type="submit" color="info" label="Login" :class="{ 'opacity-25': form.processing }"
|
<BaseButton type="submit" color="info" label=" Login to your account" :class="{ 'opacity-25': form.processing }"
|
||||||
v-bind:disabled="form.processing" /> -->
|
v-bind:disabled="form.processing" />
|
||||||
<button
|
<!-- <button type="submit" v-bind:disabled="form.processing" :class="{ 'opacity-25': form.processing }"
|
||||||
type="submit"
|
class="text-white bg-cyan-600 hover:bg-cyan-700 focus:ring-4 focus:ring-cyan-200 font-medium rounded-lg text-base px-5 py-3 w-full sm:w-auto text-center">
|
||||||
v-bind:disabled="form.processing"
|
Login to your account
|
||||||
:class="{ 'opacity-25': form.processing }"
|
</button> -->
|
||||||
class="text-white bg-cyan-600 hover:bg-cyan-700 focus:ring-4 focus:ring-cyan-200 font-medium rounded-lg text-base px-5 py-3 w-full sm:w-auto text-center"
|
<!-- <BaseButton v-if="canResetPassword" :route-name="route('password.request')" color="info" outline
|
||||||
>
|
|
||||||
Login to your account
|
|
||||||
</button>
|
|
||||||
<!-- <BaseButton v-if="canResetPassword" :route-name="route('password.request')" color="info" outline
|
|
||||||
label="Remind" /> -->
|
label="Remind" /> -->
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
<!-- <Link :href="stardust.route('app.register.show')"> Register </Link> -->
|
<!-- <Link :href="stardust.route('app.register.show')"> Register </Link> -->
|
||||||
</BaseLevel>
|
|
||||||
<div class="text-sm font-medium text-gray-500">
|
<!-- <div class="text-sm font-medium text-gray-500">
|
||||||
Not registered? <a href="" class="text-teal-500 hover:underline">Create account</a>
|
Not registered? <a href="" class="text-teal-500 hover:underline">Create account</a>
|
||||||
</div>
|
</div> -->
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|
||||||
|
|
||||||
|
<CardBox v-else-if="isTwoFactorAuthNeeded" :icon="mdiTwoFactorAuthentication" :class="cardClass" form
|
||||||
|
@submit.prevent="submitFa2Form">
|
||||||
|
<FormField label="2FA Code" label-for="code" help="Please enter 2factor code">
|
||||||
|
<FormControl v-model="fa2Form.code" :icon="mdiAccount" id="code" type="tel" required />
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<div v-if="flash && flash.message" class="flex flex-col mt-6 animate-fade-in">
|
||||||
|
<div class="bg-yellow-500 border-l-4 border-orange-400 text-white p-4" role="alert">
|
||||||
|
<p class="font-bold">Be Warned</p>
|
||||||
|
<p>{{ flash.message }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BaseDivider />
|
||||||
|
|
||||||
|
|
||||||
|
<BaseButtons>
|
||||||
|
<!-- <button type="submit" :icon="mdiContentSaveCheck" :disabled="fa2Form.processing"
|
||||||
|
:class="{ 'opacity-25': fa2Form.processing }"
|
||||||
|
class="text-white bg-cyan-600 hover:bg-cyan-700 focus:ring-4 focus:ring-cyan-200 font-medium rounded-lg text-base px-5 py-3 w-full sm:w-auto text-center">
|
||||||
|
Verify
|
||||||
|
</button> -->
|
||||||
|
<BaseButton type="submit" :icon="mdiContentSaveCheck" color="info" label=" Login to your account" :class="{ 'opacity-25': fa2Form.processing }"
|
||||||
|
v-bind:disabled="fa2Form.processing" />
|
||||||
|
</BaseButtons>
|
||||||
|
<!-- <Link :href="stardust.route('app.register.show')"> Register </Link> -->
|
||||||
|
|
||||||
|
</CardBox>
|
||||||
|
|
||||||
</SectionFullScreen>
|
</SectionFullScreen>
|
||||||
</LayoutGuest>
|
</LayoutGuest>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useForm, Head } from '@inertiajs/vue3';
|
import { useForm, Head } from '@inertiajs/vue3';
|
||||||
import { Ref } from 'vue';
|
import { Ref, ref } from 'vue';
|
||||||
// import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
|
// import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
|
||||||
import { mdiAccount, mdiAsterisk } from '@mdi/js';
|
import { mdiAccount, mdiAsterisk, mdiTwoFactorAuthentication, mdiContentSaveCheck, mdiLock } from '@mdi/js';
|
||||||
import LayoutGuest from '@/Layouts/LayoutGuest.vue';
|
import LayoutGuest from '@/Layouts/LayoutGuest.vue';
|
||||||
import SectionFullScreen from '@/Components/SectionFullScreen.vue';
|
import SectionFullScreen from '@/Components/SectionFullScreen.vue';
|
||||||
import CardBox from '@/Components/CardBox.vue';
|
import CardBox from '@/Components/CardBox.vue';
|
||||||
|
@ -116,22 +108,55 @@ import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
|
||||||
import FormField from '@/Components/FormField.vue';
|
import FormField from '@/Components/FormField.vue';
|
||||||
import FormControl from '@/Components/FormControl.vue';
|
import FormControl from '@/Components/FormControl.vue';
|
||||||
import BaseDivider from '@/Components/BaseDivider.vue';
|
import BaseDivider from '@/Components/BaseDivider.vue';
|
||||||
// import BaseButton from '@/Components/BaseButton.vue';
|
import BaseButton from '@/Components/BaseButton.vue';
|
||||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||||
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
||||||
import NotificationBarInCard from '@/Components/NotificationBarInCard.vue';
|
import NotificationBarInCard from '@/Components/NotificationBarInCard.vue';
|
||||||
import BaseLevel from '@/Components/BaseLevel.vue';
|
|
||||||
|
|
||||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||||
// import NotificationBar from '@/Components/NotificationBar.vue';
|
// import NotificationBar from '@/Components/NotificationBar.vue';
|
||||||
import { computed } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
import { usePage } from '@inertiajs/vue3';
|
import { usePage } from '@inertiajs/vue3';
|
||||||
|
// import axios from 'axios';
|
||||||
|
// import { LoginState } from '@/Dataset';
|
||||||
|
|
||||||
// interface IErrorMessage {
|
// interface IErrorMessage {
|
||||||
// [key: string]: Array<string>;
|
// [key: string]: Array<string>;
|
||||||
// }
|
// }
|
||||||
const flash: Ref<any> = computed(() => {
|
const flash: Ref<any> = computed(() => {
|
||||||
return usePage().props.flash;
|
let test = usePage().props;
|
||||||
|
return test.flash;
|
||||||
|
});
|
||||||
|
|
||||||
|
const user_id: Ref<any> = computed(() => {
|
||||||
|
let test = usePage().props;
|
||||||
|
return test.user_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isTwoFactorAuthNeeded = ref(false);
|
||||||
|
|
||||||
|
// const user_id: ComputedRef<number> = computed(() => {
|
||||||
|
// return usePage().props.flash.user_id as number;
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
// const isTwoFactorAuthNeeded = computed(() => {
|
||||||
|
// if (flash.new_user_id) {
|
||||||
|
// return true;
|
||||||
|
// } else {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
watch(user_id, () => {
|
||||||
|
if (user_id.value) {
|
||||||
|
isTwoFactorAuthNeeded.value = true;
|
||||||
|
} else {
|
||||||
|
isTwoFactorAuthNeeded.value = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
|
@ -146,11 +171,57 @@ defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm({
|
// const { version } = usePage();
|
||||||
|
|
||||||
|
const form = useForm(() => ({
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
remember: [],
|
remember: [],
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
// const login = async (data): Promise<any | null> => {
|
||||||
|
// try {
|
||||||
|
// let resp = await axios.post(stardust.route('login.store'), data, {
|
||||||
|
// headers: {
|
||||||
|
// 'X-Inertia': true,
|
||||||
|
// 'X-Inertia-Partial-Component': 'Users',
|
||||||
|
// 'X-Inertia-Partial-Data': 'users',
|
||||||
|
// 'X-Inertia-Version': version,
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Check if the response contains a redirect status
|
||||||
|
// if (resp.data.component && resp.data.url) {
|
||||||
|
// // Use Inertia.js to visit the specified component
|
||||||
|
// router.visit(resp.data.url, resp.data.props);
|
||||||
|
// } else {
|
||||||
|
// // If it's not a redirect, reset the password field in the form
|
||||||
|
// form.reset('password');
|
||||||
|
// return resp.data;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// } catch (error) {
|
||||||
|
// // Handle errors if any
|
||||||
|
// console.error('Error during login:', error);
|
||||||
|
// throw error;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const submit = async () => {
|
||||||
|
// // Transform the formData object
|
||||||
|
// const formData = form.data();
|
||||||
|
// const transformedData = {
|
||||||
|
// ...formData,
|
||||||
|
// remember: formData.remember && formData.remember.length ? 'on' : '',
|
||||||
|
// };
|
||||||
|
// const resp = await login(transformedData);
|
||||||
|
// if (resp) {
|
||||||
|
// if (resp.state == LoginState.STATE_VALIDATED) {
|
||||||
|
// isTwoFactorAuthNeeded.value = true
|
||||||
|
// }
|
||||||
|
// fa2Form.login_id = resp.new_user_id;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
await form
|
await form
|
||||||
|
@ -164,4 +235,26 @@ const submit = async () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const fa2Form = useForm(() => ({
|
||||||
|
code: '',
|
||||||
|
remember: [],
|
||||||
|
login_id: ''
|
||||||
|
}));
|
||||||
|
const submitFa2Form = async () => {
|
||||||
|
await fa2Form
|
||||||
|
.transform((data) => ({
|
||||||
|
...data,
|
||||||
|
remember: fa2Form.remember && fa2Form.remember.length ? 'on' : '',
|
||||||
|
login_id: user_id.value
|
||||||
|
}))
|
||||||
|
.post(stardust.route('login.twoFactorChallenge'), {
|
||||||
|
onFinish: () => {
|
||||||
|
fa2Form.reset('code');
|
||||||
|
fa2Form.reset('login_id');
|
||||||
|
// form.reset('password');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -16,6 +16,10 @@ Inertia.share({
|
||||||
return ctx.session.flashMessages.get('errors');
|
return ctx.session.flashMessages.get('errors');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
user_id: (ctx) => {
|
||||||
|
return ctx.session.flashMessages.get('user_id');
|
||||||
|
},
|
||||||
|
|
||||||
flash: (ctx) => {
|
flash: (ctx) => {
|
||||||
return {
|
return {
|
||||||
message: ctx.session.flashMessages.get('message'),
|
message: ctx.session.flashMessages.get('message'),
|
||||||
|
|
|
@ -98,6 +98,8 @@ Route.get('/app/login', async ({ inertia }) => {
|
||||||
|
|
||||||
// Route.post("/login", "Users/AuthController.login");
|
// Route.post("/login", "Users/AuthController.login");
|
||||||
Route.post('/app/login', 'Auth/AuthController.login').as('login.store');
|
Route.post('/app/login', 'Auth/AuthController.login').as('login.store');
|
||||||
|
Route.post('/app/twoFactorChallenge', 'Auth/AuthController.twoFactorChallenge').as('login.twoFactorChallenge');
|
||||||
|
|
||||||
// Route.on("/signup").render("signup");
|
// Route.on("/signup").render("signup");
|
||||||
// Route.post("/signup", "AuthController.signup");
|
// Route.post("/signup", "AuthController.signup");
|
||||||
Route.post('/signout', 'Auth/AuthController.logout').as('logout');
|
Route.post('/signout', 'Auth/AuthController.logout').as('logout');
|
||||||
|
|
Loading…
Reference in New Issue
Block a user