Added cookies managment for Auth purpose

This commit is contained in:
Florian Sylvain
2022-12-23 20:35:55 +01:00
parent eee7c1e067
commit 96c3f596ae
6 changed files with 171 additions and 40 deletions
+39 -11
View File
@@ -6,7 +6,11 @@
--semibold-weight: 600; --semibold-weight: 600;
--bold-weight: 700; --bold-weight: 700;
--radius: 10px; --radius: 15px;
--font-body: 16px;
--font-small: 14px;
--font-verysmall: 12px;
--primary: #2CD358; --primary: #2CD358;
--primary-dark: #00BA2A; --primary-dark: #00BA2A;
@@ -15,6 +19,7 @@
--secondary-dark: #BA0094; --secondary-dark: #BA0094;
--secondary-light: #E88DCA; --secondary-light: #E88DCA;
--neutral-dark: #121212; --neutral-dark: #121212;
--neutral-medium: #707070;
--neutral-light: #aaaaaa; --neutral-light: #aaaaaa;
} }
@@ -24,8 +29,11 @@ body {
h1 { h1 {
font-family: 'Frank Ruhl Libre', serif; font-family: 'Frank Ruhl Libre', serif;
font-size: 40px;
} }
h2 { font-size: 32px; }
h3 { font-size: 24px; }
h4 { font-size: 18px; }
h2, h3, h4, h5, h6 { h2, h3, h4, h5, h6 {
font-family: 'Nunito', sans-serif; font-family: 'Nunito', sans-serif;
} }
@@ -37,33 +45,53 @@ h2, h3, h4, h5, h6 {
color: var(--neutral-dark); color: var(--neutral-dark);
} }
.label-input { .label-input,
.label-input-error {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.label-input label { .label-input label,
font-weight: var(--medium-weight); .label-input-error label {
font-size: var(--font-body);
font-weight: var(--semibold-weight);
} }
.label-input input { .label-input p,
height: 32px; .label-input-error p {
padding: 0 8px; color: var(--neutral-medium);
font-size: var(--font-body);
font-weight: var(--medium-weight);
margin: 0;
}
.label-input input,
.label-input-error input {
padding: 3px 16px;
font-size: var(--font-body);
border: solid 1.25px var(--neutral-light); border: solid 1.25px var(--neutral-light);
border-radius: var(--radius); border-radius: var(--radius);
} }
.label-input-error input {
border: solid 1.75px red;
}
.label-input-error p {
color: red;
}
.button-primary { .button-primary {
border-radius: var(--radius); border-radius: var(--radius);
border: none; border: none;
height: 32px; padding: 3px 16px;
font-size: var(--font-body);
font-weight: var(--medium-weight);
background-color: var(--primary); background-color: var(--primary);
font-weight: var(--medium-weight);
cursor: pointer; cursor: pointer;
} }
+18 -2
View File
@@ -1,7 +1,8 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory, type NavigationGuard } from 'vue-router'
import Home from '@/views/Home.vue' import Home from '@/views/Home.vue'
import Debug from '@/views/Debug.vue' import Debug from '@/views/Debug.vue'
import Login from '@/views/Login.vue' import Login from '@/views/Login.vue'
import { useAuthStore } from '@/stores/AuthStore'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@@ -9,7 +10,7 @@ const router = createRouter({
{ {
path: '/', path: '/',
name: 'login', name: 'login',
component: Login component: Login,
}, },
{ {
path: '/debug', path: '/debug',
@@ -24,4 +25,19 @@ const router = createRouter({
] ]
}) })
router.beforeEach(async (to, from) => {
const isTokenValid = useAuthStore().isValid()
if (to.name === 'login') {
if (isTokenValid) return {
name: 'home'
}
} else {
if (!isTokenValid) {
return {
name: 'login',
}
}
}
})
export default router export default router
+33 -1
View File
@@ -1,5 +1,7 @@
import { getCookie } from "@/utils/cookies";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { ref } from "vue"; import { ref } from "vue";
import { useErrorsStore } from "./ErrorsStore";
export interface jwtFormat { export interface jwtFormat {
code: number, code: number,
@@ -11,5 +13,35 @@ export const useAuthStore = defineStore("AuthStore", () => {
const expire = ref("") const expire = ref("")
const token = ref("") const token = ref("")
return { expire, token } function isSet(): boolean {
return token.value !== undefined && token.value !== ""
}
function isExpired(): boolean {
const tokenDate = new Date(expire.value)
const currentDate = new Date()
if (currentDate.getTime() > tokenDate.getTime()) {
useErrorsStore().sessionExpired = true
return true
}
return false
}
function isValid(): boolean {
return isSet() && !isExpired()
}
function initStore(): void {
const JWTtoken = getCookie('JWTtoken')
const JWTexpire = getCookie('JWTexpire')
if (JWTtoken !== "" && JWTexpire !== "") {
token.value = JWTtoken
expire.value = JWTexpire
}
}
initStore()
return { expire, token, isValid }
}) })
+14
View File
@@ -0,0 +1,14 @@
import { defineStore } from "pinia";
import { ref, type Ref } from "vue";
interface storeErrors {
sessionExpired: boolean
}
export const useErrorsStore = defineStore("ErrorsStore", () => {
const errors: Ref<storeErrors> = ref({
sessionExpired: false
})
return errors
})
+13
View File
@@ -0,0 +1,13 @@
export interface cookie {
key: string,
value: string,
expire: string
}
export function setCookie(cookieData: cookie): void {
document.cookie = `${cookieData.key}=${cookieData.value}; SameSite=Strict; Expires=${cookieData.expire};Secure`
}
export function getCookie(cookieName: string): string {
return document.cookie.match('(^|;)\\s*' + cookieName + '\\s*=\\s*([^;]+)')?.pop() || ''
}
+35 -7
View File
@@ -1,22 +1,48 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAuthStore, type jwtFormat } from '@/stores/AuthStore'; import { useAuthStore, type jwtFormat } from '@/stores/AuthStore';
import { useErrorsStore } from '@/stores/ErrorsStore';
import { setCookie } from '@/utils/cookies';
import { ref, type Ref } from 'vue'; import { ref, type Ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
const isTokenOK: Ref<boolean|undefined> = ref(undefined) const isTokenOK: Ref<boolean|undefined> = ref(undefined)
const router = useRouter() const router = useRouter()
const authStore = useAuthStore()
function updateJWTcookies(JWTdata: jwtFormat): void {
const cookieExpire = new Date(JWTdata.expire)
cookieExpire.setDate(cookieExpire.getDate() + 1)
setCookie({
key: 'JWTtoken',
value: JWTdata.token,
expire: cookieExpire.toString()
})
setCookie({
key: 'JWTexpire',
value: JWTdata.expire,
expire: cookieExpire.toString()
})
}
function disableErrors(): void {
useErrorsStore().sessionExpired = false
}
function jwtHandler(apiResponse: jwtFormat): void { function jwtHandler(apiResponse: jwtFormat): void {
if (apiResponse.code !== 200) { if (apiResponse.code !== 200) {
console.error(apiResponse) console.error(apiResponse)
isTokenOK.value = false
return return
} }
useAuthStore().token = apiResponse.token updateJWTcookies(apiResponse)
useAuthStore().expire = apiResponse.expire disableErrors()
authStore.token = apiResponse.token
authStore.expire = apiResponse.expire
isTokenOK.value = true isTokenOK.value = true
} }
//TODO rempalcer tous les :8080 par la var d'env du port de l'API (sinon c un peu dommage)
function login(email: string, password: string): void { function login(email: string, password: string): void {
fetch(`http://${__APP_ENV__.APP_HOST_ADDRESS}:${__APP_ENV__.APP_API_PORT}/login/`, { fetch(`http://${__APP_ENV__.APP_HOST_ADDRESS}:${__APP_ENV__.APP_API_PORT}/login/`, {
method: "POST", method: "POST",
@@ -46,13 +72,15 @@ const password = ref('')
<form @submit.prevent="login(email, password)"> <form @submit.prevent="login(email, password)">
<div class="inputs-group"> <div class="inputs-group">
<div class="label-input"> <div :class="`label-input${isTokenOK === false ? '-error' : ''}`">
<label for="email">Adresse mail</label> <label for="email">Adresse mail</label>
<input id="email" name="email" placeholder="E-mail" type="text" v-model="email"> <input id="email" name="email" placeholder="E-mail" type="text" v-model="email">
</div> </div>
<div class="label-input"> <div :class="`label-input${isTokenOK === false ? '-error' : ''}`">
<label for="password">Mot de passe</label> <label for="password">Mot de passe</label>
<input id="password" name="password" placeholder="Mot de passe" type="password" v-model="password"> <input id="password" name="password" placeholder="Mot de passe" type="password" v-model="password">
<p v-if="isTokenOK === false"> E-mail et/ou mot de passe incorrect(s).</p>
<p v-else-if="useErrorsStore().sessionExpired"> Votre session a expiré.</p>
</div> </div>
</div> </div>
<button class="button-primary" type="submit">Se connecter</button> <button class="button-primary" type="submit">Se connecter</button>
@@ -74,8 +102,8 @@ h2 {
} }
.login-form { .login-form {
width: 100%; width: fit-content;
max-width: 350px; max-width: 100%;
padding: 32px; padding: 32px;
} }