awinogrodzki / next-firebase-auth-edge

Next.js Firebase Authentication for Edge and Node.js runtimes. Compatible with latest Next.js features.
https://next-firebase-auth-edge-docs.vercel.app/
MIT License
517 stars 44 forks source link

Error: MISSING_CREDENTIALS: Missing credentials #271

Open steve-marmalade opened 5 days ago

steve-marmalade commented 5 days ago

Hello! I am seeing lots of log lines like the following:

Error: MISSING_CREDENTIALS: Missing credentials

I believe this came after updating to 1.8 but not 100% sure.

Can you provide some more background on this? For reference, it's expected that a lot of our traffic is logged-out so I don't think of missing credentials as an error state. So is this something I should ignore (and if so, is there a way to silence this message?) or is it indicating some kind of configuration error on my part?

Thank you.

awinogrodzki commented 4 days ago

Hey @steve-marmalade,

Thanks for reporting!

In 1.8 I overhauled how credentials are parsed and serialized into cookies, which might've introduced this issue.

Do I understand that you get this error in handleError callback?

Could you provide more details, ideally a stack trace?

teresal92 commented 1 day ago

Hi @awinogrodzki, I just started using v1.8.0 and similarly I'm also seeing this error in my logs whenever the authMiddleware is run. Here is the stack trace on my end:

    at (node_modules/.pnpm/next-firebase-auth-edge@1.8.0_next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1_/node_modules/next-firebase-auth-edge/browser/next/cookies/parser/SingleCookieParser.js:13:0)
    at (node_modules/.pnpm/next-firebase-auth-edge@1.8.0_next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1_/node_modules/next-firebase-auth-edge/browser/next/tokens.js:18:0)
    at (node_modules/.pnpm/next-firebase-auth-edge@1.8.0_next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1_/node_modules/next-firebase-auth-edge/browser/next/middleware.js:107:29)
    at (middleware.ts:14:9)
    at (node_modules/.pnpm/next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/esm/server/web/adapter.js:175:17)
    at (node_modules/.pnpm/next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/esm/server/async-storage/request-async-storage-wrapper.js:95:0)
    at (node_modules/.pnpm/next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/esm/server/web/adapter.js:166:45)
    at (node_modules/.pnpm/next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/esm/server/lib/trace/tracer.js:114:0)
    at (node_modules/.pnpm/next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:7052)
    at (node_modules/.pnpm/next@14.2.9_@babel+core@7.25.2_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:480)
awinogrodzki commented 1 day ago

Hey @teresal92 !

Thanks for chiming in! Could you share your middleware.ts file?

I am interested in what's on line 14: at (middleware.ts:14:9)

goncaloalves commented 1 day ago

I am also getting the same error, after having following the docs.

Here is my middleware.ts:

import type { NextRequest } from "next/server";
import { authMiddleware } from "next-firebase-auth-edge";

export async function middleware(request: NextRequest) {
  console.log(process.env.NEXT_PUBLIC_FIREBASE_API_KEY)
  return authMiddleware(request, {
    debug: true,
    loginPath: "/api/login",
    logoutPath: "/api/logout",
    apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
    cookieName: "AuthToken",
    cookieSignatureKeys: ["Key-Should-Be-at-least-32-bytes-in-length"],
    cookieSerializeOptions: {
      path: "/",
      httpOnly: true,
      secure: false, // Set this to true on HTTPS environments
      sameSite: "lax" as const,
      maxAge: 12 * 60 * 60 * 24, // Twelve days
    },
    serviceAccount: {
      projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
      clientEmail: process.env.NEXT_PUBLIC_FIREBASE_CLIENT_EMAIL,
      privateKey: process.env.NEXT_FIREBASE_PRIVATE_KEY,
    },
  });
}

export const config = {
  matcher: ["/api/login", "/api/logout", "/", "/((?!_next|favicon.ico|api|.*\\.).*)"],
};
awinogrodzki commented 1 day ago

Thank you @goncaloalves! Would you be able to share the stack trace as well? I just want to rule out different sources for the issue

goncaloalves commented 1 day ago

i just have this:

