blitz-js / blitz

⚡️ The Missing Fullstack Toolkit for Next.js
https://Blitzjs.com
MIT License
13.63k stars 799 forks source link

Bad request error is thrown instead of AuthenticationError #4207

Open rimselis opened 1 year ago

rimselis commented 1 year ago

What is the problem?

Error: Bad request (400) error is thrown when query is set to be authorized.

I have created a simple query on a new project to return an empty array and used it on the index page's UserInfo component.

import { resolver } from "@blitzjs/rpc";

export default resolver.pipe(
  // resolver.authorize(),
  async ({}) => {
    return [];
  }
);

When resolver.authorize() is commented out, I can successfully use the query in component const [users] = useQuery(getUsers, {}) and it returns an empty array with no issues. But when I uncomment resolver.authorize(), a 400: Bad request error is shown. By default, blitz creates a RootErrorFallback component to be used in ErrorBoundary component as fallback.

RootErrorFallback component checks for different types of errors:

  if (error instanceof AuthenticationError) {
    return ...
  } else if (error instanceof AuthorizationError) {
    return ...
  } else {
    return ...
  }

For some reason, the AuthenticationError is skipped and the default/fallback else statement is returning the ErrorComponent.

On the server-side logs, I can see the correct issue ([AuthenticationError: You must be logged in to access this] {statusCode: 401}) being logged, but on the client side it's just an Uncaught Error: Bad Request error

Paste all your error logs here:

[Rendering Suspense fallback...: DYNAMIC_SERVER_USAGE] {
  digest: 'DYNAMIC_SERVER_USAGE'
}
2023-08-28 15:51:59.147 INFO    [blitz-rpc] getCurrentUser() Starting with input: null
2023-08-28 15:51:59.147 DEBUG   [blitz-rpc] getCurrentUser() Result: null
2023-08-28 15:51:59.147 DEBUG   [blitz-rpc] getCurrentUser() Next.js serialization:0ms
2023-08-28 15:51:59.147 INFO    [blitz-rpc] getCurrentUser() Finished: resolver:0ms serializer:0ms total:0ms
2023-08-28 15:51:59.162 INFO    [blitz-rpc] getUsers() Starting with input: {}
[AuthenticationError: You must be logged in to access this] {
  statusCode: 401
}

Error while processing the request
TypeError: Cannot read properties of undefined (reading 'map')
    at prettyFormatErrorObj (/.../blitz-test/node_modules/tslog/dist/cjs/runtime/nodejs/index.js:95:47)
    at maskedArgs.reduce.args (/.../blitz-test/node_modules/tslog/dist/cjs/runtime/nodejs/index.js:89:43)
    at Array.reduce (<anonymous>)
    at Object.prettyFormatLogObj (/.../blitz-test/node_modules/tslog/dist/cjs/runtime/nodejs/index.js:88:23)
    at Logger.log (/.../blitz-test/node_modules/tslog/dist/cjs/BaseLogger.js:111:77)
    at Logger.error (/.../blitz-test/node_modules/tslog/dist/cjs/index.js:51:22)
    at /.../blitz-test/node_modules/@blitzjs/rpc/dist/index-server.cjs:326:15
    at Generator.throw (<anonymous>)
    at rejected (/.../blitz-test/node_modules/@blitzjs/rpc/dist/index-server.cjs:127:29)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Browser console:

