aboutsummaryrefslogtreecommitdiff
path: root/front-end/src/views/Login/components
diff options
context:
space:
mode:
Diffstat (limited to 'front-end/src/views/Login/components')
-rw-r--r--front-end/src/views/Login/components/Social.vue57
-rw-r--r--front-end/src/views/Login/components/Totp.vue65
-rw-r--r--front-end/src/views/Login/components/UserPass.vue92
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