Skip to content

Commit 82c2470

Browse files
authored
feat(users): enhance and implement new OAuth flow (#451)
* feat(users): enhance security and performance - Fix user enumeration vulnerabilities with generic error responses - Add rate limiting to critical endpoints using @nestjs/throttler - Implement batch getUsersDtoByIds to resolve N+1 query issues - Replace synchronous bcrypt operations with async versions - Add secure login with constant-time authentication * feat(oauth): implement user-friendly OAuth flow with decision page - Replace automatic user creation with user choice mechanism - Add OAuth state token system for secure multi-step authentication - Implement decision page flow allowing users to: * Create new account with customizable username/nickname * Bind OAuth account to existing user account - Add new API endpoints: * GET /users/auth/oauth/state - retrieve OAuth state information * POST /users/oauth/create - create new user from OAuth decision * POST /users/oauth/bind - bind OAuth to existing user - Enhance nickname validation to support Chinese characters - Improve error handling with better user-facing messages - Add comprehensive test coverage for new OAuth scenarios Breaking Changes: - Replace UsersService.loginWithOAuth() with initiateOAuthFlow() - OAuth callback now redirects to different pages based on user state: * /oauth-success - existing OAuth connection * /oauth-verify - email conflict (force binding) * /oauth-complete - new decision page for user choice This improves user experience by eliminating forced automatic registration and allowing users to maintain control over their account creation process. * fix: resolve SRP auth test failures and add error filter to users controller - Update SRP tests to expect generic InvalidLoginCredentialsError - Add @UseFilters decorator to UsersController - Fix user profile e2e test user ID * fix: resolve test failures and database constraint issues - Update SRP tests to use generic error types - Add error filter to users controller - Fix foreign key constraint in user profile queries * fix(auth): prevent type confusion in token validation Add explicit string type check before calling indexOf() to prevent parameter tampering attacks in verify() and decode() methods * test(users): add unit tests for cookie helper utilities * test(users): enhance unit tests for OAuth user creation and login flows
1 parent 3803d29 commit 82c2470

File tree

13 files changed

+4179
-592
lines changed

13 files changed

+4179
-592
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@nestjs/mapped-types": "^2.0.6",
4040
"@nestjs/platform-express": "^10.4.15",
4141
"@nestjs/serve-static": "^4.0.2",
42+
"@nestjs/throttler": "^6.4.0",
4243
"@prisma/client": "6.4.1",
4344
"@ruc-cheese/node-srp-rs": "^0.2.2",
4445
"@simplewebauthn/server": "^13.1.1",
@@ -50,6 +51,7 @@
5051
"class-transformer": "^0.5.1",
5152
"class-validator": "^0.14.1",
5253
"connect-redis": "^8.0.1",
54+
"cookie-parser": "^1.4.7",
5355
"cross-env": "^7.0.3",
5456
"express": "^4.21.2",
5557
"express-session": "^1.18.1",
@@ -77,6 +79,7 @@
7779
"@nestjs/schematics": "^10.1.4",
7880
"@nestjs/testing": "^10.4.15",
7981
"@types/bcryptjs": "^3.0.0",
82+
"@types/cookie-parser": "^1.4.9",
8083
"@types/express": "^5.0.0",
8184
"@types/express-session": "^1.18.1",
8285
"@types/fluent-ffmpeg": "^2.1.27",

pnpm-lock.yaml

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/auth/auth.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class AuthService {
7979
// Parameters:
8080
// token: both the pure jwt token and the one with "Bearer " or "bearer " are supported.
8181
verify(token: string | undefined): Authorization {
82-
if (token == undefined || token == '')
82+
if (token == undefined || token == '' || typeof token !== 'string')
8383
throw new AuthenticationRequiredError();
8484
if (token.indexOf('Bearer ') == 0) token = token.slice(7);
8585
else if (token.indexOf('bearer ') == 0) token = token.slice(7);
@@ -225,7 +225,7 @@ export class AuthService {
225225

226226
// Decode a token, WITHOUT verifying it.
227227
decode(token: string | undefined): TokenPayload {
228-
if (token == undefined || token == '')
228+
if (token == undefined || token == '' || typeof token !== 'string')
229229
throw new AuthenticationRequiredError();
230230
if (token.indexOf('Bearer ') == 0) token = token.slice(7);
231231
else if (token.indexOf('bearer ') == 0) token = token.slice(7);

src/main.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { NestFactory } from '@nestjs/core';
22
import { RedisStore } from 'connect-redis';
3+
import cookieParser from 'cookie-parser';
34
import session from 'express-session';
45
import Redis from 'ioredis';
56
import { AppModule } from './app.module';
@@ -19,6 +20,9 @@ async function bootstrap() {
1920
};
2021
app.enableCors(corsOptions);
2122

23+
// Configure cookie parser middleware
24+
app.use(cookieParser(process.env.COOKIE_SECRET));
25+
2226
const redis = new Redis({
2327
host: process.env.REDIS_HOST ?? 'localhost',
2428
port: process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : 6379,

0 commit comments

Comments
 (0)