New login form

This commit is contained in:
silverpill 2022-08-13 22:56:16 +00:00
parent e88635b09e
commit 4902f089e6
2 changed files with 191 additions and 103 deletions

View file

@ -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",

View file

@ -8,17 +8,26 @@
<br>
<router-link :to="{name: 'about-public'}">Learn more <span class="arrow">&gt;&gt;</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>
&thinsp;
<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
const signer = await getWallet()
if (!signer) {
return
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,
instance.uri,
instance.login_message,
)
userData = {
username,
password: null,
message,
signature,
invite_code: inviteCode,
}
loginData = { username: null, password: null, message, signature }
}
const { message, signature } = await createEip4361_SignedMessage(
signer,
instanceHost,
loginMessage,
)
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
const signer = await getWallet()
if (!signer) {
return
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,
instance.uri,
instance.login_message,
)
loginData = { username: null, password: null, message, signature }
}
const { message, signature } = await createEip4361_SignedMessage(
signer,
instanceHost,
loginMessage,
)
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;