{"id":"47adb12df3eeb148","repo":"nestjs/nest","slug":"unauthorized-47adb1","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"sample/19-auth-jwt/src/auth/auth.service.ts","lineNumber":15,"sourceCode":"import { Injectable, UnauthorizedException } from '@nestjs/common';\nimport { JwtService } from '@nestjs/jwt';\nimport { UsersService } from '../users/users.service';\n\n@Injectable()\nexport class AuthService {\n  constructor(\n    private usersService: UsersService,\n    private jwtService: JwtService,\n  ) {}\n\n  async signIn(username: string, pass: string) {\n    const user = await this.usersService.findOne(username);\n    if (user?.password !== pass) {\n      throw new UnauthorizedException();\n    }\n    const payload = { username: user.username, sub: user.userId };\n    return {\n      access_token: await this.jwtService.signAsync(payload),\n    };\n  }\n}\n","sourceCodeStart":1,"sourceCodeEnd":23,"githubUrl":"https://github.com/nestjs/nest/blob/6ec0e2783d15290732447f304d8549b591b9749e/sample/19-auth-jwt/src/auth/auth.service.ts#L1-L23","documentation":"NestJS's UnauthorizedException (HTTP 401), thrown by AuthService.signIn at sample/19-auth-jwt/src/auth/auth.service.ts:15 when the credential check `user?.password !== pass` fails. Because optional-chaining is used, BOTH cases collapse into the same 401: a non-existent user (findOne returns undefined so user?.password is undefined) and a wrong password. The built-in exception is caught by NestJS's global exception filter and serialised as { statusCode: 401, message: 'Unauthorized', error: 'Unauthorized' }.","triggerScenarios":"POST to the auth/login route (wired to signIn) with a username not present in the backing store, or with a password that differs from the stored value. Also fires when UsersService.findOne returns undefined because seed/fixture users were never loaded.","commonSituations":"Fresh dev DB without seeded users; plaintext-vs-hashed mismatch (e.g., DB stores a bcrypt hash but the code compares it verbatim to a plaintext input); frontend posting the wrong field name; e2e/unit tests that skip seeding; case-sensitivity differences in username.","solutions":["Confirm the user exists in the data source the app points at (check UsersService.findOne and the underlying DB/fixture) before assuming a wrong password.","Verify the password comparison strategy: if the store keeps hashes, compare with bcrypt.compare(pass, user.password) rather than a raw !== check.","Ensure test/dev seed scripts run before the request (e.g., the sample's UsersService seed).","Make the failure explicit: split the null-user case from the wrong-password case, or log the username for debugging (never log the password)."],"exampleFix":"// before\nconst user = await this.usersService.findOne(username);\nif (user?.password !== pass) {\n  throw new UnauthorizedException();\n}\n\n// after\nconst user = await this.usersService.findOne(username);\nif (!user || !(await bcrypt.compare(pass, user.password))) {\n  throw new UnauthorizedException('Invalid credentials');\n}","handlingStrategy":"validation","validationCode":"// Validate the request shape before calling signIn\nfunction isSignInDto(v: unknown): v is { username: string; password: string } {\n  return typeof v === 'object' && v !== null\n    && typeof (v as any).username === 'string' && (v as any).username.length > 0\n    && typeof (v as any).password === 'string' && (v as any).password.length > 0;\n}\n\nif (!isSignInDto(body)) {\n  throw new BadRequestException('username and password are required');\n}\n// Preferably: apply a DTO + global ValidationPipe at the controller boundary.","typeGuard":"function isUser(u: unknown): u is { username: string; userId: string; password: string } {\n  return typeof u === 'object' && u !== null\n    && typeof (u as any).username === 'string'\n    && typeof (u as any).userId === 'string';\n}","tryCatchPattern":"try {\n  const token = await authService.signIn(username, password);\n} catch (e) {\n  if (e instanceof UnauthorizedException) {\n    // 401 — return generic 'invalid credentials', never reveal which part failed\n    return reply.code(401).send({ message: 'Invalid credentials' });\n  }\n  throw e;\n}","preventionTips":["Always seed the user store in dev/test before running auth flows.","Hash passwords with bcrypt and compare via bcrypt.compare, never plaintext.","Apply a DTO with class-validator at the controller so malformed input never reaches signIn.","Use the same error message for 'user not found' and 'wrong password' to avoid user enumeration."],"tags":["auth","nestjs","http-401","credentials","jwt"],"analyzedSha":"6ec0e2783d15290732447f304d8549b591b9749e","analyzedAt":"2026-08-03T17:42:23.673Z","schemaVersion":2}