initial setup for google signin signup integration

This commit is contained in:
Sheen Capadngan
2023-04-25 23:46:21 +08:00
parent 3b00df6662
commit 007e8c4442
19 changed files with 455 additions and 16 deletions

View File

@@ -8,6 +8,7 @@ JWT_SIGNUP_SECRET=3679e04ca949f914c03332aaaeba805a
JWT_REFRESH_SECRET=5f2f3c8f0159068dc2bbb3a652a716ff JWT_REFRESH_SECRET=5f2f3c8f0159068dc2bbb3a652a716ff
JWT_AUTH_SECRET=4be6ba5602e0fa0ac6ac05c3cd4d247f JWT_AUTH_SECRET=4be6ba5602e0fa0ac6ac05c3cd4d247f
JWT_SERVICE_SECRET=f32f716d70a42c5703f4656015e76200 JWT_SERVICE_SECRET=f32f716d70a42c5703f4656015e76200
JWT_PROVIDER_AUTH_SECRET=f32f716d70a42c5703f4656015e76201
# JWT lifetime # JWT lifetime
# Optional lifetimes for JWT tokens expressed in seconds or a string # Optional lifetimes for JWT tokens expressed in seconds or a string
@@ -15,6 +16,7 @@ JWT_SERVICE_SECRET=f32f716d70a42c5703f4656015e76200
JWT_AUTH_LIFETIME= JWT_AUTH_LIFETIME=
JWT_REFRESH_LIFETIME= JWT_REFRESH_LIFETIME=
JWT_SIGNUP_LIFETIME= JWT_SIGNUP_LIFETIME=
JWT_PROVIDER_AUTH_LIFETIME=
# MongoDB # MongoDB
# Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref # Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref
@@ -67,3 +69,7 @@ STRIPE_PRODUCT_STARTER=
STRIPE_PRODUCT_TEAM= STRIPE_PRODUCT_TEAM=
STRIPE_PRODUCT_PRO= STRIPE_PRODUCT_PRO=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
SESSION_SECRET=

View File

