♻️ Moved sign in from Passport to here
This commit is contained in:
		
							
								
								
									
										66
									
								
								components/auth/Authenticate.vue
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										66
									
								
								components/auth/Authenticate.vue
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <template> | ||||
|   <div class="flex items-center"> | ||||
|     <v-form class="flex-grow-1" @submit.prevent="submit"> | ||||
|       <v-text-field label="Username" variant="solo" density="comfortable" class="mb-3" :hide-details="true" | ||||
|                     :disabled="props.loading" v-model="probe" /> | ||||
|       <v-text-field label="Password" variant="solo" density="comfortable" type="password" :disabled="props.loading" | ||||
|                     v-model="password" /> | ||||
|  | ||||
|       <v-expand-transition> | ||||
|         <v-alert v-show="error" variant="tonal" type="error" class="text-xs mb-3"> | ||||
|           Something went wrong... {{ error }} | ||||
|         </v-alert> | ||||
|       </v-expand-transition> | ||||
|  | ||||
|       <div class="flex justify-between"> | ||||
|         <v-btn type="button" variant="plain" color="grey-darken-3" to="/auth/sign-up">Sign up</v-btn> | ||||
|  | ||||
|         <v-btn | ||||
|           type="submit" | ||||
|           variant="text" | ||||
|           color="primary" | ||||
|           class="justify-self-end" | ||||
|           append-icon="mdi-arrow-right" | ||||
|           :disabled="props.loading" | ||||
|         > | ||||
|           Next | ||||
|         </v-btn> | ||||
|       </div> | ||||
|     </v-form> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref } from "vue" | ||||
|  | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const probe = ref("") | ||||
| const password = ref("") | ||||
|  | ||||
| const error = ref<string | null>(null) | ||||
|  | ||||
| const props = defineProps<{ loading?: boolean }>() | ||||
| const emits = defineEmits(["swap", "update:loading", "update:ticket"]) | ||||
|  | ||||
| async function submit() { | ||||
|   if (!probe.value || !password.value) return | ||||
|  | ||||
|   emits("update:loading", true) | ||||
|   const res = await fetch(`${config.public.solarNetworkApi}/cgi/auth/auth`, { | ||||
|     method: "POST", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify({ username: probe.value, password: password.value }), | ||||
|   }) | ||||
|   if (res.status !== 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     const data = await res.json() | ||||
|     emits("update:ticket", data["ticket"]) | ||||
|     if (data.is_finished) emits("swap", "completed") | ||||
|     else emits("swap", "mfa") | ||||
|     error.value = null | ||||
|   } | ||||
|   emits("update:loading", false) | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										69
									
								
								components/auth/AuthenticateCompleted.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								components/auth/AuthenticateCompleted.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <v-icon icon="mdi-lan-check" size="32" color="grey-darken-3" class="mb-3" /> | ||||
|  | ||||
|     <h1 class="font-bold text-xl">All Done!</h1> | ||||
|     <p>Welcome back! You just signed in right now! We're going to direct you to dashboard...</p> | ||||
|  | ||||
|     <v-expand-transition> | ||||
|       <v-alert v-show="error" variant="tonal" type="error" class="text-xs mb-3"> | ||||
|         Something went wrong... {{ error }} | ||||
|       </v-alert> | ||||
|     </v-expand-transition> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { onMounted, ref } from "vue" | ||||
| import { useRoute, useRouter } from "vue-router" | ||||
|  | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const route = useRoute() | ||||
| const router = useRouter() | ||||
| const userinfo = useUserinfo() | ||||
|  | ||||
| const props = defineProps<{ loading?: boolean; currentFactor?: any; ticket?: any }>() | ||||
| const emits = defineEmits(["update:loading"]) | ||||
|  | ||||
| const error = ref<string | null>(null) | ||||
|  | ||||
| async function load() { | ||||
|   emits("update:loading", true) | ||||
|   await getToken(props.ticket.grant_token) | ||||
|   await userinfo.readProfiles() | ||||
|   setTimeout(() => callback(), 1850) | ||||
| } | ||||
|  | ||||
| onMounted(() => load()) | ||||
|  | ||||
| async function getToken(tk: string) { | ||||
|   const res = await fetch(`${config.public.solarNetworkApi}/cgi/auth/auth/token`, { | ||||
|     method: "POST", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify({ | ||||
|       code: tk, | ||||
|       grant_type: "grant_token", | ||||
|     }), | ||||
|   }) | ||||
|   if (res.status !== 200) { | ||||
|     const err = await res.text() | ||||
|     error.value = err | ||||
|     throw new Error(err) | ||||
|   } else { | ||||
|     const out = await res.json() | ||||
|     setTokenSet(out["access_token"], out["refresh_token"]) | ||||
|     error.value = null | ||||
|   } | ||||
| } | ||||
|  | ||||
| function callback() { | ||||
|   if (route.query["close"]) { | ||||
|     window.close() | ||||
|   } else if (route.query["redirect_uri"]) { | ||||
|     window.open((route.query["redirect_uri"] as string) ?? "/", "_self") | ||||
|   } else { | ||||
|     router.push("/users/me") | ||||
|   } | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										14
									
								
								components/auth/CallbackHint.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								components/auth/CallbackHint.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <template> | ||||
|   <div class="w-full max-w-[720px]"> | ||||
|     <v-expand-transition> | ||||
|       <v-alert v-show="route.query['redirect_uri']" variant="tonal" type="info" class="text-xs"> | ||||
|         You need to sign in before access that page. After you signed in, we will redirect you to: <br /> | ||||
|         <span class="font-mono">{{ route.query["redirect_uri"] }}</span> | ||||
|       </v-alert> | ||||
|     </v-expand-transition> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| const route = useRoute() | ||||
| </script> | ||||
							
								
								
									
										94
									
								
								components/auth/FactorApplicator.vue
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										94
									
								
								components/auth/FactorApplicator.vue
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| <template> | ||||
|   <div class="flex items-center"> | ||||
|     <v-form class="flex-grow-1" @submit.prevent="submit"> | ||||
|       <div v-if="inputType === 'one-time-password'" class="text-center"> | ||||
|         <p class="text-xs opacity-90">Check your inbox!</p> | ||||
|         <v-otp-input | ||||
|           class="pt-0" | ||||
|           variant="solo" | ||||
|           density="compact" | ||||
|           type="text" | ||||
|           :length="6" | ||||
|           v-model="password" | ||||
|           :loading="loading" | ||||
|         /> | ||||
|       </div> | ||||
|       <v-text-field | ||||
|         v-else | ||||
|         label="Password" | ||||
|         type="password" | ||||
|         variant="solo" | ||||
|         density="comfortable" | ||||
|         :disabled="loading" | ||||
|         v-model="password" | ||||
|       /> | ||||
|  | ||||
|       <v-expand-transition> | ||||
|         <v-alert v-show="error" variant="tonal" type="error" class="text-xs mb-3"> | ||||
|           Something went wrong... {{ error }} | ||||
|         </v-alert> | ||||
|       </v-expand-transition> | ||||
|  | ||||
|       <div class="flex justify-end"> | ||||
|         <v-btn | ||||
|           type="submit" | ||||
|           variant="text" | ||||
|           color="primary" | ||||
|           class="justify-self-end" | ||||
|           append-icon="mdi-arrow-right" | ||||
|           :disabled="loading" | ||||
|         > | ||||
|           Next | ||||
|         </v-btn> | ||||
|       </div> | ||||
|     </v-form> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { computed, ref } from "vue" | ||||
|  | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const password = ref("") | ||||
|  | ||||
| const error = ref<string | null>(null) | ||||
|  | ||||
| const props = defineProps<{ loading?: boolean; currentFactor?: any; ticket?: any }>() | ||||
| const emits = defineEmits(["swap", "update:ticket", "update:loading"]) | ||||
|  | ||||
| async function submit() { | ||||
|   emits("update:loading", true) | ||||
|   const res = await fetch(`${config.public.solarNetworkApi}/cgi/auth/auth/mfa`, { | ||||
|     method: "POST", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify({ | ||||
|       ticket_id: props.ticket?.id, | ||||
|       factor_id: props.currentFactor?.id, | ||||
|       code: password.value, | ||||
|     }), | ||||
|   }) | ||||
|   if (res.status !== 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     const data = await res.json() | ||||
|     error.value = null | ||||
|     password.value = "" | ||||
|     emits("update:ticket", data["ticket"]) | ||||
|     if (data["is_finished"]) emits("swap", "completed") | ||||
|     else emits("swap", "mfa") | ||||
|   } | ||||
|   emits("update:loading", false) | ||||
| } | ||||
|  | ||||
| const inputType = computed(() => { | ||||
|   switch (props.currentFactor?.type) { | ||||
|     case 0: | ||||
|       return "text" | ||||
|     case 1: | ||||
|       return "one-time-password" | ||||
|     default: | ||||
|       return "unknown" | ||||
|   } | ||||
| }) | ||||
| </script> | ||||
							
								
								
									
										89
									
								
								components/auth/FactorPicker.vue
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										89
									
								
								components/auth/FactorPicker.vue
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| <template> | ||||
|   <div class="flex items-center"> | ||||
|     <div class="flex-grow-1"> | ||||
|       <v-card class="mb-3"> | ||||
|         <v-list density="compact" color="primary"> | ||||
|           <v-list-item | ||||
|             v-for="(item, idx) in factors ?? []" | ||||
|             :key="idx" | ||||
|             :prepend-icon="getFactorType(item)?.icon" | ||||
|             :title="getFactorType(item)?.label" | ||||
|             :active="focus === item.id" | ||||
|             :disabled="getFactorAvailable(item)" | ||||
|             @click="focus = item.id" | ||||
|           /> | ||||
|         </v-list> | ||||
|       </v-card> | ||||
|  | ||||
|       <v-expand-transition> | ||||
|         <v-alert v-show="error" variant="tonal" type="error" class="text-xs mb-3"> | ||||
|           Something went wrong... {{ error }} | ||||
|         </v-alert> | ||||
|       </v-expand-transition> | ||||
|  | ||||
|       <div class="flex justify-end"> | ||||
|         <v-btn variant="text" color="primary" class="justify-self-end" append-icon="mdi-arrow-right" @click="submit"> | ||||
|           Next | ||||
|         </v-btn> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { onMounted, ref } from "vue" | ||||
|  | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const focus = ref<number | null>(null) | ||||
| const factors = ref<any[]>([]) | ||||
|  | ||||
| const error = ref<string | null>(null) | ||||
|  | ||||
| const props = defineProps<{ ticket?: any }>() | ||||
| const emits = defineEmits(["swap", "update:loading", "update:currentFactor"]) | ||||
|  | ||||
| async function load() { | ||||
|   emits("update:loading", true) | ||||
|   const res = await fetch(`${config.public.solarNetworkApi}/cgi/auth/auth/factors?ticketId=${props.ticket.id}`) | ||||
|   if (res.status !== 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     factors.value = (await res.json()).filter((e: any) => e.type != 0) | ||||
|   } | ||||
|   emits("update:loading", false) | ||||
| } | ||||
|  | ||||
| onMounted(() => load()) | ||||
|  | ||||
| async function submit() { | ||||
|   if (!focus.value) return | ||||
|  | ||||
|   emits("update:loading", true) | ||||
|   const res = await fetch(`${config.public.solarNetworkApi}/cgi/auth/auth/factors/${focus.value}`, { | ||||
|     method: "POST", | ||||
|   }) | ||||
|   if (res.status !== 200 && res.status !== 204) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     const item = factors.value.find((item: any) => item.id === focus.value) | ||||
|     emits("update:currentFactor", item) | ||||
|     emits("swap", "applicator") | ||||
|     error.value = null | ||||
|     focus.value = null | ||||
|   } | ||||
|   emits("update:loading", false) | ||||
| } | ||||
|  | ||||
| function getFactorType(item: any) { | ||||
|   switch (item.type) { | ||||
|     case 1: | ||||
|       return { icon: "mdi-email-fast", label: "Email Validation" } | ||||
|   } | ||||
| } | ||||
|  | ||||
| function getFactorAvailable(factor: any) { | ||||
|   const blacklist: number[] = props.ticket?.blacklist_factors ?? [] | ||||
|   return blacklist.includes(factor.id) | ||||
| } | ||||
| </script> | ||||
		Reference in New Issue
	
	Block a user