New login form
This commit is contained in:
parent
e88635b09e
commit
4902f089e6
2 changed files with 191 additions and 103 deletions
|
@ -62,10 +62,11 @@ export class ProfileWrapper {
|
|||
|
||||
}
|
||||
|
||||
export interface UserCreateForm {
|
||||
interface UserCreateForm {
|
||||
username: string;
|
||||
message: string;
|
||||
signature: string;
|
||||
password: string | null;
|
||||
message: string | null;
|
||||
signature: string | null;
|
||||
invite_code: string | null;
|
||||
}
|
||||
|
||||
|
@ -83,15 +84,21 @@ export async function createUser(userData: UserCreateForm): Promise<User> {
|
|||
}
|
||||
}
|
||||
|
||||
interface LoginForm {
|
||||
username: string | null;
|
||||
password: string | null;
|
||||
message: string | null;
|
||||
signature: string | null;
|
||||
}
|
||||
|
||||
export async function getAccessToken(
|
||||
message: string,
|
||||
signature: string,
|
||||
loginType: "password" | "eip4361",
|
||||
loginData: LoginForm,
|
||||
): Promise<string> {
|
||||
const url = `${BACKEND_URL}/oauth/token`
|
||||
const tokenRequestData = {
|
||||
grant_type: "eip4361",
|
||||
message: message,
|
||||
signature: signature,
|
||||
grant_type: loginType,
|
||||
...loginData,
|
||||
}
|
||||
const response = await http(url, {
|
||||
method: "POST",
|
||||
|
|
|
@ -8,17 +8,26 @@
|
|||
<br>
|
||||
<router-link :to="{name: 'about-public'}">Learn more <span class="arrow">>></span></router-link>
|
||||
</div>
|
||||
<div class="login">
|
||||
<button @click="login()">Sign In</button>
|
||||
<div v-if="loginErrorMessage" class="error-message">{{ loginErrorMessage }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<form v-if="instance" class="registration-form">
|
||||
<div v-if="isLoading" class="registration-form-loader">
|
||||
<form v-if="instance" class="login-form">
|
||||
<div v-if="isLoading" class="login-form-loader">
|
||||
<loader></loader>
|
||||
</div>
|
||||
<div class="form-title">Want to join?</div>
|
||||
<div class="form-control">
|
||||
<div class="login-type">
|
||||
<button
|
||||
:class="{ active: loginType === 'password' }"
|
||||
@click.prevent="loginType = 'password'; loginErrorMessage = null"
|
||||
>
|
||||
Password
|
||||
</button>
|
||||
<button
|
||||
:class="{ active: loginType === 'eip4361' }"
|
||||
@click.prevent="loginType = 'eip4361'; loginErrorMessage = null"
|
||||
>
|
||||
Ethereum
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-control" v-if="!isRegistered || loginType == 'password'">
|
||||
<div class="input-group">
|
||||
<input
|
||||
id="username"
|
||||
|
@ -30,7 +39,16 @@
|
|||
</div>
|
||||
<div class="form-message">Only letters, numbers and underscores are allowed.</div>
|
||||
</div>
|
||||
<div class="form-control" v-if="!instance.registrations">
|
||||
<div class="form-control" v-if="loginType === 'password'">
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
v-model="password"
|
||||
required
|
||||
placeholder="Password"
|
||||
>
|
||||
</div>
|
||||
<div class="form-control" v-if="!instance.registrations && !isRegistered">
|
||||
<input
|
||||
id="invite-token"
|
||||
v-model="inviteCode"
|
||||
|
@ -38,16 +56,36 @@
|
|||
placeholder="Enter the invite code"
|
||||
>
|
||||
</div>
|
||||
<div class="wallet-required">
|
||||
<div class="wallet-required" v-if="loginType === 'eip4361'">
|
||||
<img :src="require('@/assets/forkawesome/ethereum.svg')">
|
||||
<router-link :to="{ name: 'ethereum' }">Ethereum Wallet</router-link> is required
|
||||
</div>
|
||||
<button
|
||||
v-if="isRegistered"
|
||||
type="submit"
|
||||
:disabled="!username"
|
||||
:disabled="!isLoginFormValid()"
|
||||
@click.prevent="login()"
|
||||
>
|
||||
Sign in
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
type="submit"
|
||||
:disabled="!isLoginFormValid()"
|
||||
@click.prevent="register()"
|
||||
>Sign Up</button>
|
||||
<div v-if="registrationErrorMessage" class="error-message">{{ registrationErrorMessage }}</div>
|
||||
>
|
||||
Sign Up
|
||||
</button>
|
||||
<div class="error-message" v-if="loginErrorMessage" >{{ loginErrorMessage }}</div>
|
||||
<div class="switch-mode">
|
||||
<template v-if="isRegistered">Don't have an account?</template>
|
||||
<template v-else>Already registered?</template>
|
||||
 
|
||||
<button @click.prevent="isRegistered = !isRegistered; loginErrorMessage = null">
|
||||
<template v-if="isRegistered">Sign Up</template>
|
||||
<template v-else>Sign In</template>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,42 +109,78 @@ const router = useRouter()
|
|||
const { setCurrentUser, setAuthToken } = useCurrentUser()
|
||||
const { instance } = $(useInstanceInfo())
|
||||
|
||||
const isRegistered = $ref(true)
|
||||
const loginType = $ref<"password" | "eip4361">("password")
|
||||
const username = $ref("")
|
||||
const password = $ref<string | null>(null)
|
||||
const inviteCode = $ref<string | null>(null)
|
||||
let isLoading = $ref(false)
|
||||
let loginErrorMessage = $ref<string | null>(null)
|
||||
let registrationErrorMessage = $ref<string | null>(null)
|
||||
|
||||
function isLoginFormValid(): boolean {
|
||||
if (!instance) {
|
||||
return false
|
||||
}
|
||||
if (isRegistered) {
|
||||
if (loginType === "password") {
|
||||
return Boolean(username) && Boolean(password)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
const inviteCodeValid = instance.registrations ? true : Boolean(inviteCode)
|
||||
if (loginType === "password") {
|
||||
return Boolean(username) && Boolean(password) && inviteCodeValid
|
||||
} else {
|
||||
return Boolean(username) && inviteCodeValid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function register() {
|
||||
registrationErrorMessage = null
|
||||
loginErrorMessage = null
|
||||
if (!instance) {
|
||||
return
|
||||
}
|
||||
const instanceHost = instance.uri
|
||||
const loginMessage = instance.login_message
|
||||
let userData
|
||||
let loginData
|
||||
if (loginType === "password") {
|
||||
userData = {
|
||||
username,
|
||||
password,
|
||||
message: null,
|
||||
signature: null,
|
||||
invite_code: inviteCode,
|
||||
}
|
||||
loginData = { username, password, message: null, signature: null }
|
||||
} else {
|
||||
const signer = await getWallet()
|
||||
if (!signer) {
|
||||
return
|
||||
}
|
||||
const { message, signature } = await createEip4361_SignedMessage(
|
||||
signer,
|
||||
instanceHost,
|
||||
loginMessage,
|
||||
instance.uri,
|
||||
instance.login_message,
|
||||
)
|
||||
userData = {
|
||||
username,
|
||||
password: null,
|
||||
message,
|
||||
signature,
|
||||
invite_code: inviteCode,
|
||||
}
|
||||
loginData = { username: null, password: null, message, signature }
|
||||
}
|
||||
isLoading = true
|
||||
let user
|
||||
let authToken
|
||||
try {
|
||||
user = await createUser({
|
||||
username: username,
|
||||
message,
|
||||
signature,
|
||||
invite_code: inviteCode,
|
||||
})
|
||||
authToken = await getAccessToken(message, signature)
|
||||
user = await createUser(userData)
|
||||
authToken = await getAccessToken(loginType, loginData)
|
||||
} catch (error: any) {
|
||||
isLoading = false
|
||||
registrationErrorMessage = error.message
|
||||
loginErrorMessage = error.message
|
||||
return
|
||||
}
|
||||
setCurrentUser(user)
|
||||
|
@ -120,28 +194,35 @@ async function login() {
|
|||
if (!instance) {
|
||||
return
|
||||
}
|
||||
const instanceHost = instance.uri
|
||||
const loginMessage = instance.login_message
|
||||
let loginData
|
||||
if (loginType === "password") {
|
||||
loginData = { username, password, message: null, signature: null }
|
||||
} else {
|
||||
const signer = await getWallet()
|
||||
if (!signer) {
|
||||
return
|
||||
}
|
||||
const { message, signature } = await createEip4361_SignedMessage(
|
||||
signer,
|
||||
instanceHost,
|
||||
loginMessage,
|
||||
instance.uri,
|
||||
instance.login_message,
|
||||
)
|
||||
loginData = { username: null, password: null, message, signature }
|
||||
}
|
||||
isLoading = true
|
||||
let user
|
||||
let authToken
|
||||
try {
|
||||
authToken = await getAccessToken(message, signature)
|
||||
authToken = await getAccessToken(loginType, loginData)
|
||||
user = await getCurrentUser(authToken)
|
||||
} catch (error: any) {
|
||||
isLoading = false
|
||||
loginErrorMessage = error.message
|
||||
return
|
||||
}
|
||||
setCurrentUser(user)
|
||||
setAuthToken(authToken)
|
||||
isLoading = false
|
||||
router.push({ name: "home" })
|
||||
}
|
||||
</script>
|
||||
|
@ -152,18 +233,6 @@ async function login() {
|
|||
|
||||
$text-color: #fff;
|
||||
|
||||
button {
|
||||
background-color: #000;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: $text-color;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
padding: 10px 60px;
|
||||
}
|
||||
|
||||
.landing-page {
|
||||
background-color: #000;
|
||||
background-image: url("../assets/startpage.png");
|
||||
|
@ -215,50 +284,21 @@ button {
|
|||
}
|
||||
}
|
||||
|
||||
.login {
|
||||
display: inline-block;
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
|
||||
button {
|
||||
border: 1px solid #979797;
|
||||
box-shadow: 0 2px 16px -5px #6E6E6E;
|
||||
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
border-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: $error-color;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.registration-form {
|
||||
border: 1px solid #979797;
|
||||
.login-form {
|
||||
background-color: #1A1818;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
min-width: $wide-sidebar-width - 50px;
|
||||
padding: 25px 40px;
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
width: $wide-sidebar-width;
|
||||
|
||||
.form-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input,
|
||||
.addon {
|
||||
background-color: #201f1f;
|
||||
background-color: #2E2C2C;
|
||||
border: none;
|
||||
line-height: 18px;
|
||||
padding: 15px;
|
||||
|
@ -297,13 +337,21 @@ button {
|
|||
padding: 0 15px;
|
||||
}
|
||||
|
||||
button {
|
||||
button[type="submit"] {
|
||||
background: linear-gradient(to right, #FF5959, #FF5EAD, #D835FE, #D963FF);
|
||||
background-color: #000;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 16px -5px #BB5CC7;
|
||||
color: $text-color;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
height: 48px;
|
||||
margin-top: 5px;
|
||||
padding: 10px 60px;
|
||||
|
||||
&:hover {
|
||||
&:not([disabled]):hover {
|
||||
background: linear-gradient(to right, #FF7373, #FF78BA, #DD4FFE, #DF7DFF);
|
||||
}
|
||||
}
|
||||
|
@ -331,9 +379,18 @@ button {
|
|||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-mode {
|
||||
text-align: center;
|
||||
|
||||
button {
|
||||
color: $text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.registration-form-loader {
|
||||
.login-form-loader {
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
left: 0;
|
||||
|
@ -352,8 +409,32 @@ button {
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login-type {
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
border: 1px solid #3D3D3D;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 10px 0 0 10px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #3D3D3D;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $screen-breakpoint-medium) {
|
||||
.registration-form {
|
||||
.login-form {
|
||||
padding: 25px;
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +449,7 @@ button {
|
|||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.registration-form {
|
||||
.login-form {
|
||||
margin-right: auto;
|
||||
min-width: auto;
|
||||
|
||||
|
|
Loading…
Reference in a new issue