ⓘ next-firebase-auth-edge: Handle request                                                                                                                         
path: /signin
ⓘ next-firebase-auth-edge: Token is missing or has incorrect formatting. This is expected and usually means that user has not yet logged in                       
message: MISSING_CREDENTIALS: Missing credentials                                                                                                        
reason: MISSING_CREDENTIALS                                                                                                                             
 stack: Error: MISSING_CREDENTIALS: Missing credentials
    at SingleCookieParser.parseCookies (webpack-internal:///(middleware)/./node_modules/next-firebase-auth-edge/browser/next/cookies/parser/SingleCookieParser.js:20:19)
    at getRequestCookiesTokens (webpack-internal:///(middleware)/./node_modules/next-firebase-auth-edge/browser/next/tokens.js:37:19)
    at authMiddleware (webpack-internal:///(middleware)/./node_modules/next-firebase-auth-edge/browser/next/middleware.js:126:97)
    at Object.middleware [as handler] (webpack-internal:///(middleware)/./middleware.ts:10:83)
    at eval (webpack-internal:///(middleware)/./node_modules/next/dist/esm/server/web/adapter.js:197:31)
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at Object.wrap (webpack-internal:///(middleware)/./node_modules/next/dist/esm/server/async-storage/request-async-storage-wrapper.js:105:24)
    at eval (webpack-internal:///(middleware)/./node_modules/next/dist/esm/server/web/adapter.js:188:122)
    at eval (webpack-internal:///(middleware)/./node_modules/next/dist/esm/server/lib/trace/tracer.js:115:36)
    at NoopContextManager.with (webpack-internal:///(middleware)/./node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js:58:24)
awinogrodzki commented 1 day ago

@goncaloalves does the issue still log when you change to debug: false, in authMiddleware?

goncaloalves commented 1 day ago

yes, I placed debug true to try and debug it myself.

teresal92 commented 1 day ago

Hey @awinogrodzki, here's my middleware.ts:

line 14 is returning authMiddleware

import {
  authMiddleware,
  redirectToPath,
  redirectToLogin,
} from 'next-firebase-auth-edge'
import { serverConfig, clientConfig } from './lib/firebase/config'

const PUBLIC_PATHS = ['/signup', '/login', '/forgot-password', '/']
const PROTECTED_PATHS = ['/home', '/home/onboarding']

export async function middleware(request: NextRequest) {
  // https://github.com/awinogrodzki/next-firebase-auth-edge/blob/main/examples/next-typescript-minimal/middleware.ts
  return authMiddleware(request, {
    loginPath: '/api/login',
    logoutPath: '/api/logout',
    apiKey: clientConfig.apiKey,
    cookieName: serverConfig.cookieName,
    cookieSignatureKeys: serverConfig.cookieSignatureKeys,
    cookieSerializeOptions: serverConfig.cookieSerializeOptions,
    serviceAccount: serverConfig.serviceAccount,
    handleValidToken: async ({ token }, headers) => {
      // Authenticated user should not be able to access /login, /signup and /forgot-password routes
      if (PUBLIC_PATHS.includes(request.nextUrl.pathname) && token) {
        return redirectToPath(request, '/home')
      }

      return NextResponse.next({
        request: {
          headers,
        },
      })
    },
    handleInvalidToken: async (reason) => {
      if (PROTECTED_PATHS.includes(request.nextUrl.pathname)) {
        console.info('Missing or malformed credentials', { reason })

        // redirect to /login if the user is not authenticated
        return redirectToLogin(request, {
          path: '/login',
          publicPaths: PUBLIC_PATHS,
        })
      }
      return NextResponse.next()
    },
    handleError: async (error) => {
      console.error('Unhandled authentication error', { error })

      return redirectToLogin(request, {
        path: '/login',
        publicPaths: PUBLIC_PATHS,
      })
    },
  })
}

export const config = {
  matcher: [
    '/api/login',
    '/api/logout',
    '/((?!api|_next/static|_next/image|.*\\.).*)',
  ],
}
awinogrodzki commented 1 day ago

Thank you @teresal92!

That's interesting, I am not able to reproduce the issue myself – @teresal92, do you have the rest of the stack trace? Does it log out as Unhandled authentication error or Missing or malformed credentials?

Ultimately, I am most interested if the error is logged in handleInvalidToken or handleError method. console.info('Missing or malformed credentials', { reason }) is purely informative and can be safely removed. handleInvalidToken is expected to be called.

If the error is logged out insidehandleError, then it's something that I need to pin-point and fix

There is also a third case where the error is unhandled by neither handleInvalidToken nor handleError, which would also be identifiable by the rest of the stack trace

awinogrodzki commented 1 day ago

@goncaloalves could you share the logs of the error with debug: false? I want to understand if the error is somehow recoverable. If yes, what is the source of it

michael5r commented 22 hours ago

I'm seeing the same issue, btw - it started with the 1.8 update.

Error: MISSING_CREDENTIALS: Missing credentials at (../../node_modules/next-firebase-auth-edge/browser/next/cookies/parser/SingleCookieParser.js:13:0)

Everything works fine in localhost, but I get the error on my Vercel staging site.

awinogrodzki commented 14 hours ago

Hey @michael5r,

Does the app stop from working after this error, or is it recoverable?

Could you share the rest of the stack-trace? SingleCookieParser can be used in multiple places. I need to understand whether the issue originates from getTokens, authMiddleware or one of the error handling functions

awinogrodzki commented 13 hours ago

I think I was finally able to reproduce the issue on Vercel – thanks everyone for cooperation – I will start working on a fix shortly

awinogrodzki commented 6 hours ago

It seems that authMiddleware error is not catched by neither handleInvalidError nor handleError. The following condition returns false on some Edge Middleware executions:

error instanceof InvalidTokenError

It's not consistent. Most of the time the check works as expected. I think it has something to do with a changes I did around ESM, Browser and Node modules. One reason for it might be that Vercel is using different type of InvalidTokenError constructor between throwing an error and checking it's instance using instanceof operator.

I've added some additional logs in latest canary release and will provide further updates soon

awinogrodzki commented 3 hours ago

I think I know what's happening. Looks like Vercel is trying to guess and log errors that resulted from unhandled promises. It may involve a Proxy over Promise and Error classes.

The app handles the error correctly, but despite that it is picked up by Vercel and logged as error. It can be somehow related to the v1.8 though

@steve-marmalade, @teresal92, @goncaloalves, @michael5r can I ask you to confirm that you all see the error in Vercel, but not locally (with debug: false)? Most likely it's a false positive due to some change on how Vercel reports Errors.

I will research further if we could mark this as error as handled in Vercel

michael5r commented 1 hour ago

@awinogrodzki Yep, it doesn't happen locally - only on Vercel.

awinogrodzki commented 31 minutes ago

I have found a potential fix for the issue.

It's related to the problem mentioned in the previous comment. The details can be found in PR description

Could you install next-firebase-auth-edge@1.8.2-canary.10 and let me know if it works for you?