Uncaught Error: Bad Request
[rpc.mjs:81](webpack://_N_E/node_modules/@blitzjs/rpc/dist/chunks/rpc.mjs?506a)
    promise rpc.mjs:81
    InterpretGeneratorResume self-hosted:1455
    next self-hosted:1376
    __async rpc.mjs:23
    __async rpc.mjs:7
    promise rpc.mjs:76
    (Async: promise callback)
    httpClient rpc.mjs:76
    InterpretGeneratorResume self-hosted:1455
    next self-hosted:1376
    fulfilled rpc.mjs:10
    (Async: promise callback)
    step rpc.mjs:22
    __async rpc.mjs:23
    __async rpc.mjs:7
    httpClient rpc.mjs:37
    queryFn index.mjs:71
    fetchFn query.mjs:252
    run retryer.mjs:97
    createRetryer retryer.mjs:145
    fetch query.mjs:306
    fetchOptimistic queryObserver.mjs:150
    fetchOptimistic suspense.mjs:12
    useBaseQuery useBaseQuery.mjs:47
    useQuery useQuery.mjs:6
    useQuery index.mjs:69
    UserInfo index.tsx:18
    React 13
    NextJS 2
The above error occurred in the <UserInfo> component:

UserInfo@webpack-internal:///./src/pages/index.tsx:32:103
Suspense
div
div
div
main
div
Layout@webpack-internal:///./src/core/layouts/Layout.tsx:12:33
Home
AuthRoot@webpack-internal:///./node_modules/@blitzjs/auth/dist/chunks/index.mjs:387:15
_a@webpack-internal:///./node_modules/@blitzjs/next/dist/chunks/index-browser.mjs:176:7
WithRouterWrapper@webpack-internal:///./node_modules/next/dist/client/with-router.js:17:43
MyApp@webpack-internal:///./src/pages/_app.tsx:49:38
Hydrate@webpack-internal:///./node_modules/@tanstack/react-query/build/lib/Hydrate.mjs:29:17
QueryClientProvider@webpack-internal:///./node_modules/@tanstack/react-query/build/lib/QueryClientProvider.mjs:46:29
BlitzProvider@webpack-internal:///./node_modules/@blitzjs/next/dist/chunks/index-browser.mjs:109:23
BlitzOuterRoot@webpack-internal:///./node_modules/@blitzjs/next/dist/chunks/index-browser.mjs:465:105
WithSuperJSON@webpack-internal:///./node_modules/@blitzjs/next/dist/chunks/index-browser.mjs:103:104
PathnameContextProviderAdapter@webpack-internal:///./node_modules/next/dist/shared/lib/router/adapters.js:74:44
ErrorBoundary@webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:305:63
ReactDevOverlay@webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:854:908
Container@webpack-internal:///./node_modules/next/dist/client/index.js:83:1
AppContainer@webpack-internal:///./node_modules/next/dist/client/index.js:187:25
Root@webpack-internal:///./node_modules/next/dist/client/index.js:365:37

React will try to recreate this component tree from scratch using the error boundary you provided, _a.

Paste all relevant code snippets here:

const [users] = useQuery(getUsers, {})
import { resolver } from "@blitzjs/rpc";

export default resolver.pipe(
  // resolver.authorize(),
  async ({}) => {
    return [];
}
);
export default async function getUsers(input, ctx) {
  // ctx.session.$authorize();
  return [];
}

What are detailed steps to reproduce this?

  1. Create new Blitz project (TS, Full, yarn, hook form)
  2. Create a new query file src/users/queries/getUsers.ts
  3. Write the query to return anything (array, object, boolean, etc.)
  4. Import and the query in UserInfo component via useQuery from @blitzjs/rpc
  5. Start the project (blitz dev), go to localhost, create a user/login - everything should work
  6. Uncomment resolver.authorize(), or ctx.session.$authorize(); in query file
  7. Log out the user
  8. ErrorComponent will be shown instead of Error: You are not authenticated

Run blitz -v and paste the output here:

Blitz version: 2.0.0-beta.32 (global)
Blitz version: 2.0.0-beta.32 (local)
macOS Ventura | darwin-arm64 | Node: v18.16.1

 Package manager: npm

  System:
    OS: macOS 13.4.1
    CPU: (8) arm64 Apple M2
    Memory: 874.50 MB / 24.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.16.1 - ~/.nvm/versions/node/v18.16.1/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.16.1/bin/yarn
    npm: 9.5.1 - ~/.nvm/versions/node/v18.16.1/bin/npm
  npmPackages:
    @blitzjs/auth: 2.0.0-beta.32 => 2.0.0-beta.32
    @blitzjs/next: 2.0.0-beta.32 => 2.0.0-beta.32
    @blitzjs/rpc: 2.0.0-beta.32 => 2.0.0-beta.32
    @prisma/client: 4.6.1 => 4.6.1
    blitz: 2.0.0-beta.32 => 2.0.0-beta.32
    next: 13.4.5 => 13.4.5
    prisma: 4.6.1 => 4.6.1
    react: 18.2.0 => 18.2.0
    react-dom: 18.2.0 => 18.2.0
    typescript: ^4.8.4 => 4.9.5

Please include below any other applicable logs and screenshots that show your problem:

Screenshot 2023-08-28 at 4 02 52 pm

rodobre commented 1 year ago

Experiencing the same issue on 2.0.0-beta.32. Same blitz version output as @rimselis

rimselis commented 1 year ago

Tested it from blitz@2.0.0-beta.28 to blitz@2.0.0-beta.32. Issue happens only on blitz@2.0.0-beta.32.

jrausell commented 1 year ago

Same issue on 2.0.0-beta.32.

Tested downgrading to blitz@2.0.0-beta.28 as commented by Rimselis, and also .beta-31. In both appear but the expected message "Error: You are not authenticated"

siddhsuresh commented 1 year ago

Hey @rimselis, could you add an explicit Superjson registration in your blitz-sever.ts file and check.

//blitz-server.ts
...
  SuperJson.registerClass(AuthenticationError, {
    identifier: "BlitzAuthenticationError",
    allowProps: ["name", "message", "code", "statusCode", "meta", "url"],
  })
mjyoung commented 1 year ago

I'm on 2.0.0-beta.33 and am still getting a 400 instead of the expected 401 AuthenticationError. This only occurs in my production env and not in local dev.

This occurs when I am using resolver.authorize(Role.Admin) for example.

Are you sure it was fixed @rodobre ? This is what I see instead of the expected error fallback: image

aaly00 commented 1 year ago

~I am seeing the same issue. For some reason it only happens on my machine, and doesn't happen in production on Vercel.~ Upgrading to beta.33 fixes the issue. @mjyoung Did you make sure to update all blitz packages ? The fix is in the RPC package.

image
mjyoung commented 1 year ago

Yes, all packages are updated to 2.0.0-beta.33:

    "@blitzjs/auth": "2.0.0-beta.33",
    "@blitzjs/next": "2.0.0-beta.33",
    "@blitzjs/rpc": "2.0.0-beta.33",
rimselis commented 1 year ago

The initial issue seems to be fixed with 2.0.0-beta.33 (checked both with resolver.authorize() and with ctx.session.$authorize()) on the same fresh project. Thank you!

Not sure if I should close this as there are open questions in the comments.

mjyoung commented 1 year ago

Not sure what I'm doing wrong then. I'm on 2.0.0-beta.33 and in production, my error boundaries do not trigger on instanceof AuthenticationError and instanceof AuthorizationError.

Above still doesn't work even if I add the following to blitz-server:

SuperJSON.registerClass(AuthenticationError);
SuperJSON.registerClass(AuthorizationError);

Can I use instanceof or do I need to do if (error.name === 'AuthenticationError)?