feat(auth): implement Google and Zalo OAuth backend strategies
Add complete OAuth2 authentication flow for Google and Zalo providers: - OAuthService: handles account linking (by email/phone), new user creation for OAuth-only accounts, and JWT token generation - GoogleOAuthStrategy: passport-google-oauth20 integration - ZaloOAuthStrategy: custom OAuth2 implementation using Zalo's API (authorization URL generation, code exchange, user info fetch) - OAuthController: redirect and callback endpoints for both providers with httpOnly cookie-based token management - Unit tests for OAuthService (7 tests), GoogleOAuthStrategy (4 tests), and ZaloOAuthStrategy (7 tests) - OAuth env vars added to .env.example and env-validation warnings Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
67
pnpm-lock.yaml
generated
67
pnpm-lock.yaml
generated
@@ -150,6 +150,9 @@ importers:
|
||||
passport:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
passport-google-oauth20:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
passport-jwt:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
@@ -208,6 +211,9 @@ importers:
|
||||
'@types/nodemailer':
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0
|
||||
'@types/passport-google-oauth20':
|
||||
specifier: ^2.0.17
|
||||
version: 2.0.17
|
||||
'@types/passport-jwt':
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
@@ -2440,12 +2446,21 @@ packages:
|
||||
'@types/nodemailer@8.0.0':
|
||||
resolution: {integrity: sha512-fyf8jWULsCo0d0BuoQ75i6IeoHs47qcqxWc7yUdUcV0pOZGjUTTOvwdG1PRXUDqN/8A64yQdQdnA2pZgcdi+cA==}
|
||||
|
||||
'@types/oauth@0.9.6':
|
||||
resolution: {integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==}
|
||||
|
||||
'@types/passport-google-oauth20@2.0.17':
|
||||
resolution: {integrity: sha512-MHNOd2l7gOTCn3iS+wInPQMiukliAUvMpODO3VlXxOiwNEMSyzV7UNvAdqxSN872o8OXx1SqPDVT6tLW74AtqQ==}
|
||||
|
||||
'@types/passport-jwt@4.0.1':
|
||||
resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==}
|
||||
|
||||
'@types/passport-local@1.0.38':
|
||||
resolution: {integrity: sha512-nsrW4A963lYE7lNTv9cr5WmiUD1ibYJvWrpE13oxApFsRt77b0RdtZvKbCdNIY4v/QZ6TRQWaDDEwV1kCTmcXg==}
|
||||
|
||||
'@types/passport-oauth2@1.8.0':
|
||||
resolution: {integrity: sha512-6//z+4orIOy/g3zx17HyQ71GSRK4bs7Sb+zFasRoc2xzlv7ZCJ+vkDBYFci8U6HY+or6Zy7ajf4mz4rK7nsWJQ==}
|
||||
|
||||
'@types/passport-strategy@0.2.38':
|
||||
resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==}
|
||||
|
||||
@@ -2926,6 +2941,10 @@ packages:
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
base64url@3.0.1:
|
||||
resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
baseline-browser-mapping@2.10.16:
|
||||
resolution: {integrity: sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -4529,6 +4548,9 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
oauth@0.10.2:
|
||||
resolution: {integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4597,6 +4619,10 @@ packages:
|
||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
passport-google-oauth20@2.0.0:
|
||||
resolution: {integrity: sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-jwt@4.0.1:
|
||||
resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==}
|
||||
|
||||
@@ -4604,6 +4630,10 @@ packages:
|
||||
resolution: {integrity: sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
resolution: {integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-strategy@1.0.0:
|
||||
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
@@ -5520,6 +5550,9 @@ packages:
|
||||
engines: {node: '>=0.8.0'}
|
||||
hasBin: true
|
||||
|
||||
uid2@0.0.4:
|
||||
resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
|
||||
|
||||
uid@2.0.2:
|
||||
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -8441,6 +8474,16 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 25.5.2
|
||||
|
||||
'@types/oauth@0.9.6':
|
||||
dependencies:
|
||||
'@types/node': 25.5.2
|
||||
|
||||
'@types/passport-google-oauth20@2.0.17':
|
||||
dependencies:
|
||||
'@types/express': 5.0.6
|
||||
'@types/passport': 1.0.17
|
||||
'@types/passport-oauth2': 1.8.0
|
||||
|
||||
'@types/passport-jwt@4.0.1':
|
||||
dependencies:
|
||||
'@types/jsonwebtoken': 9.0.10
|
||||
@@ -8452,6 +8495,12 @@ snapshots:
|
||||
'@types/passport': 1.0.17
|
||||
'@types/passport-strategy': 0.2.38
|
||||
|
||||
'@types/passport-oauth2@1.8.0':
|
||||
dependencies:
|
||||
'@types/express': 5.0.6
|
||||
'@types/oauth': 0.9.6
|
||||
'@types/passport': 1.0.17
|
||||
|
||||
'@types/passport-strategy@0.2.38':
|
||||
dependencies:
|
||||
'@types/express': 5.0.6
|
||||
@@ -8965,6 +9014,8 @@ snapshots:
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
base64url@3.0.1: {}
|
||||
|
||||
baseline-browser-mapping@2.10.16: {}
|
||||
|
||||
bcrypt@6.0.0:
|
||||
@@ -10654,6 +10705,8 @@ snapshots:
|
||||
pathe: 2.0.3
|
||||
tinyexec: 1.1.1
|
||||
|
||||
oauth@0.10.2: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-hash@3.0.0: {}
|
||||
@@ -10726,6 +10779,10 @@ snapshots:
|
||||
|
||||
parseurl@1.3.3: {}
|
||||
|
||||
passport-google-oauth20@2.0.0:
|
||||
dependencies:
|
||||
passport-oauth2: 1.8.0
|
||||
|
||||
passport-jwt@4.0.1:
|
||||
dependencies:
|
||||
jsonwebtoken: 9.0.3
|
||||
@@ -10735,6 +10792,14 @@ snapshots:
|
||||
dependencies:
|
||||
passport-strategy: 1.0.0
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
dependencies:
|
||||
base64url: 3.0.1
|
||||
oauth: 0.10.2
|
||||
passport-strategy: 1.0.0
|
||||
uid2: 0.0.4
|
||||
utils-merge: 1.0.1
|
||||
|
||||
passport-strategy@1.0.0: {}
|
||||
|
||||
passport@0.7.0:
|
||||
@@ -11719,6 +11784,8 @@ snapshots:
|
||||
uglify-js@3.19.3:
|
||||
optional: true
|
||||
|
||||
uid2@0.0.4: {}
|
||||
|
||||
uid@2.0.2:
|
||||
dependencies:
|
||||
'@lukeed/csprng': 1.1.0
|
||||
|
||||
Reference in New Issue
Block a user