aboutsummaryrefslogtreecommitdiff
path: root/front-end/src/components/Login/UserPass.vue
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-01-20 23:49:29 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2024-01-20 23:49:29 -0500
commit6cb7da37824d02a1898d08d0f9495c77fde4dd1d (patch)
tree95e37ea3c20f416d6a205ee4ab050c307b18eafe /front-end/src/components/Login/UserPass.vue
inital commit
Diffstat (limited to 'front-end/src/components/Login/UserPass.vue')
-rw-r--r--front-end/src/components/Login/UserPass.vue118
1 files changed, 118 insertions, 0 deletions
diff --git a/front-end/src/components/Login/UserPass.vue b/front-end/src/components/Login/UserPass.vue
new file mode 100644
index 0000000..b25a335
--- /dev/null
+++ b/front-end/src/components/Login/UserPass.vue
@@ -0,0 +1,118 @@
+<script setup lang="ts">
+import { ref, shallowRef, reactive, defineAsyncComponent } from 'vue'
+import { useTimeoutFn, set } from '@vueuse/core'
+import { useVuelidate } from '@vuelidate/core'
+import { isEqual } from 'lodash-es'
+import { required, maxLength, minLength, email, helpers } from '@vuelidate/validators'
+import {
+ useVuelidateWrapper, useMfaLogin, totpMfaProcessor, IMfaFlowContinuiation, MfaMethod,
+ apiCall, useMessage, useWait, debugLog, WebMessage
+} from '@vnuge/vnlib.browser'
+const OptInput = defineAsyncComponent(() => import('../global/OtpInput.vue'))
+
+const { setMessage } = useMessage();
+const { waiting } = useWait();
+
+//Setup mfa login with TOTP support
+const { login } = useMfaLogin([ totpMfaProcessor() ])
+
+const mfaUpgrade = shallowRef<IMfaFlowContinuiation | undefined>();
+
+const mfaTimeout = ref<number>(600 * 1000);
+const mfaTimer = useTimeoutFn(() => {
+ //Clear upgrade message
+ set(mfaUpgrade, undefined);
+ setMessage('Your TOTP request has expired')
+}, mfaTimeout, { immediate: false })
+
+const vState = reactive({ username: '', password: '' })
+
+const rules = {
+ username: {
+ required: helpers.withMessage('Email cannot be empty', required),
+ email: helpers.withMessage('Your email address is not valid', email),
+ maxLength: helpers.withMessage('Email address must be less than 50 characters', maxLength(50))
+ },
+ password: {
+ required: helpers.withMessage('Password cannot be empty', required),
+ minLength: helpers.withMessage('Password must be at least 8 characters', minLength(8)),
+ maxLength: helpers.withMessage('Password must have less than 128 characters', maxLength(128))
+ }
+}
+
+const v$ = useVuelidate(rules, vState)
+const { validate } = useVuelidateWrapper(v$ as any);
+
+const onSubmit = async () => {
+
+ // If the form is not valid set the error message
+ if (!await validate()) {
+ return
+ }
+
+ // Run login in an apicall wrapper
+ await apiCall(async ({ toaster }) => {
+
+ //Attempt to login
+ const response = await login(
+ v$.value.username.$model,
+ v$.value.password.$model
+ );
+
+ debugLog('Mfa-login', response);
+
+ //See if the response is a web message
+ if (response.getResultOrThrow) {
+ (response as WebMessage).getResultOrThrow();
+ }
+
+ //Try to get response as a flow continuation
+ const mfa = response as IMfaFlowContinuiation
+
+ // Response is a totp upgrade request
+ if (isEqual(mfa.type, MfaMethod.TOTP)) {
+ //Store the upgrade message
+ set(mfaUpgrade, mfa);
+ //Setup timeout timer
+ set(mfaTimeout, mfa.expires! * 1000);
+ mfaTimer.start();
+ }
+ //If login without mfa was successful
+ else if (response.success) {
+ // Push a new toast message
+ toaster.general.success({
+ title: 'Success',
+ text: 'You have been logged in',
+ })
+ }
+ })
+}
+</script>
+
+<template>
+ <form v-if="mfaUpgrade" @submit.prevent="" class="max-w-sm mx-auto">
+ <OptInput :upgrade="mfaUpgrade" />
+ <p id="helper-text-explanation" class="mt-2 text-sm text-gray-500 dark:text-gray-400">
+ Please enter your 6 digit TOTP code from your authenticator app.
+ </p>
+ </form>
+ <form v-else class="space-y-4 md:space-y-6" action="#" @submit.prevent="onSubmit" :disabled="waiting">
+ <fieldset>
+ <label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your email</label>
+ <input type="email" name="email" id="email" class="input" placeholder="name@company.com" required
+ v-model="v$.username.$model"
+ >
+ </fieldset>
+ <fieldset>
+ <label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Password</label>
+ <input type="password" name="password" id="password" class="input" placeholder="••••••••" required
+ v-model="v$.password.$model"
+ >
+ </fieldset>
+ <button type="submit" class="btn">Sign in</button>
+ </form>
+</template>
+
+<style scoped lang="scss">
+
+</style> \ No newline at end of file