Création d'un composable pour la gestion de la crypto, mise en place de l'initialisation de la première liste et sauvegarde de celle-ci. TODO NEXT : Récupération de la liste en la déchiffrant, mise en place d'une masterKey dérivée du mot de passe pour les utilisateurs non google, basculement vers la page des listes après connexion google etc.

This commit is contained in:
2026-03-15 08:15:42 +01:00
parent d9d84634e8
commit 5d582461e2
10 changed files with 562 additions and 29 deletions

View File

@@ -0,0 +1,177 @@
<script setup lang="ts">
import {
useTokenClient,
type TokenClientSuccessResponse,
type TokenClientErrorResponse
} from "vue3-google-signin";
const authStore = useAuthStore();
const listStore = useListStore();
const crypto = useCryptoStore()
/**
* Logique Google Drive (appDataFolder)
*/
const handleDriveKey = async (accessToken: string) => {
const fileName = 'list_master_key';
const baseUrl = 'https://www.googleapis.com/drive/v3/files';
const authHeader = { Authorization: `Bearer ${accessToken}` };
try {
const searchParams = new URLSearchParams({
q: `name = '${fileName}'`,
spaces: 'appDataFolder',
fields: 'files(id, name)'
});
const searchRes = await $fetch<any>(`${baseUrl}?${searchParams}`, { headers: authHeader });
if (searchRes.files && searchRes.files.length > 0) {
const fileId = searchRes.files[0].id;
// 1. Récupérer le contenu en tant que Blob ou ArrayBuffer pour préserver les bytes
const blob = await $fetch<Blob>(`${baseUrl}/${fileId}?alt=media`, {
headers: authHeader,
responseType: 'blob'
});
// Convertir le Blob en Uint8Array
const arrayBuffer = await blob.arrayBuffer();
const keyUint8 = new Uint8Array(arrayBuffer);
listStore.setMasterKey(keyUint8);
console.log("Clé binaire récupérée du Drive : " + keyUint8);
} else {
// 2. Création avec le type Uint8Array
const newKey = crypto.generateMasterKey();
const metadata = {
name: fileName,
parents: ['appDataFolder']
};
const formData = new FormData();
// On spécifie explicitement 'application/octet-stream' pour la clé binaire
formData.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
formData.append('file', new Blob([newKey as any], { type: 'application/octet-stream' }));
await $fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', {
method: 'POST',
headers: authHeader,
body: formData
});
listStore.setMasterKey(newKey);
console.log("Nouvelle clé binaire générée et stockée : " + newKey);
}
} catch (err) {
console.error("Erreur Google Drive:", err);
}
};
/**
* Succès de l'authentification Google
*/
const handleSuccess = async (response: TokenClientSuccessResponse) => {
const accessToken = response.access_token;
console.log(response.access_token)
try {
// // A. Vérification côté WordPress (Login)
// // Note : On envoie l'accessToken ou on utilise l'ID Token si configuré.
// // Si ton API WP attendait un 'code', il faudra l'adapter pour vérifier l'access_token ou l'email.
const data = await $fetch<any>('/api-wp/auth/google', {
method: 'POST',
body: { access_token: accessToken }
});
// Stockage des infos WP
authStore.setTokenCookie(data.token);
authStore.setUserCookie(data.user);
// B. Gestion de la clé sur Google Drive (Entièrement géré par le Front)
await handleDriveKey(accessToken);
listStore.processRawData(data.lists);
} catch (error) {
console.error("Erreur login WordPress:", error);
}
};
const handleError = (error: TokenClientErrorResponse) => {
console.error("Erreur Google:", error);
};
// Initialisation du Token Client (Implicit Flow - Pas de secret nécessaire)
const { login } = useTokenClient({
scope: 'openid email profile https://www.googleapis.com/auth/drive.appdata',
onSuccess: handleSuccess,
onError: handleError,
});
</script>
<template>
<button @click="() => login()" class="g-signin-button">
<div class="g-logo-wrapper">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class="g-logo">
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
<path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
<path fill="none" d="M0 0h48v48H0z"/>
</svg>
</div>
<span class="g-button-text">{{ $t('googleBtnTxt') }}</span>
</button>
</template>
<style scoped>
.g-signin-button {
margin:auto;
margin-top:1em;
display: flex;
align-items: center;
justify-content: flex-start;
background-color: #ffffff;
color: #757575;
height: 40px;
width: auto;
border: 1px solid #dadce0;
border-radius: 4px;
padding: 0em;
font-family: 'Roboto', arial, sans-serif;
font-weight: 500;
font-size: 14px;
cursor: pointer;
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.302), 0 1px 3px 1px rgba(60,64,67,0.149);
transition: background-color .218s, border-color .218s, box-shadow .218s;
overflow: hidden;
}
.g-signin-button:hover {
background-color: hsl(210, 17%, 90%);
border-color: #d2d2d2;
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.302), 0 1px 3px 1px rgba(60,64,67,0.149);
}
.g-signin-button:active {
background-color: #eeeeee;
}
.g-logo-wrapper {
padding: 11px;
height: 100%;
display: flex;
align-items: center;
}
.g-logo {
width: 18px;
height: 18px;
}
.g-button-text {
padding: 0 12px 0 10px;
white-space: nowrap;
}
</style>

View File

@@ -24,7 +24,7 @@
authStore.setTokenCookie(data.token);
authStore.setUserCookie(data.user);
listStore.lists = data.lists
console.log(listStore.lists) //ok
//console.log(listStore.lists) //ok
} catch (error) {
console.error("Erreur côté WordPress:", error);
}