@@ -30,6 +30,7 @@
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"express": "^4.18.1", "express": "^4.18.1",
"express-rate-limit": "^6.7.0", "express-rate-limit": "^6.7.0",
"express-session": "^1.17.3",
"express-validator": "^6.14.2", "express-validator": "^6.14.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"helmet": "^5.1.1", "helmet": "^5.1.1",
@@ -41,6 +42,8 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mongoose": "^6.10.4", "mongoose": "^6.10.4",
"nodemailer": "^6.8.0", "nodemailer": "^6.8.0",
"passport": "^0.6.0",
"passport-google-oidc": "^0.1.0",
"posthog-node": "^2.6.0", "posthog-node": "^2.6.0",
"query-string": "^7.1.3", "query-string": "^7.1.3",
"request-ip": "^3.3.0", "request-ip": "^3.3.0",
@@ -63,11 +66,13 @@
"@types/cookie-parser": "^1.4.3", "@types/cookie-parser": "^1.4.3",
"@types/cors": "^2.8.12", "@types/cors": "^2.8.12",
"@types/express": "^4.17.14", "@types/express": "^4.17.14",
"@types/express-session": "^1.17.7",
"@types/jest": "^29.5.0", "@types/jest": "^29.5.0",
"@types/jsonwebtoken": "^8.5.9", "@types/jsonwebtoken": "^8.5.9",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/node": "^18.11.3", "@types/node": "^18.11.3",
"@types/nodemailer": "^6.4.6", "@types/nodemailer": "^6.4.6",
"@types/passport": "^1.0.12",
"@types/supertest": "^2.0.12", "@types/supertest": "^2.0.12",
"@types/swagger-jsdoc": "^6.0.1", "@types/swagger-jsdoc": "^6.0.1",
"@types/swagger-ui-express": "^4.1.3", "@types/swagger-ui-express": "^4.1.3",
@@ -3938,6 +3943,15 @@
"@types/range-parser": "*" "@types/range-parser": "*"
} }
}, },
"node_modules/@types/express-session": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.7.tgz",
"integrity": "sha512-L25080PBYoRLu472HY/HNCxaXY8AaGgqGC8/p/8+BYMhG0RDOLQ1wpXOpAzr4Gi5TGozTKyJv5BVODM5UNyVMw==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/graceful-fs": { "node_modules/@types/graceful-fs": {
"version": "4.1.6", "version": "4.1.6",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
@@ -4032,6 +4046,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/passport": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz",
"integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/prettier": { "node_modules/@types/prettier": {
"version": "2.7.2", "version": "2.7.2",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
@@ -6141,6 +6164,37 @@
"express": "^4 || ^5" "express": "^4 || ^5"
} }
}, },
"node_modules/express-session": {
"version": "1.17.3",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
"dependencies": {
"cookie": "0.4.2",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/express-session/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express-session/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/express-validator": { "node_modules/express-validator": {
"version": "6.15.0", "version": "6.15.0",
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.15.0.tgz", "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.15.0.tgz",
@@ -11122,6 +11176,11 @@
"set-blocking": "^2.0.0" "set-blocking": "^2.0.0"
} }
}, },
"node_modules/oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -11149,6 +11208,14 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -11274,6 +11341,62 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/passport": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
"dependencies": {
"passport-strategy": "1.x.x",
"pause": "0.0.1",
"utils-merge": "^1.0.1"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-google-oidc": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/passport-google-oidc/-/passport-google-oidc-0.1.0.tgz",
"integrity": "sha512-/TtFXaWvmubm5kXNoJMyzBfxhnZ0lnBPA6w6rmQMP9klmHZf0ArE8IrIEt3yAHoiDzGx4eTO7YasKQFbPsNtVA==",
"dependencies": {
"passport-openidconnect": "0.1.x"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-openidconnect": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/passport-openidconnect/-/passport-openidconnect-0.1.1.tgz",
"integrity": "sha512-r0QJiWEzwCg2MeCIXVP5G6YxVRqnEsZ2HpgKRthZ9AiQHJrgGUytXpsdcGF9BRwd3yMrEesb/uG/Yxb86rrY0g==",
"dependencies": {
"oauth": "0.9.x",
"passport-strategy": "1.x.x"
},
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/path-exists": { "node_modules/path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -11320,6 +11443,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -11607,6 +11735,14 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/randombytes": { "node_modules/randombytes": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -12732,6 +12868,17 @@
"node": ">=0.8.0" "node": ">=0.8.0"
} }
}, },
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"dependencies": {
"random-bytes": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/undefsafe": { "node_modules/undefsafe": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -16242,6 +16389,15 @@
"@types/range-parser": "*" "@types/range-parser": "*"
} }
}, },
"@types/express-session": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.7.tgz",
"integrity": "sha512-L25080PBYoRLu472HY/HNCxaXY8AaGgqGC8/p/8+BYMhG0RDOLQ1wpXOpAzr4Gi5TGozTKyJv5BVODM5UNyVMw==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/graceful-fs": { "@types/graceful-fs": {
"version": "4.1.6", "version": "4.1.6",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
@@ -16336,6 +16492,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/passport": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz",
"integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/prettier": { "@types/prettier": {
"version": "2.7.2", "version": "2.7.2",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
@@ -17892,6 +18057,36 @@
"integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==", "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
"requires": {} "requires": {}
}, },
"express-session": {
"version": "1.17.3",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
"requires": {
"cookie": "0.4.2",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
}
}
},
"express-validator": { "express-validator": {
"version": "6.15.0", "version": "6.15.0",
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.15.0.tgz", "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.15.0.tgz",
@@ -21498,6 +21693,11 @@
"set-blocking": "^2.0.0" "set-blocking": "^2.0.0"
} }
}, },
"oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
},
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -21516,6 +21716,11 @@
"ee-first": "1.1.1" "ee-first": "1.1.1"
} }
}, },
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -21605,6 +21810,38 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
}, },
"passport": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
"requires": {
"passport-strategy": "1.x.x",
"pause": "0.0.1",
"utils-merge": "^1.0.1"
}
},
"passport-google-oidc": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/passport-google-oidc/-/passport-google-oidc-0.1.0.tgz",
"integrity": "sha512-/TtFXaWvmubm5kXNoJMyzBfxhnZ0lnBPA6w6rmQMP9klmHZf0ArE8IrIEt3yAHoiDzGx4eTO7YasKQFbPsNtVA==",
"requires": {
"passport-openidconnect": "0.1.x"
}
},
"passport-openidconnect": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/passport-openidconnect/-/passport-openidconnect-0.1.1.tgz",
"integrity": "sha512-r0QJiWEzwCg2MeCIXVP5G6YxVRqnEsZ2HpgKRthZ9AiQHJrgGUytXpsdcGF9BRwd3yMrEesb/uG/Yxb86rrY0g==",
"requires": {
"oauth": "0.9.x",
"passport-strategy": "1.x.x"
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA=="
},
"path-exists": { "path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -21639,6 +21876,11 @@
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true "dev": true
}, },
"pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
},
"picocolors": { "picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -21840,6 +22082,11 @@
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"dev": true "dev": true
}, },
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="
},
"randombytes": { "randombytes": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -22660,6 +22907,14 @@
"integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
"optional": true "optional": true
}, },
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"undefsafe": { "undefsafe": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",

View File

@@ -5,7 +5,6 @@
"@octokit/rest": "^19.0.5", "@octokit/rest": "^19.0.5",
"@sentry/node": "^7.45.0", "@sentry/node": "^7.45.0",
"@sentry/tracing": "^7.46.0", "@sentry/tracing": "^7.46.0",
"@sentry/node": "^7.41.0",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/libsodium-wrappers": "^0.7.10", "@types/libsodium-wrappers": "^0.7.10",
"argon2": "^0.30.3", "argon2": "^0.30.3",
@@ -22,6 +21,7 @@
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"express": "^4.18.1", "express": "^4.18.1",
"express-rate-limit": "^6.7.0", "express-rate-limit": "^6.7.0",
"express-session": "^1.17.3",
"express-validator": "^6.14.2", "express-validator": "^6.14.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"helmet": "^5.1.1", "helmet": "^5.1.1",
@@ -33,6 +33,8 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mongoose": "^6.10.4", "mongoose": "^6.10.4",
"nodemailer": "^6.8.0", "nodemailer": "^6.8.0",
"passport": "^0.6.0",
"passport-google-oidc": "^0.1.0",
"posthog-node": "^2.6.0", "posthog-node": "^2.6.0",
"query-string": "^7.1.3", "query-string": "^7.1.3",
"request-ip": "^3.3.0", "request-ip": "^3.3.0",
@@ -82,11 +84,13 @@
"@types/cookie-parser": "^1.4.3", "@types/cookie-parser": "^1.4.3",
"@types/cors": "^2.8.12", "@types/cors": "^2.8.12",
"@types/express": "^4.17.14", "@types/express": "^4.17.14",
"@types/express-session": "^1.17.7",
"@types/jest": "^29.5.0", "@types/jest": "^29.5.0",
"@types/jsonwebtoken": "^8.5.9", "@types/jsonwebtoken": "^8.5.9",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/node": "^18.11.3", "@types/node": "^18.11.3",
"@types/nodemailer": "^6.4.6", "@types/nodemailer": "^6.4.6",
"@types/passport": "^1.0.12",
"@types/supertest": "^2.0.12", "@types/supertest": "^2.0.12",
"@types/swagger-jsdoc": "^6.0.1", "@types/swagger-jsdoc": "^6.0.1",
"@types/swagger-ui-express": "^4.1.3", "@types/swagger-ui-express": "^4.1.3",

View File

@@ -12,6 +12,8 @@ export const getJwtRefreshSecret = () => infisical.get('JWT_REFRESH_SECRET')!;
export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!; export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!;
export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')! || '15m'; export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')! || '15m';
export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!; export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!;
export const getJwtProviderAuthSecret = () => infisical.get('JWT_PROVIDER_AUTH_SECRET')!;
export const getJwtProviderAuthLifetime = () => infisical.get('JWT_PROVIDER_AUTH_LIFETIME')! || '15m';
export const getMongoURL = () => infisical.get('MONGO_URL')!; export const getMongoURL = () => infisical.get('MONGO_URL')!;
export const getNodeEnv = () => infisical.get('NODE_ENV')! || 'production'; export const getNodeEnv = () => infisical.get('NODE_ENV')! || 'production';
export const getVerboseErrorOutput = () => infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true; export const getVerboseErrorOutput = () => infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true;
@@ -32,6 +34,7 @@ export const getClientSlugVercel = () => infisical.get('CLIENT_SLUG_VERCEL')!;
export const getPostHogHost = () => infisical.get('POSTHOG_HOST')! || 'https://app.posthog.com'; export const getPostHogHost = () => infisical.get('POSTHOG_HOST')! || 'https://app.posthog.com';
export const getPostHogProjectApiKey = () => infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE'; export const getPostHogProjectApiKey = () => infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
export const getSentryDSN = () => infisical.get('SENTRY_DSN')!; export const getSentryDSN = () => infisical.get('SENTRY_DSN')!;
export const getSessionSecret = () => infisical.get('SESSION_SECRET')!;
export const getSiteURL = () => infisical.get('SITE_URL')!; export const getSiteURL = () => infisical.get('SITE_URL')!;
export const getSmtpHost = () => infisical.get('SMTP_HOST')!; export const getSmtpHost = () => infisical.get('SMTP_HOST')!;
export const getSmtpSecure = () => infisical.get('SMTP_SECURE')! === 'true' || false; export const getSmtpSecure = () => infisical.get('SMTP_SECURE')! === 'true' || false;

