」 feat: added google auth

This commit is contained in:
2025-07-18 17:28:47 +02:00
parent 1fb6d07d10
commit ff8a4863ba
10 changed files with 381 additions and 27 deletions

View File

@ -3,6 +3,7 @@
"@fastify/cookie": "^11.0.2", "@fastify/cookie": "^11.0.2",
"@fastify/env": "^5.0.2", "@fastify/env": "^5.0.2",
"@fastify/jwt": "^9.1.0", "@fastify/jwt": "^9.1.0",
"axios": "^1.10.0",
"bcrypt": "^6.0.0", "bcrypt": "^6.0.0",
"better-sqlite3": "^12.2.0", "better-sqlite3": "^12.2.0",
"fastify": "^5.4.0", "fastify": "^5.4.0",

190
pnpm-lock.yaml generated
View File

@ -17,6 +17,9 @@ importers:
'@fastify/jwt': '@fastify/jwt':
specifier: ^9.1.0 specifier: ^9.1.0
version: 9.1.0 version: 9.1.0
axios:
specifier: ^1.10.0
version: 1.10.0
bcrypt: bcrypt:
specifier: ^6.0.0 specifier: ^6.0.0
version: 6.0.0 version: 6.0.0
@ -480,6 +483,9 @@ packages:
asn1.js@5.4.1: asn1.js@5.4.1:
resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
atomic-sleep@1.0.0: atomic-sleep@1.0.0:
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
@ -487,6 +493,9 @@ packages:
avvio@9.1.0: avvio@9.1.0:
resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==}
axios@1.10.0:
resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==}
base64-js@1.5.1: base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@ -516,6 +525,10 @@ packages:
buffer@5.7.1: buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
chalk@4.1.2: chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -544,6 +557,10 @@ packages:
colorette@2.0.20: colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
commist@3.2.0: commist@3.2.0:
resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==} resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==}
@ -575,6 +592,10 @@ packages:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dequal@2.0.3: dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -591,6 +612,10 @@ packages:
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
engines: {node: '>=12'} engines: {node: '>=12'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
ecdsa-sig-formatter@1.0.11: ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
@ -604,6 +629,22 @@ packages:
env-schema@6.0.1: env-schema@6.0.1:
resolution: {integrity: sha512-WRD40Q25pP4NUbI3g3CNU5PPzcaiX7YYcPwiCZlfR4qGsKmTlckRixgHww0/fOXiXSNKA87pwshzq0ULTK/48A==} resolution: {integrity: sha512-WRD40Q25pP4NUbI3g3CNU5PPzcaiX7YYcPwiCZlfR4qGsKmTlckRixgHww0/fOXiXSNKA87pwshzq0ULTK/48A==}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
esbuild@0.25.6: esbuild@0.25.6:
resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -691,6 +732,19 @@ packages:
resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
engines: {node: '>=6'} engines: {node: '>=6'}
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
form-data@4.0.4:
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
engines: {node: '>= 6'}
formdata-polyfill@4.0.10: formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'} engines: {node: '>=12.20.0'}
@ -703,6 +757,9 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin] os: [darwin]
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
gaxios@7.1.1: gaxios@7.1.1:
resolution: {integrity: sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==} resolution: {integrity: sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -715,6 +772,14 @@ packages:
resolution: {integrity: sha512-b4cVhbPfbgbCZtK0dcUc1lASitXGEAIqukV5DDAyWm25fomWnV+C+a1yXvqikcRZXHN2j0pSDyj3cTfzq8pC7Q==} resolution: {integrity: sha512-b4cVhbPfbgbCZtK0dcUc1lASitXGEAIqukV5DDAyWm25fomWnV+C+a1yXvqikcRZXHN2j0pSDyj3cTfzq8pC7Q==}
hasBin: true hasBin: true
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
github-from-package@0.0.0: github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
@ -726,6 +791,10 @@ packages:
resolution: {integrity: sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==} resolution: {integrity: sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==}
engines: {node: '>=14'} engines: {node: '>=14'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
graceful-fs@4.2.11: graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@ -737,6 +806,18 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
help-me@5.0.0: help-me@5.0.0:
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
@ -866,6 +947,18 @@ packages:
makeerror@1.0.12: makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mimic-response@3.1.0: mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -993,6 +1086,9 @@ packages:
process-warning@5.0.0: process-warning@5.0.0:
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
pump@3.0.3: pump@3.0.3:
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
@ -1516,6 +1612,8 @@ snapshots:
minimalistic-assert: 1.0.1 minimalistic-assert: 1.0.1
safer-buffer: 2.1.2 safer-buffer: 2.1.2
asynckit@0.4.0: {}
atomic-sleep@1.0.0: {} atomic-sleep@1.0.0: {}
avvio@9.1.0: avvio@9.1.0:
@ -1523,6 +1621,14 @@ snapshots:
'@fastify/error': 4.2.0 '@fastify/error': 4.2.0
fastq: 1.19.1 fastq: 1.19.1
axios@1.10.0:
dependencies:
follow-redirects: 1.15.9
form-data: 4.0.4
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
base64-js@1.5.1: {} base64-js@1.5.1: {}
bcrypt@6.0.0: bcrypt@6.0.0:
@ -1556,6 +1662,11 @@ snapshots:
base64-js: 1.5.1 base64-js: 1.5.1
ieee754: 1.2.1 ieee754: 1.2.1
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
chalk@4.1.2: chalk@4.1.2:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
@ -1579,6 +1690,10 @@ snapshots:
colorette@2.0.20: {} colorette@2.0.20: {}
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
commist@3.2.0: {} commist@3.2.0: {}
cookie@1.0.2: {} cookie@1.0.2: {}
@ -1597,6 +1712,8 @@ snapshots:
deep-extend@0.6.0: {} deep-extend@0.6.0: {}
delayed-stream@1.0.0: {}
dequal@2.0.3: {} dequal@2.0.3: {}
detect-libc@2.0.4: {} detect-libc@2.0.4: {}
@ -1605,6 +1722,12 @@ snapshots:
dotenv@16.6.1: {} dotenv@16.6.1: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
ecdsa-sig-formatter@1.0.11: ecdsa-sig-formatter@1.0.11:
dependencies: dependencies:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
@ -1624,6 +1747,21 @@ snapshots:
dotenv: 16.6.1 dotenv: 16.6.1
dotenv-expand: 10.0.0 dotenv-expand: 10.0.0
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
esbuild@0.25.6: esbuild@0.25.6:
optionalDependencies: optionalDependencies:
'@esbuild/aix-ppc64': 0.25.6 '@esbuild/aix-ppc64': 0.25.6
@ -1767,6 +1905,16 @@ snapshots:
dependencies: dependencies:
locate-path: 3.0.0 locate-path: 3.0.0
follow-redirects@1.15.9: {}
form-data@4.0.4:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
hasown: 2.0.2
mime-types: 2.1.35
formdata-polyfill@4.0.10: formdata-polyfill@4.0.10:
dependencies: dependencies:
fetch-blob: 3.2.0 fetch-blob: 3.2.0
@ -1776,6 +1924,8 @@ snapshots:
fsevents@2.3.3: fsevents@2.3.3:
optional: true optional: true
function-bind@1.1.2: {}
gaxios@7.1.1: gaxios@7.1.1:
dependencies: dependencies:
extend: 3.0.2 extend: 3.0.2
@ -1799,6 +1949,24 @@ snapshots:
split2: 3.2.2 split2: 3.2.2
walker: 1.0.8 walker: 1.0.8
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
github-from-package@0.0.0: {} github-from-package@0.0.0: {}
google-auth-library@10.1.0: google-auth-library@10.1.0:
@ -1815,6 +1983,8 @@ snapshots:
google-logging-utils@1.1.1: {} google-logging-utils@1.1.1: {}
gopd@1.2.0: {}
graceful-fs@4.2.11: {} graceful-fs@4.2.11: {}
gtoken@8.0.0: gtoken@8.0.0:
@ -1826,6 +1996,16 @@ snapshots:
has-flag@4.0.0: {} has-flag@4.0.0: {}
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
help-me@5.0.0: {} help-me@5.0.0: {}
https-proxy-agent@7.0.6: https-proxy-agent@7.0.6:
@ -1936,6 +2116,14 @@ snapshots:
dependencies: dependencies:
tmpl: 1.0.5 tmpl: 1.0.5
math-intrinsics@1.1.0: {}
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
mimic-response@3.1.0: {} mimic-response@3.1.0: {}
minimalistic-assert@1.0.1: {} minimalistic-assert@1.0.1: {}
@ -2067,6 +2255,8 @@ snapshots:
process-warning@5.0.0: {} process-warning@5.0.0: {}
proxy-from-env@1.1.0: {}
pump@3.0.3: pump@3.0.3:
dependencies: dependencies:
end-of-stream: 1.4.5 end-of-stream: 1.4.5

View File

@ -3,7 +3,10 @@ import fastifyCookie from '@fastify/cookie';
import { register } from './register.js'; import { register } from './register.js';
import { login } from './login.js'; import { login } from './login.js';
import { gRedir } from './gRedir.js';
import authDB from '../../utils/authDB.js' import authDB from '../../utils/authDB.js'
import { gLogCallback } from './gLogCallback.js';
import { gRegisterCallback } from './gRegisterCallback.js';
const saltRounds = 10; const saltRounds = 10;
@ -36,7 +39,18 @@ export default async function(fastify, options) {
}); });
// GOOGLE sign in // GOOGLE sign in
fastify.get('/login/google', async (request, reply) => {
return gRedir(request, reply, fastify, '/login/google/callback');
});
fastify.get('/register/google', async (request, reply) => {
return gRedir(request, reply, fastify, '/register/google/callback');
});
fastify.get('/login/google/callback', async (request, reply) => {
return gLogCallback(request, reply, fastify);
})
fastify.get('/register/google/callback', async (request, reply) => {
return gRegisterCallback(request, reply, fastify);
})
fastify.post('/login', { fastify.post('/login', {
schema: { schema: {

View File

@ -0,0 +1,55 @@
import axios from 'axios'
import authDB from '../../utils/authDB.js';
var env = process.env.NODE_ENV || 'development';
/**
* @param {import("fastify").FastifyRequest} request
* @param {import("fastify").FastifyReply} reply
* @param {import("fastify").FastifyInstance} fastify
*
* @returns {import('fastify').FastifyReply}
*/
export async function gLogCallback(request, reply, fastify) {
const { code } = request.query;
try {
const response = await axios.post('https://oauth2.googleapis.com/token', {
code,
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
redirect_uri: process.env.GOOGLE_CALLBACK_URL + '/login/google/callback',
grant_type: 'authorization_code',
});
const { access_token } = response.data;
const userInfoResponse = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', {
headers: { Authorization: `Bearer ${access_token}` },
});
const userProfile = userInfoResponse.data;
const user = {
username: userProfile.email, // Assuming email is used as the username
};
if (!authDB.checkUser(user.username) || authDB.RESERVED_USERNAMES.includes(user.username)) {
return reply.code(400).send({ error: "User does not exist" });
}
const token = fastify.jwt.sign(user);
return reply
.setCookie('token', token, {
httpOnly: true,
path: '/',
secure: env !== 'development',
sameSite: 'lax',
})
.code(200)
.send({ msg: "Login successful" });
} catch (error) {
fastify.log.error(error);
reply.code(500).send({ error: 'Internal server error' });
}
}

23
src/api/auth/gRedir.js Normal file
View File

@ -0,0 +1,23 @@
/**
* @param {import("fastify").FastifyRequest} request
* @param {import("fastify").FastifyReply} reply
* @param {import("fastify").FastifyInstance} fastify
* @param {string} callbackRoute
*
* @returns {import('fastify').FastifyReply}
*/
export async function gRedir(request, reply, fastify, callbackRoute) {
try {
const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
`client_id=${process.env.GOOGLE_CLIENT_ID}&` +
`redirect_uri=${encodeURIComponent(process.env.GOOGLE_CALLBACK_URL + callbackRoute)}&` +
`response_type=code&` +
`scope=email profile&` +
`access_type=offline`;
return reply.redirect(authUrl);
} catch (error) {
fastify.log.error(error);
return reply.code(500).send({ error: "Internal server error" });
}
}

View File

@ -0,0 +1,64 @@
import axios from 'axios'
import authDB from '../../utils/authDB.js';
var env = process.env.NODE_ENV || 'development';
/**
* @param {import("fastify").FastifyRequest} request
* @param {import("fastify").FastifyReply} reply
* @param {import("fastify").FastifyInstance} fastify
*
* @returns {import('fastify').FastifyReply}
*/
export async function gRegisterCallback(request, reply, fastify) {
const { code } = request.query;
try {
const response = await axios.post('https://oauth2.googleapis.com/token', {
code,
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
redirect_uri: process.env.GOOGLE_CALLBACK_URL + '/register/google/callback',
grant_type: 'authorization_code',
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const { access_token } = response.data;
const userInfoResponse = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', {
headers: { Authorization: `Bearer ${access_token}` },
});
const userProfile = userInfoResponse.data;
const user = {
username: userProfile.email, // Assuming email is used as the username
};
if (authDB.RESERVED_USERNAMES.includes(user)) {
return reply.code(400).send({ error: 'Reserved username' });
}
if (authDB.checkUser(user.username) === true) {
return reply.code(400).send({ error: "User already exist" });
}
authDB.addUser(user.username, '');
const token = fastify.jwt.sign(user);
return reply
.setCookie('token', token, {
httpOnly: true,
path: '/',
secure: env !== 'development',
sameSite: 'lax',
})
.code(200)
.send({ msg: "Register successful" });
} catch (error) {
fastify.log.error(error);
reply.code(500).send({ error: 'Internal server error' });
}
}

View File

@ -1,6 +1,5 @@
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import { checkUser } from '../../utils/authUtils.js';
import authDB from '../../utils/authDB.js'; import authDB from '../../utils/authDB.js';
var env = process.env.NODE_ENV || 'development'; var env = process.env.NODE_ENV || 'development';
@ -18,11 +17,11 @@ export async function login(request, reply, fastify) {
/** @type {{ user: string, password: string }} */ /** @type {{ user: string, password: string }} */
const { user, password } = request.body; const { user, password } = request.body;
if (!checkUser(user) || user === 'admin') { if (!authDB.checkUser(user) || authDB.RESERVED_USERNAMES.includes(user)) {
return reply.code(400).send({ error: "User does not exist" }); return reply.code(400).send({ error: "User does not exist" });
} }
const query = authDB.passwordQuery.get(user); const query = authDB.passwordQuery(user);
const hash = query?.passwordHash; const hash = query?.passwordHash;
if (!hash) { if (!hash) {

View File

@ -1,6 +1,6 @@
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import { isValidString, checkUser } from '../../utils/authUtils.js'; import { isValidString } from '../../utils/authUtils.js';
import authDB from '../../utils/authDB.js'; import authDB from '../../utils/authDB.js';
var env = process.env.NODE_ENV || 'development'; var env = process.env.NODE_ENV || 'development';
@ -25,7 +25,7 @@ export async function register(request, reply, saltRounds, fastify) {
if (!isValidString(user) || !isValidString(password)) { if (!isValidString(user) || !isValidString(password)) {
return reply.code(400).send({ error: 'Invalid username or password' }); return reply.code(400).send({ error: 'Invalid username or password' });
} else if (checkUser(user) === true) { } else if (authDB.checkUser(user) === true) {
return reply.code(400).send({ error: "User already exist" }); return reply.code(400).send({ error: "User already exist" });
} else if (password.length <= 8) { } else if (password.length <= 8) {
return reply.code(400).send({ error: "Password too short" }); return reply.code(400).send({ error: "Password too short" });
@ -34,7 +34,7 @@ export async function register(request, reply, saltRounds, fastify) {
} }
const hash = await bcrypt.hash(password, saltRounds); const hash = await bcrypt.hash(password, saltRounds);
authDB.userAdd.run(user, hash); authDB.addUser(user, hash);
const token = fastify.jwt.sign({ user }); const token = fastify.jwt.sign({ user });

View File

@ -3,7 +3,6 @@ import Database from 'better-sqlite3';
var env = process.env.NODE_ENV || 'development'; var env = process.env.NODE_ENV || 'development';
let database; let database;
const RESERVED_USERNAMES = ['admin']; const RESERVED_USERNAMES = ['admin'];
let userCheck, passwordQuery, userAdd;
if (!env || env === 'development') { if (!env || env === 'development') {
database = new Database(":memory:", { verbose: console.log }); database = new Database(":memory:", { verbose: console.log });
@ -22,17 +21,39 @@ function prepareDB() {
passwordHash TEXT passwordHash TEXT
) STRICT ) STRICT
`); `);
userCheck = database.prepare('SELECT EXISTS (SELECT 1 FROM credentials WHERE username = ?);');
passwordQuery = database.prepare('SELECT passwordHash FROM credentials WHERE username = ?;');
userAdd = database.prepare('INSERT INTO credentials (username, passwordHash) VALUES (?, ?)');
} }
/**
* @param {string} name
*
* @returns {boolean}
*/
function checkUser(name) {
/**
* @type: {import('better-sqlite3').Statement}
*/
let userCheck = database.prepare('SELECT EXISTS (SELECT 1 FROM credentials WHERE username = ?);');
const result = userCheck.get(name);
const key = Object.keys(result)[0];
return result[key] === 1;
}
function addUser(name, pass) {
let userAdd = database.prepare('INSERT INTO credentials (username, passwordHash) VALUES (?, ?)');
userAdd.run(name, pass);
}
function passwordQuery(user) {
let passwordQuery = database.prepare('SELECT passwordHash FROM credentials WHERE username = ?;');
return passwordQuery.get(user)
}
const authDB = { const authDB = {
prepareDB, prepareDB,
get userCheck() { return userCheck; }, checkUser,
get userAdd() { return userAdd; }, addUser,
get passwordQuery() { return passwordQuery; }, passwordQuery,
RESERVED_USERNAMES RESERVED_USERNAMES
}; };

View File

@ -8,16 +8,3 @@ import authDB from './authDB.js';
export function isValidString(value) { export function isValidString(value) {
return typeof value === 'string' && value.trim() !== ''; return typeof value === 'string' && value.trim() !== '';
} }
/**
* @param {string} name
* @param {import('better-sqlite3').Statement} userCheck
*
* @returns {boolean}
*/
export function checkUser(name, userCheck) {
const result = authDB.userCheck.get(name);
const key = Object.keys(result)[0];
return result[key] === 1;
}