1
0

Launcher Auth (#450)

* Launcher Auth

* Finish auth

* final fixes
This commit is contained in:
Geometrically
2023-08-04 23:38:34 -07:00
committed by GitHub
parent a35dd67b77
commit 47e28d24c8
38 changed files with 1200 additions and 477 deletions

View File

@@ -35,9 +35,10 @@ async function login() {
const loggedIn = await authenticate_await_completion().catch(handleError)
loginModal.value.hide()
props.nextPage()
props.nextPage(loggedIn[1])
const settings = await get().catch(handleError)
settings.default_user = loggedIn.id
settings.default_user = loggedIn[0].id
await set(settings).catch(handleError)
await mixpanel.track('AccountLogIn')
}
@@ -83,7 +84,7 @@ const openUrl = async () => {
Browser didn't open?
</Button>
</div>
<Button class="transparent" large @click="nextPage"> Next </Button>
<Button class="transparent" large @click="nextPage()"> Next </Button>
</div>
</Card>
</div>

View File

@@ -1,5 +1,5 @@
<script setup>
import { Button, Card, UserIcon, LockIcon } from 'omorphia'
import { Button, Card, UserIcon, LockIcon, MailIcon, Checkbox } from 'omorphia'
import {
DiscordIcon,
GithubIcon,
@@ -8,8 +8,19 @@ import {
SteamIcon,
GitLabIcon,
} from '@/assets/external'
import {
authenticate_begin_flow,
authenticate_await_completion,
login_2fa,
create_account,
login_pass,
get as getCreds,
login_minecraft,
} from '@/helpers/mr_auth.js'
import { handleError, useNotifications } from '@/store/state.js'
import { onMounted, ref } from 'vue'
defineProps({
const props = defineProps({
nextPage: {
type: Function,
required: true,
@@ -18,58 +29,201 @@ defineProps({
type: Function,
required: true,
},
modal: {
type: Boolean,
required: true,
},
flow: {
type: String,
default: null,
},
})
const loggingIn = ref(true)
const twoFactorFlow = ref(null)
const twoFactorCode = ref('')
const email = ref('')
const username = ref('')
const password = ref('')
const confirmPassword = ref('')
const subscribe = ref(true)
async function signInOauth(provider) {
const url = await authenticate_begin_flow(provider).catch(handleError)
await window.__TAURI_INVOKE__('tauri', {
__tauriModule: 'Shell',
message: {
cmd: 'open',
path: url,
},
})
const creds = await authenticate_await_completion().catch(handleError)
if (creds && creds.type === 'two_factor_required') {
twoFactorFlow.value = creds.flow
} else if (creds && creds.session) {
props.nextPage()
}
}
async function signIn2fa() {
const creds = await login_2fa(twoFactorCode.value, twoFactorFlow.value).catch(handleError)
if (creds && creds.session) {
props.nextPage()
}
}
async function signIn() {
const creds = await login_pass(
username.value,
password.value,
window.turnstile.getResponse()
).catch(handleError)
window.turnstile.reset()
if (creds && creds.type === 'two_factor_required') {
twoFactorFlow.value = creds.flow
} else if (creds && creds.session) {
props.nextPage()
}
}
async function createAccount() {
if (password.value !== confirmPassword.value) {
const notifs = useNotifications()
notifs.addNotification({
title: 'An error occurred',
text: 'Passwords do not match!',
type: 'error',
})
return
}
const creds = await create_account(
username.value,
email.value,
password.value,
window.turnstile.getResponse(),
subscribe.value
).catch(handleError)
window.turnstile.reset()
if (creds && creds.session) {
props.nextPage()
}
}
async function goToNextPage() {
const creds = await getCreds().catch(handleError)
if (!creds) {
try {
await login_minecraft(props.flow)
} catch {
/* empty */
}
}
props.nextPage()
}
onMounted(() => {
if (window.turnstile === null || !window.turnstile) {
const script = document.createElement('script')
script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js'
script.async = true
script.defer = true
document.head.appendChild(script)
}
})
</script>
<template>
<Card>
<h1>Login to Modrinth</h1>
<div class="button-grid">
<Button class="discord" large>
<DiscordIcon />
Discord
</Button>
<Button class="github" large>
<GithubIcon />
Github
</Button>
<Button class="white" large>
<MicrosoftIcon />
Microsoft
</Button>
<Button class="google" large>
<GoogleIcon />
Google
</Button>
<Button class="white" large>
<SteamIcon />
Steam
</Button>
<Button class="gitlab" large>
<GitLabIcon />
GitLab
</Button>
</div>
<div class="divider">
<hr />
<p>Or</p>
</div>
<div class="iconified-input username">
<UserIcon />
<input type="text" placeholder="Email or username" />
</div>
<div class="iconified-input">
<LockIcon />
<input type="password" placeholder="Password" />
</div>
<div class="link-row">
<a class="button-base"> Create account </a>
<a class="button-base"> Forgot password? </a>
</div>
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAHWfmKCm7cUG869"></div>
<template v-if="twoFactorFlow">
<h1>Enter two-factor code</h1>
<p>Please enter a two-factor code to proceed.</p>
<input v-model="twoFactorCode" maxlength="11" type="text" placeholder="Enter code..." />
</template>
<template v-else>
<h1 v-if="loggingIn">Login to Modrinth</h1>
<h1 v-else>Create an account</h1>
<div class="button-grid">
<Button class="discord" large @click="signInOauth('discord')">
<DiscordIcon />
Discord
</Button>
<Button class="github" large @click="signInOauth('github')">
<GithubIcon />
Github
</Button>
<Button class="white" large @click="signInOauth('microsoft')">
<MicrosoftIcon />
Microsoft
</Button>
<Button class="google" large @click="signInOauth('google')">
<GoogleIcon />
Google
</Button>
<Button class="white" large @click="signInOauth('steam')">
<SteamIcon />
Steam
</Button>
<Button class="gitlab" large @click="signInOauth('gitlab')">
<GitLabIcon />
GitLab
</Button>
</div>
<div class="divider">
<hr />
<p>Or</p>
</div>
<div v-if="!loggingIn" class="iconified-input username">
<MailIcon />
<input v-model="email" type="text" placeholder="Email" />
</div>
<div class="iconified-input username">
<UserIcon />
<input
v-model="username"
type="text"
:placeholder="loggingIn ? 'Email or username' : 'Username'"
/>
</div>
<div class="iconified-input" :class="{ username: !loggingIn }">
<LockIcon />
<input v-model="password" type="password" placeholder="Password" />
</div>
<div v-if="!loggingIn" class="iconified-input username">
<LockIcon />
<input v-model="confirmPassword" type="password" placeholder="Confirm password" />
</div>
<Checkbox
v-if="!loggingIn"
v-model="subscribe"
class="subscribe-btn"
label="Subscribe to updates about Modrinth"
/>
<div class="link-row">
<a v-if="loggingIn" class="button-base" @click="loggingIn = false"> Create account </a>
<a v-else class="button-base" @click="loggingIn = true">Sign in</a>
<a class="button-base" href="https://staging.modrinth.com/auth/reset-password">
Forgot password?
</a>
</div>
</template>
<div class="button-row">
<Button class="transparent" large @click="prevPage"> Back </Button>
<Button color="primary" large> Login </Button>
<Button class="transparent" large @click="nextPage"> Next </Button>
<Button class="transparent" large @click="prevPage"> {{ modal ? 'Close' : 'Back' }} </Button>
<Button v-if="twoFactorCode" color="primary" large @click="signIn2fa"> Login </Button>
<Button v-else-if="loggingIn" color="primary" large @click="signIn"> Login </Button>
<Button v-else color="primary" large @click="createAccount"> Create account </Button>
<Button class="transparent" large @click="goToNextPage">
{{ modal ? 'Continue' : 'Next' }}
</Button>
</div>
</Card>
</template>
@@ -179,4 +333,10 @@ defineProps({
padding: var(--gap-md) 0;
}
}
:deep {
.checkbox {
border: none;
}
}
</style>

File diff suppressed because one or more lines are too long

View File

@@ -32,7 +32,7 @@ import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
import { auto_install_java, get_jre } from '@/helpers/jre.js'
import { handleError } from '@/store/notifications.js'
import ImportingCard from '@/components/ui/tutorial/ImportingCard.vue'
// import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
import PreImportScreen from '@/components/ui/tutorial/PreImportScreen.vue'
const phase = ref(0)
@@ -45,6 +45,8 @@ const props = defineProps({
},
})
const flow = ref('')
const nextPhase = () => {
phase.value++
mixpanel.track('TutorialPhase', { page: phase.value })
@@ -54,9 +56,13 @@ const prevPhase = () => {
phase.value--
}
const nextPage = () => {
const nextPage = (newFlow) => {
page.value++
mixpanel.track('OnboardingPage', { page: page.value })
if (newFlow) {
flow.value = newFlow
}
}
const endOnboarding = () => {
@@ -70,7 +76,7 @@ const prevPage = () => {
const finishOnboarding = async () => {
mixpanel.track('OnboardingFinish')
const settings = await get()
settings.onboarded_new = true
settings.fully_onboarded = true
await set(settings)
props.finish()
}
@@ -119,14 +125,20 @@ onMounted(async () => {
<Button color="primary" @click="nextPage"> Get started </Button>
</GalleryImage>
<LoginCard v-else-if="page === 2" :next-page="nextPage" :prev-page="prevPage" />
<!-- <ModrinthLoginScreen v-else-if="page === 3" :next-page="nextPage" :prev-page="prevPage" />-->
<PreImportScreen
<ModrinthLoginScreen
v-else-if="page === 3"
:modal="false"
:next-page="nextPage"
:prev-page="prevPage"
:flow="flow"
/>
<PreImportScreen
v-else-if="page === 4"
:next-page="endOnboarding"
:prev-page="prevPage"
:import-page="nextPage"
/>
<ImportingCard v-else-if="page === 4" :next-page="endOnboarding" :prev-page="prevPage" />
<ImportingCard v-else-if="page === 5" :next-page="endOnboarding" :prev-page="prevPage" />
</div>
<div v-else class="container">
<StickyTitleBar v-if="phase === 9" />