View File

@@ -267,3 +267,7 @@ export const getNewToken = async (req: Request, res: Response) => {
}); });
} }
}; };
export const handleGoogleCallback = (req: Request, res: Response) => {
res.redirect(`/login/provider/success?token=${encodeURIComponent(req.providerAuthToken)}`);
}

View File

@@ -5,6 +5,7 @@ import infisical from 'infisical-node';
import express from 'express'; import express from 'express';
import helmet from 'helmet'; import helmet from 'helmet';
import cors from 'cors'; import cors from 'cors';
import session from 'express-session';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { DatabaseService } from './services'; import { DatabaseService } from './services';
import { setUpHealthEndpoint } from './services/health'; import { setUpHealthEndpoint } from './services/health';
@@ -75,7 +76,7 @@ import {
getPort, getPort,
getSentryDSN, getSentryDSN,
getSiteURL, getSiteURL,
getSmtpHost getSessionSecret,
} from './config'; } from './config';
const main = async () => { const main = async () => {
@@ -111,6 +112,11 @@ const main = async () => {
); );
app.use(requestIp.mw()); app.use(requestIp.mw());
app.use(session({
secret: getSessionSecret(),
resave: false, // don't save session if unmodified
saveUninitialized: false, // don't create session until something stored
}));
if (getNodeEnv() === 'production') { if (getNodeEnv() === 'production') {
// enable app-wide rate-limiting + helmet security // enable app-wide rate-limiting + helmet security

View File

@@ -21,7 +21,7 @@ export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | E
//* Set Sentry user identification if req.user is populated //* Set Sentry user identification if req.user is populated
if (req.user !== undefined && req.user !== null) { if (req.user !== undefined && req.user !== null) {
Sentry.setUser({ email: req.user.email }) Sentry.setUser({ email: (req.user as any).email })
} }
//* Only sent error to Sentry if LogLevel is one of the following level 'ERROR', 'EMERGENCY' or 'CRITICAL' //* Only sent error to Sentry if LogLevel is one of the following level 'ERROR', 'EMERGENCY' or 'CRITICAL'
//* with this we will eliminate false-positive errors like 'BadRequestError', 'UnauthorizedRequestError' and so on //* with this we will eliminate false-positive errors like 'BadRequestError', 'UnauthorizedRequestError' and so on

View File

@@ -2,6 +2,8 @@ import { Schema, model, Types, Document } from 'mongoose';
export interface IUser extends Document { export interface IUser extends Document {
_id: Types.ObjectId; _id: Types.ObjectId;
authId?: string;
authProvider?: string;
email: string; email: string;
firstName?: string; firstName?: string;
lastName?: string; lastName?: string;
@@ -26,9 +28,15 @@ export interface IUser extends Document {
const userSchema = new Schema<IUser>( const userSchema = new Schema<IUser>(
{ {
authId: {
type: String,
},
authProvider: {
type: String,
},
email: { email: {
type: String, type: String,
required: true required: true,
}, },
firstName: { firstName: {
type: String type: String

View File

@@ -1,10 +1,50 @@
import express from 'express'; import express from 'express';
const router = express.Router(); const router = express.Router();
import { body } from 'express-validator'; import { body } from 'express-validator';
import passport from 'passport';
import { requireAuth, validateRequest } from '../../middleware'; import { requireAuth, validateRequest } from '../../middleware';
import { authController } from '../../controllers/v1'; import { authController } from '../../controllers/v1';
import { authLimiter } from '../../helpers/rateLimiter'; import { authLimiter } from '../../helpers/rateLimiter';
import { AUTH_MODE_JWT } from '../../variables'; import { AUTH_MODE_JWT } from '../../variables';
import { User } from '../../models';
import { createToken } from '../../helpers/auth';
import { getJwtProviderAuthLifetime, getJwtProviderAuthSecret } from '../../config';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const GoogleStrategy = require('passport-google-oidc');
passport.use(new GoogleStrategy({
passReqToCallback: true,
clientID: process.env['GOOGLE_CLIENT_ID'],
clientSecret: process.env['GOOGLE_CLIENT_SECRET'],
callbackURL: '/api/v1/auth/google/callback',
}, async (req: express.Request, issuer: any, profile: any, cb: any) => {
const email = profile.emails[0].value;
let user = await User.findOne({
authProvider: issuer,
authId: profile.id,
})
if (!user) {
user = await new User({
email,
authProvider: issuer,
authId: profile.id,
}).save();
}
const providerAuthToken = createToken({
payload: {
userId: user._id.toString(),
email: user.email,
},
expiresIn: getJwtProviderAuthLifetime(),
secret: getJwtProviderAuthSecret(),
});
req.providerAuthToken = providerAuthToken;
cb(null, profile);
}));
router.post('/token', validateRequest, authController.getNewToken); router.post('/token', validateRequest, authController.getNewToken);
@@ -27,20 +67,34 @@ router.post( // deprecated (moved to api/v2/auth/login2)
); );
router.post( router.post(
'/logout', '/logout',
authLimiter, authLimiter,
requireAuth({ requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT] acceptedAuthModes: [AUTH_MODE_JWT]
}), }),
authController.logout authController.logout
); );
router.post( router.post(
'/checkAuth', '/checkAuth',
requireAuth({ requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT] acceptedAuthModes: [AUTH_MODE_JWT]
}), }),
authController.checkAuth authController.checkAuth
); );
router.get(
'/login/federated/google',
authLimiter,
passport.authenticate('google', {
scope: ['profile', 'email'],
}),
)
router.get(
'/google/callback',
passport.authenticate('google', { failureRedirect: '/error', session: false }),
authController.handleGoogleCallback,
)
export default router; export default router;

View File

@@ -9,6 +9,12 @@ import {
AuthData AuthData
} from '../../interfaces/middleware'; } from '../../interfaces/middleware';
declare module 'express' {
interface Request {
user?: any;
}
}
// TODO: fix (any) types // TODO: fix (any) types
declare global { declare global {
namespace Express { namespace Express {
@@ -18,6 +24,7 @@ declare global {
workspace: any; workspace: any;
membership: any; membership: any;
targetMembership: any; targetMembership: any;
providerAuthToken: any;
organization: any; organization: any;
membershipOrg: any; membershipOrg: any;
integration: any; integration: any;

View File

@@ -51,6 +51,7 @@
"infisical-node": "^1.0.37", "infisical-node": "^1.0.37",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jsrp": "^0.2.4", "jsrp": "^0.2.4",
"jwt-decode": "^3.1.2",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"next": "^12.3.4", "next": "^12.3.4",
"next-i18next": "^13.0.2", "next-i18next": "^13.0.2",
@@ -15007,6 +15008,11 @@
"node": ">=4.0" "node": ">=4.0"
} }
}, },
"node_modules/jwt-decode": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
},
"node_modules/keygrip": { "node_modules/keygrip": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@@ -33554,6 +33560,11 @@
"object.assign": "^4.1.3" "object.assign": "^4.1.3"
} }
}, },
"jwt-decode": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
},
"keygrip": { "keygrip": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",

View File

@@ -58,6 +58,7 @@
"infisical-node": "^1.0.37", "infisical-node": "^1.0.37",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jsrp": "^0.2.4", "jsrp": "^0.2.4",
"jwt-decode": "^3.1.2",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"next": "^12.3.4", "next": "^12.3.4",
"next-i18next": "^13.0.2", "next-i18next": "^13.0.2",

View File

@@ -4,8 +4,20 @@ import {
setMfaTempToken, setMfaTempToken,
setSignupTempToken} from '@app/reactQuery'; setSignupTempToken} from '@app/reactQuery';
export const PROVIDER_AUTH_TOKEN_KEY = 'infisical__provider-auth-token';
// depreciated: go for apiRequest module in config/api // depreciated: go for apiRequest module in config/api
export default class SecurityClient { export default class SecurityClient {
static setProviderAuthToken(tokenStr: string) {
localStorage.setItem(PROVIDER_AUTH_TOKEN_KEY, tokenStr)
}
static getProviderAuthToken() {
return localStorage.getItem(PROVIDER_AUTH_TOKEN_KEY);
}
static setSignupToken(tokenStr: string) { static setSignupToken(tokenStr: string) {
setSignupTempToken(tokenStr); setSignupTempToken(tokenStr);
} }

View File

@@ -1,5 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import SecurityClient from '@app/components/utilities/SecurityClient';
import { import {
getAuthToken, getAuthToken,
getMfaTempToken, getMfaTempToken,
@@ -16,6 +17,7 @@ apiRequest.interceptors.request.use((config) => {
const signupTempToken = getSignupTempToken(); const signupTempToken = getSignupTempToken();
const mfaTempToken = getMfaTempToken(); const mfaTempToken = getMfaTempToken();
const token = getAuthToken(); const token = getAuthToken();
const providerAuthToken = SecurityClient.getProviderAuthToken();
if (signupTempToken && config.headers) { if (signupTempToken && config.headers) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@@ -26,6 +28,9 @@ apiRequest.interceptors.request.use((config) => {
} else if (token && config.headers) { } else if (token && config.headers) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
config.headers.Authorization = `Bearer ${token}`; config.headers.Authorization = `Bearer ${token}`;
} else if(providerAuthToken && config.headers) {
// eslint-disable-next-line no-param-reassign
config.headers.Authorization = `Bearer ${providerAuthToken}`;
} }
return config; return config;
}); });

View File

@@ -16,7 +16,8 @@ export const publicPaths = [
`/terms`, `/terms`,
`/subprocessors`, `/subprocessors`,
`/verify-email`, `/verify-email`,
`/password-reset` `/password-reset`,
`/login/provider/success`
]; ];
export const languageMap = { export const languageMap = {

View File

@@ -0,0 +1,47 @@
import { useEffect, useState } from 'react';
import jwt_decode from 'jwt-decode';
import SecurityClient, { PROVIDER_AUTH_TOKEN_KEY } from '@app/components/utilities/SecurityClient';
export const useProviderAuth = () => {
const [email, setEmail] = useState<string>('');
const [userId, setUserId] = useState<string>('');
const [providerAuthToken, setProviderAuthToken] = useState<string>(
SecurityClient.getProviderAuthToken() || ''
);
useEffect(() => {
const handleStorageChange = (event: StorageEvent) => {
if (event.storageArea === localStorage && event.key === PROVIDER_AUTH_TOKEN_KEY) {
if (event.newValue) {
const token = event.newValue;
const { userId: resultUserId, email: resultEmail } = jwt_decode(token) as any;
setProviderAuthToken(token);
setEmail(resultEmail);
setUserId(resultUserId);
} else {
setProviderAuthToken('');
setEmail('');
setUserId('');
}
setProviderAuthToken(event.newValue || '');
}
};
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
});
return {
email,
providerAuthToken,
userId,
setProviderAuthToken,
setEmail,
setUserId,
};
};

View File

@@ -0,0 +1,16 @@
import { useEffect } from "react";
import { useRouter } from "next/router"
import SecurityClient from '@app/components/utilities/SecurityClient';
export default function LoginProviderSuccess() {
const router = useRouter();
useEffect(() => {
const { token } = router.query;
SecurityClient.setProviderAuthToken(token as string);
window.close();
}, [])
return <div />
}

View File

@@ -30,9 +30,7 @@ export default function SignUp() {
const [codeError, setCodeError] = useState(false); const [codeError, setCodeError] = useState(false);
const [step, setStep] = useState(1); const [step, setStep] = useState(1);
const router = useRouter(); const router = useRouter();
const {data: serverDetails } = useFetchServerStatus() const { data: serverDetails } = useFetchServerStatus();
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {

View File

@@ -4,16 +4,17 @@ server {
location /api { location /api {
proxy_set_header X-Real-RIP $remote_addr; proxy_set_header X-Real-RIP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true; proxy_set_header X-NginX-Proxy true;
proxy_pass http://backend:4000; proxy_pass http://backend:4000;
proxy_redirect off; proxy_redirect off;
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; proxy_cookie_path / "/; secure; HttpOnly; SameSite=lax";
} }
location / { location / {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;