add google sso
This commit is contained in:
parent
3e85733f4a
commit
191b8a4da2
@ -18,6 +18,7 @@
|
||||
"dotenv": "^16.3.1",
|
||||
"fastify": "^4.19.0",
|
||||
"fastify-plugin": "^4.5.0",
|
||||
"google-auth-library": "^9.15.1",
|
||||
"openai": "^4.0.0",
|
||||
"stripe": "^12.12.0"
|
||||
},
|
||||
|
||||
138
backend/pnpm-lock.yaml
generated
138
backend/pnpm-lock.yaml
generated
@ -32,6 +32,9 @@ importers:
|
||||
fastify-plugin:
|
||||
specifier: ^4.5.0
|
||||
version: 4.5.1
|
||||
google-auth-library:
|
||||
specifier: ^9.15.1
|
||||
version: 9.15.1
|
||||
openai:
|
||||
specifier: ^4.0.0
|
||||
version: 4.86.2
|
||||
@ -133,6 +136,10 @@ packages:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
|
||||
agent-base@7.1.3:
|
||||
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
agentkeepalive@4.6.0:
|
||||
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
@ -188,10 +195,16 @@ packages:
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
bcrypt@5.1.1:
|
||||
resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
bignumber.js@9.1.2:
|
||||
resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
@ -206,6 +219,9 @@ packages:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -294,6 +310,9 @@ packages:
|
||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
extend@3.0.2:
|
||||
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||
|
||||
fast-content-type-parse@1.1.0:
|
||||
resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
|
||||
|
||||
@ -385,6 +404,14 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
gaxios@6.7.1:
|
||||
resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
gcp-metadata@6.1.1:
|
||||
resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -401,10 +428,22 @@ packages:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
google-auth-library@9.15.1:
|
||||
resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
google-logging-utils@0.0.2:
|
||||
resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
gtoken@7.1.0:
|
||||
resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
has-flag@3.0.0:
|
||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||
engines: {node: '>=4'}
|
||||
@ -428,6 +467,10 @@ packages:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
https-proxy-agent@7.0.6:
|
||||
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
humanize-ms@1.2.1:
|
||||
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
||||
|
||||
@ -465,12 +508,25 @@ packages:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
is-stream@2.0.1:
|
||||
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
json-bigint@1.0.0:
|
||||
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
|
||||
|
||||
json-schema-ref-resolver@1.0.1:
|
||||
resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==}
|
||||
|
||||
json-schema-traverse@1.0.0:
|
||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||
|
||||
jwa@2.0.0:
|
||||
resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==}
|
||||
|
||||
jws@4.0.0:
|
||||
resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
|
||||
|
||||
light-my-request@5.14.0:
|
||||
resolution: {integrity: sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==}
|
||||
|
||||
@ -782,6 +838,10 @@ packages:
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
uuid@9.0.1:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
hasBin: true
|
||||
|
||||
web-streams-polyfill@4.0.0-beta.3:
|
||||
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
||||
engines: {node: '>= 14'}
|
||||
@ -920,6 +980,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
agent-base@7.1.3: {}
|
||||
|
||||
agentkeepalive@4.6.0:
|
||||
dependencies:
|
||||
humanize-ms: 1.2.1
|
||||
@ -971,6 +1033,8 @@ snapshots:
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
bcrypt@5.1.1:
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.11
|
||||
@ -979,6 +1043,8 @@ snapshots:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
bignumber.js@9.1.2: {}
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
bn.js@4.12.1: {}
|
||||
@ -992,6 +1058,8 @@ snapshots:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
buffer-equal-constant-time@1.0.1: {}
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@ -1071,6 +1139,8 @@ snapshots:
|
||||
|
||||
event-target-shim@5.0.1: {}
|
||||
|
||||
extend@3.0.2: {}
|
||||
|
||||
fast-content-type-parse@1.1.0: {}
|
||||
|
||||
fast-decode-uri-component@1.0.1: {}
|
||||
@ -1192,6 +1262,26 @@ snapshots:
|
||||
strip-ansi: 6.0.1
|
||||
wide-align: 1.1.5
|
||||
|
||||
gaxios@6.7.1:
|
||||
dependencies:
|
||||
extend: 3.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
is-stream: 2.0.1
|
||||
node-fetch: 2.7.0
|
||||
uuid: 9.0.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
gcp-metadata@6.1.1:
|
||||
dependencies:
|
||||
gaxios: 6.7.1
|
||||
google-logging-utils: 0.0.2
|
||||
json-bigint: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
@ -1223,8 +1313,30 @@ snapshots:
|
||||
once: 1.4.0
|
||||
path-is-absolute: 1.0.1
|
||||
|
||||
google-auth-library@9.15.1:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
gaxios: 6.7.1
|
||||
gcp-metadata: 6.1.1
|
||||
gtoken: 7.1.0
|
||||
jws: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
google-logging-utils@0.0.2: {}
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
gtoken@7.1.0:
|
||||
dependencies:
|
||||
gaxios: 6.7.1
|
||||
jws: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
has-flag@3.0.0: {}
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
@ -1246,6 +1358,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
https-proxy-agent@7.0.6:
|
||||
dependencies:
|
||||
agent-base: 7.1.3
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
humanize-ms@1.2.1:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
@ -1275,12 +1394,29 @@ snapshots:
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
is-stream@2.0.1: {}
|
||||
|
||||
json-bigint@1.0.0:
|
||||
dependencies:
|
||||
bignumber.js: 9.1.2
|
||||
|
||||
json-schema-ref-resolver@1.0.1:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
|
||||
json-schema-traverse@1.0.0: {}
|
||||
|
||||
jwa@2.0.0:
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
jws@4.0.0:
|
||||
dependencies:
|
||||
jwa: 2.0.0
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
light-my-request@5.14.0:
|
||||
dependencies:
|
||||
cookie: 0.7.2
|
||||
@ -1581,6 +1717,8 @@ snapshots:
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
uuid@9.0.1: {}
|
||||
|
||||
web-streams-polyfill@4.0.0-beta.3: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Made the column `name` on table `User` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `stripeId` on table `User` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_User" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"email" TEXT NOT NULL,
|
||||
"password" TEXT,
|
||||
"name" TEXT NOT NULL,
|
||||
"googleId" TEXT,
|
||||
"stripeId" TEXT NOT NULL,
|
||||
"subscription" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_User" ("createdAt", "email", "id", "name", "password", "stripeId", "subscription", "updatedAt") SELECT "createdAt", "email", "id", "name", "password", "stripeId", "subscription", "updatedAt" FROM "User";
|
||||
DROP TABLE "User";
|
||||
ALTER TABLE "new_User" RENAME TO "User";
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
CREATE UNIQUE INDEX "User_googleId_key" ON "User"("googleId");
|
||||
CREATE UNIQUE INDEX "User_stripeId_key" ON "User"("stripeId");
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
@ -10,10 +10,11 @@ datasource db {
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
password String
|
||||
name String?
|
||||
subscription String @default("free") // "free" ou "premium"
|
||||
stripeId String?
|
||||
password String? // Optionnel pour les utilisateurs Google
|
||||
name String
|
||||
googleId String? @unique // ID Google pour SSO
|
||||
stripeId String @unique
|
||||
subscription String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
recipes Recipe[]
|
||||
|
||||
8
backend/src/plugins/google-auth.js
Normal file
8
backend/src/plugins/google-auth.js
Normal file
@ -0,0 +1,8 @@
|
||||
const fp = require('fastify-plugin');
|
||||
const { OAuth2Client } = require('google-auth-library');
|
||||
|
||||
module.exports = fp(async function (fastify, opts) {
|
||||
const googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
|
||||
|
||||
fastify.decorate('googleClient', googleClient);
|
||||
});
|
||||
@ -108,4 +108,75 @@ module.exports = async function (fastify, opts) {
|
||||
return reply.code(500).send({ error: 'Erreur lors de la connexion' });
|
||||
}
|
||||
});
|
||||
|
||||
fastify.post('/google-auth', {
|
||||
schema: {
|
||||
body: {
|
||||
type: 'object',
|
||||
required: ['token'],
|
||||
properties: {
|
||||
token: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}, async (request, reply) => {
|
||||
const { token } = request.body;
|
||||
|
||||
try {
|
||||
// Utiliser le token d'accès pour obtenir les informations utilisateur
|
||||
// au lieu d'essayer de le vérifier comme un ID token
|
||||
const response = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Impossible de récupérer les informations utilisateur');
|
||||
}
|
||||
|
||||
const userData = await response.json();
|
||||
const { email, name, sub: googleId } = userData;
|
||||
|
||||
// Vérifier si l'utilisateur existe déjà
|
||||
let user = await fastify.prisma.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
// Créer un client Stripe
|
||||
const customer = await fastify.createCustomer(email, name);
|
||||
|
||||
// Créer l'utilisateur
|
||||
user = await fastify.prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
name,
|
||||
googleId,
|
||||
stripeId: customer.id
|
||||
}
|
||||
});
|
||||
} else if (!user.googleId) {
|
||||
// Mettre à jour l'utilisateur existant avec l'ID Google
|
||||
user = await fastify.prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { googleId }
|
||||
});
|
||||
}
|
||||
|
||||
// Générer un token JWT
|
||||
const jwtToken = fastify.jwt.sign({ id: user.id }, { expiresIn: '7d' });
|
||||
|
||||
return {
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
subscription: user.subscription
|
||||
},
|
||||
token: jwtToken
|
||||
};
|
||||
} catch (error) {
|
||||
fastify.log.error(error);
|
||||
return reply.code(500).send({ error: 'Erreur lors de l\'authentification Google' });
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -14,10 +14,10 @@ fastify.register(require('@fastify/cors'), {
|
||||
allowedHeaders: ['Content-Type', 'Authorization'],
|
||||
credentials: true
|
||||
});
|
||||
|
||||
fastify.register(require('./plugins/auth'));
|
||||
fastify.register(require('./plugins/stripe'));
|
||||
fastify.register(require('./plugins/ai'));
|
||||
fastify.register(require('./plugins/google-auth'));
|
||||
|
||||
// Routes
|
||||
fastify.register(require('./routes/auth'), { prefix: '/auth' });
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"build-no-error": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
@ -19,6 +20,7 @@
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@radix-ui/react-toast": "^1.2.6",
|
||||
"@react-oauth/google": "^0.12.1",
|
||||
"@tailwindcss/vite": "^4.0.12",
|
||||
"axios": "^1.8.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@ -47,4 +49,4 @@
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
14
frontend/pnpm-lock.yaml
generated
14
frontend/pnpm-lock.yaml
generated
@ -35,6 +35,9 @@ importers:
|
||||
'@radix-ui/react-toast':
|
||||
specifier: ^1.2.6
|
||||
version: 1.2.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@react-oauth/google':
|
||||
specifier: ^0.12.1
|
||||
version: 0.12.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.0.12
|
||||
version: 4.0.12(vite@6.2.1(@types/node@22.13.9)(jiti@2.4.2)(lightningcss@1.29.2))
|
||||
@ -806,6 +809,12 @@ packages:
|
||||
'@radix-ui/rect@1.1.0':
|
||||
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
|
||||
|
||||
'@react-oauth/google@0.12.1':
|
||||
resolution: {integrity: sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.34.9':
|
||||
resolution: {integrity: sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==}
|
||||
cpu: [arm]
|
||||
@ -2533,6 +2542,11 @@ snapshots:
|
||||
|
||||
'@radix-ui/rect@1.1.0': {}
|
||||
|
||||
'@react-oauth/google@0.12.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.34.9':
|
||||
optional: true
|
||||
|
||||
|
||||
@ -77,4 +77,14 @@ export const isAuthenticated = (): boolean => {
|
||||
// Récupérer le token
|
||||
export const getToken = (): string | null => {
|
||||
return localStorage.getItem('token');
|
||||
};
|
||||
};
|
||||
|
||||
export const googleAuth = async (token: string) => {
|
||||
const response = await base.post('/auth/google-auth', { token });
|
||||
|
||||
if (!response.data.token) {
|
||||
throw new Error('Erreur lors de l\'authentification Google');
|
||||
}
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { useState } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useGoogleLogin } from "@react-oauth/google"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { login } from "@/api/auth"
|
||||
import { login, googleAuth } from "@/api/auth"
|
||||
|
||||
export function LoginForm({
|
||||
className,
|
||||
...props
|
||||
@ -36,6 +38,33 @@ export function LoginForm({
|
||||
}
|
||||
}
|
||||
|
||||
const handleGoogleLogin = useGoogleLogin({
|
||||
onSuccess: async (tokenResponse) => {
|
||||
setLoading(true)
|
||||
setError("")
|
||||
|
||||
try {
|
||||
const response = await googleAuth(tokenResponse.access_token)
|
||||
|
||||
if (!response.user) {
|
||||
throw new Error("Échec de la connexion")
|
||||
}
|
||||
|
||||
localStorage.setItem("token", response.token)
|
||||
|
||||
navigate("/")
|
||||
} catch (err) {
|
||||
console.error("Erreur de connexion Google:", err)
|
||||
setError(err instanceof Error ? err.message : "Une erreur est survenue")
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
setError("Échec de l'authentification Google")
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<form className={cn("flex flex-col gap-6", className)} {...props} onSubmit={handleSubmit}>
|
||||
<div className="flex flex-col items-center gap-2 text-center">
|
||||
@ -87,11 +116,29 @@ export function LoginForm({
|
||||
Ou continuer avec
|
||||
</span>
|
||||
</div>
|
||||
<Button variant="outline" className="w-full">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
onClick={() => handleGoogleLogin()}
|
||||
disabled={loading}
|
||||
>
|
||||
<svg className="mr-2 h-4 w-4" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
fill="currentColor"
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
fill="#4285F4"
|
||||
/>
|
||||
<path
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
fill="#34A853"
|
||||
/>
|
||||
<path
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
fill="#FBBC05"
|
||||
/>
|
||||
<path
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
fill="#EA4335"
|
||||
/>
|
||||
</svg>
|
||||
Se connecter avec Google
|
||||
|
||||
@ -5,6 +5,8 @@ import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { register } from "@/api/auth"
|
||||
import { useGoogleLogin } from '@react-oauth/google'
|
||||
import { googleAuth } from "@/api/auth"
|
||||
|
||||
export function RegisterForm({
|
||||
className,
|
||||
@ -39,6 +41,30 @@ export function RegisterForm({
|
||||
}
|
||||
}
|
||||
|
||||
const handleGoogleLogin = useGoogleLogin({
|
||||
onSuccess: async (tokenResponse) => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
|
||||
try {
|
||||
const response = await googleAuth(tokenResponse.access_token);
|
||||
|
||||
if (response.token) {
|
||||
localStorage.setItem("token", response.token);
|
||||
}
|
||||
|
||||
navigate("/recipes");
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Une erreur est survenue");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
onError: (errorResponse) => {
|
||||
setError("Échec de l'authentification Google");
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<form className={cn("flex flex-col gap-6", className)} {...props} onSubmit={handleSubmit}>
|
||||
<div className="flex flex-col items-center gap-2 text-center">
|
||||
@ -101,12 +127,31 @@ export function RegisterForm({
|
||||
Ou continuez avec
|
||||
</span>
|
||||
</div>
|
||||
<Button variant="outline" className="w-full">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
onClick={() => handleGoogleLogin()}
|
||||
disabled={loading}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" className="mr-2 h-4 w-4">
|
||||
<path
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
fill="currentColor"
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
fill="#4285F4"
|
||||
/>
|
||||
<path
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
fill="#34A853"
|
||||
/>
|
||||
<path
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
fill="#FBBC05"
|
||||
/>
|
||||
<path
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
fill="#EA4335"
|
||||
/>
|
||||
<path d="M1 1h22v22H1z" fill="none" />
|
||||
</svg>
|
||||
Créer un compte avec Google
|
||||
</Button>
|
||||
|
||||
@ -2,9 +2,12 @@ import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
import { GoogleOAuthProvider } from '@react-oauth/google'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
<GoogleOAuthProvider clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}>
|
||||
<App />
|
||||
</GoogleOAuthProvider>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user