diff options
author | vnugent <public@vaughnnugent.com> | 2023-07-12 01:28:23 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-07-12 01:28:23 -0400 |
commit | f64955c69d91e578e580b409ba31ac4b3477da96 (patch) | |
tree | 16f01392ddf1abfea13d7d1ede3bfb0459fe8f0d /front-end/src/views/Login/components |
Initial commit
Diffstat (limited to 'front-end/src/views/Login/components')
-rw-r--r-- | front-end/src/views/Login/components/Social.vue | 57 | ||||
-rw-r--r-- | front-end/src/views/Login/components/Totp.vue | 65 | ||||
-rw-r--r-- | front-end/src/views/Login/components/UserPass.vue | 92 |
3 files changed, 214 insertions, 0 deletions
diff --git a/front-end/src/views/Login/components/Social.vue b/front-end/src/views/Login/components/Social.vue new file mode 100644 index 0000000..34c5a1e --- /dev/null +++ b/front-end/src/views/Login/components/Social.vue @@ -0,0 +1,57 @@ +<template> + + <form class="w-full" @submit.prevent="SocalLogin('/login/social/github')"> + <button type="submit" class="btn social-button" :disabled="waiting"> + <fa-icon :icon="['fab','github']" size="xl" /> + Login with Github + </button> + </form> + + <form class="mt-4" @submit.prevent="SocalLogin('/login/social/discord')"> + <button type="submit" class="btn social-button" :disabled="waiting"> + <fa-icon :icon="['fab','discord']" size="xl" /> + Login with Discord + </button> + </form> + + <form v-if="auth0Enabled" class="mt-4" @submit.prevent="SocalLogin('/login/social/auth0')"> + <button type="submit" class="btn social-button" :disabled="waiting"> + <fa-icon :icon="['fa','key']" size="xl" /> + Login with Auth0 + </button> + </form> + +</template> + +<script setup lang="ts"> +import { apiCall, useWait, useSession, useSessionUtils, WebMessage } from '@vnuge/vnlib.browser' + +//auth0 enabled flag from env +const auth0Enabled = import.meta.env.VITE_ENABLE_AUTH0 == 'true'; + +const { waiting } = useWait() +const { browserId, publicKey } = useSession() +const { KeyStore } = useSessionUtils() + +const SocalLogin = async (url:string) => { + await apiCall(async ({ axios }) => { + const { data } = await axios.put<WebMessage<string>>(url, { + browser_id: browserId.value, + public_key: publicKey.value + }) + + const encDat = data.getResultOrThrow() + // Decrypt the result which should be a redirect url + const result = await KeyStore.decryptDataAsync(encDat) + // get utf8 text + const text = new TextDecoder('utf-8').decode(result) + // Recover url + const redirect = new URL(text) + // Force https + redirect.protocol = 'https:' + // redirect to the url + window.location.href = redirect.href + }) +} + +</script>
\ No newline at end of file diff --git a/front-end/src/views/Login/components/Totp.vue b/front-end/src/views/Login/components/Totp.vue new file mode 100644 index 0000000..50a5be3 --- /dev/null +++ b/front-end/src/views/Login/components/Totp.vue @@ -0,0 +1,65 @@ +<template> + <div id="totp-login-form"> + <h5>Enter your TOTP code</h5> + <div class="flex flex-col h-32"> + <div class="h-8 mx-auto"> + <fa-icon v-if="waiting" class="animate-spin" size="xl" icon="spinner"/> + </div> + <div class="mx-auto mt-4"> + <VOtpInput + class="otp-input" + input-type="letter-numeric" + :is-disabled="waiting" + separator="" + input-classes="primary input rounded" + :num-inputs="6" + value="" + @on-change="onInput" + @on-complete="SubimitTotp" + /> + </div> + </div> + </div> +</template> + +<script setup lang="ts"> +import { useMessage, useWait } from '@vnuge/vnlib.browser'; +import { toSafeInteger } from 'lodash'; +import VOtpInput from "vue3-otp-input"; + +const emit = defineEmits(['submit']) + +const { waiting } = useWait(); +const { onInput } = useMessage(); + +const SubimitTotp = async (code : string) => { + + //If a request is still pending, do nothing + if (waiting.value) { + return + } + + //Submit a mfa upgrade result + emit('submit', { + code: toSafeInteger(code) + }) +} + + +</script> + +<style lang="scss"> + +#totp-login-form { + .otp-input { + @apply rounded-sm gap-2; + + input { + @apply w-12 h-12 p-3 text-center text-2xl; + appearance: none; + -webkit-appearance: none; + } + } +} + +</style>
\ No newline at end of file diff --git a/front-end/src/views/Login/components/UserPass.vue b/front-end/src/views/Login/components/UserPass.vue new file mode 100644 index 0000000..e218cb8 --- /dev/null +++ b/front-end/src/views/Login/components/UserPass.vue @@ -0,0 +1,92 @@ +<template> + <div class=""> + <h3>Login</h3> + <form id="user-pass-submit-form" method="post" action="/login" @submit.prevent="SubmitLogin"> + <fieldset class="" :disabled="waiting" > + <div> + <div class="float-label"> + <input + id="username" + v-model="v$.username.$model" + type="email" + class="w-full primary input" + placeholder="Email" + :class="{ 'data-invalid': v$.username.$invalid }" + @input="onInput" + > + <label for="username">Email</label> + </div> + </div> + <div class="py-3"> + <div class="mb-2 float-label"> + <input + id="password" + v-model="v$.password.$model" + type="password" + class="w-full primary input" + placeholder="Password" + :class="{ 'data-invalid': v$.password.$invalid }" + @input="onInput" + > + <label for="password">Password</label> + </div> + </div> + </fieldset> + <button type="submit" form="user-pass-submit-form" class="btn primary" :disabled="waiting"> + <!-- Display spinner if waiting, otherwise the sign-in icon --> + <fa-icon :class="{'animate-spin':waiting}" :icon="waiting ? 'spinner' : 'sign-in-alt'"/> + Log-in + </button> + </form> + <div class="flex flex-row justify-between gap-3 pt-3 pb-2 form-links"> + <router-link to="/pwreset"> + Forgot password + </router-link> + <router-link to="/register"> + Register a new account + </router-link> + </div> + </div> +</template> + +<script setup lang="ts"> +import { reactive } from 'vue' +import useVuelidate from '@vuelidate/core' +import { required, maxLength, minLength, email, helpers } from '@vuelidate/validators' +import { useMessage, useVuelidateWrapper, useWait } from '@vnuge/vnlib.browser' + +const emit = defineEmits(['login']) + +const { onInput } = useMessage(); +const { waiting } = useWait(); + +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$); + +const SubmitLogin = async () => { + + // If the form is not valid set the error message + if (!await validate()) { + return + } + + //Emit login and pass the username and password + emit('login', { username: v$.value.username.$model, password: v$.value.password.$model }); +} + +</script>
\ No newline at end of file |