Delveroff
Delveroff•5mo ago

ConvexAuth+Next.js: should I implement `/api/sign-in`?

I try to set up ConvexAuth+Next.js. When I call useAuthActions().signIn('resend', formData), I see that the request goes to my server's api/sign-in, which is 404. I read here that this is the default value. However, the docs does not mention that I have to configure this route somehow. I thought this is handled by convexAuthMiddleware, but this isn't true. I even tried to do the following, but without success:
import { ConvexAuthNextjsServerProvider } from '@convex-dev/auth/nextjs/server'
import type { PropsWithChildren } from 'react'

export function ConvexServerProvider({ children }: PropsWithChildren) {
return (
<ConvexAuthNextjsServerProvider
apiRoute={`${process.env['NEXT_PUBLIC_CONVEX_URL']}/auth/sign-in`}
>
{children}
</ConvexAuthNextjsServerProvider>
)
}
import { ConvexAuthNextjsServerProvider } from '@convex-dev/auth/nextjs/server'
import type { PropsWithChildren } from 'react'

export function ConvexServerProvider({ children }: PropsWithChildren) {
return (
<ConvexAuthNextjsServerProvider
apiRoute={`${process.env['NEXT_PUBLIC_CONVEX_URL']}/auth/sign-in`}
>
{children}
</ConvexAuthNextjsServerProvider>
)
}
Maybe I'm missing something but it's really unclear what to do with the api/sign-in route. Also, I'm acknowledged that auth.js, especially next-auth, tells us to create these routes. But since Convex Auth docs doesn't mention it, I'm not sure if these two approaches will conflict.
nextjs/server - Convex Auth
Authentication library for your Convex backend
40 Replies
Delveroff
DelveroffOP•5mo ago
By the way, I still receive the confirmation email, and database records are created. But signIn throws the error because it tries to res.json() of a 404 page which is an HTML
Michal Srb
Michal Srb•5mo ago
What does your middleware.ts file look like? And did you change the client provider to the Next.js one? (in your ConvexClientProvider)
Delveroff
DelveroffOP•5mo ago
The middleware:
import { convexAuthNextjsMiddleware } from '@convex-dev/auth/nextjs/server'

export default convexAuthNextjsMiddleware()

export const config = {
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api)(.*)'],
}
import { convexAuthNextjsMiddleware } from '@convex-dev/auth/nextjs/server'

export default convexAuthNextjsMiddleware()

export const config = {
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api)(.*)'],
}
The client provider:
'use client'

import { ConvexAuthNextjsProvider } from '@convex-dev/auth/nextjs'
import { ConvexReactClient } from 'convex/react'
import type { PropsWithChildren } from 'react'

const convexProvider = new ConvexReactClient(
process.env['NEXT_PUBLIC_CONVEX_URL'] ?? '',
)

export function ConvexClientProvider({ children }: PropsWithChildren) {
return (
<ConvexAuthNextjsProvider client={convexProvider}>
{children}
</ConvexAuthNextjsProvider>
)
}
'use client'

import { ConvexAuthNextjsProvider } from '@convex-dev/auth/nextjs'
import { ConvexReactClient } from 'convex/react'
import type { PropsWithChildren } from 'react'

const convexProvider = new ConvexReactClient(
process.env['NEXT_PUBLIC_CONVEX_URL'] ?? '',
)

export function ConvexClientProvider({ children }: PropsWithChildren) {
return (
<ConvexAuthNextjsProvider client={convexProvider}>
{children}
</ConvexAuthNextjsProvider>
)
}
Michal Srb
Michal Srb•5mo ago
Hmm, that's surprising. Did you restart your Next.js server?
Delveroff
DelveroffOP•5mo ago
Yes, I did. I've also just restarted it one more time to be sure.
Michal Srb
Michal Srb•5mo ago
And you're definitely getting 404? Can you screenshot the network tab?
kstulgys
kstulgys•5mo ago
does @convex-dev/auth/nextjs/server exist? is it installed with @convex-dev/auth "^0.0.47" seem to be latest?
Delveroff
DelveroffOP•5mo ago
No description
No description
Delveroff
DelveroffOP•5mo ago
@Michal Srb Could you please clarify if the middleware should intercept /api/auth? Because I don't have something like /app/api/auth/[...nextauth]/route.ts in my project, didn't see this in docs.
Michal Srb
Michal Srb•5mo ago
Yes, the middleware should intercept /api/auth What is your middleware file called, and where is it in your project directory?
Delveroff
DelveroffOP•5mo ago
It's located in the root of the application and is called middleware.ts (the root of the project, /middleware.ts). It's also included in the tsconfig.
Michal Srb
Michal Srb•5mo ago
Try: In middleware.ts:
export default () => {console.log("Hello from middleware"}
export default () => {console.log("Hello from middleware"}
first to see if the middleware file is running at all, and then In middleware.ts:
export default convexAuthNextjsMiddleware(() => {console.log("Hello from auth middleware"})
export default convexAuthNextjsMiddleware(() => {console.log("Hello from auth middleware"})
this should log on all page requests (but it should not log on /api/auth). I'm also assuming you are on latest Next.js version.
Delveroff
DelveroffOP•5mo ago
Both log, so the middleware works. Next.js is 14.2.5 However, I have left the config, just replaced the default export (removing the config would let them log anyway, but with much more entries)
Michal Srb
Michal Srb•5mo ago
instead of the console.log (either one), can you log new URL(request.url).pathname:
export default (request) => {console.log(new URL(request.url).pathname)}
export default (request) => {console.log(new URL(request.url).pathname)}
and see what you get when you try to hit the signIn / signOut action?
Delveroff
DelveroffOP•5mo ago
No description
Michal Srb
Michal Srb•5mo ago
Can you share the repo? (make a sharable version and push it to GitHub?)
Delveroff
DelveroffOP•5mo ago
Sure, I will create a repro. Will take ~20mins
Delveroff
DelveroffOP•5mo ago
GitHub
GitHub - BorisZubchenko/convex-auth-middleware-repro
Contribute to BorisZubchenko/convex-auth-middleware-repro development by creating an account on GitHub.
Delveroff
DelveroffOP•5mo ago
You will need AUTH_RESEND_KEY= To reproduce, go to the root page and fill up the form
Michal Srb
Michal Srb•5mo ago
Just looking at the code: Your middleware.ts is not in the root, it is in the app folder. It should be in the root of your project (sibling to package.json)
Delveroff
DelveroffOP•5mo ago
Checking
Michal Srb
Michal Srb•5mo ago
Looks like that's not an issue though
Delveroff
DelveroffOP•5mo ago
It's a repro mistake, it doesn't work even if the middleware is in the root (pushed the fix) Just checked it But I was worried for a moment 😅
Michal Srb
Michal Srb•5mo ago
Found the bug, uff, thanks!
Delveroff
DelveroffOP•5mo ago
By the way, I think this repo can be cloned in order to be used as a reproduction repo for issues. Because when I faced this bug, I was trying to find a complete example of a Next.js project.
Michal Srb
Michal Srb•5mo ago
There will be one, waiting on a new Convex NPM version before I publish it @Delveroff fixed: @convex-dev/auth@0.0.48-alpha.1
Delveroff
DelveroffOP•5mo ago
Thank you!
Michal Srb
Michal Srb•5mo ago
Thank you! Sorry about!
Delveroff
DelveroffOP•5mo ago
That's fine, this is the alpha version after all 🙂
Dorji Tshering
Dorji Tshering•5mo ago
@Michal Srb I am also facing a similar issue so thought to continue here. I have followed the documentation without missing anything for the email & password signin. However my set up is quite different as in, I have i18n set up, and also I am combining the convex middleware with i18n middleware. On signin, the request is being sent to /api/auth which returns 404. However, if i remove i18n setup and default exporting the convex middleware, it works(with an exception that I also have to pass flow property to the signIn function from convex to be either signIn or signUp which doesn't seem to be documented yet.). I believe the issue lies in the middleware composition. Would really appreciate if there is a documentation on how to combine convex middleware with others. My middleware file currently:
import {
convexAuthNextjsMiddleware,
createRouteMatcher,
isAuthenticatedNextjs,
nextjsMiddlewareRedirect,
} from "@convex-dev/auth/nextjs/server";
import createIntlMiddleware from "next-intl/middleware";

import { LOCALES } from "./i18n/locales";

export default convexAuthNextjsMiddleware((request) => {
// Create and call the next-intl middleware
const handleI18nRouting = createIntlMiddleware({
// A list of all locales that are supported
locales: LOCALES,
// Used when no locale matches
defaultLocale: "en",
});

const response = handleI18nRouting(request);

const locale = new URL(
response.headers.get("location") || request.url,
).pathname.split("/")[1];

const isAuthPage = createRouteMatcher([`/${locale}/auth(.*)`]);
const isProtectedRoute = createRouteMatcher([`/${locale}/dashboard(.*)`]);

if (isAuthPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}`);
}
if (isProtectedRoute(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}/auth/login`);
}
return response;
});

export const config = {
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - images (assets inside images folder under public)
* - favicon.ico (favicon file)
*/
matcher: ["/((?!api|_next/static|_next/image|images|assets|favicon.ico).*)"],
};
import {
convexAuthNextjsMiddleware,
createRouteMatcher,
isAuthenticatedNextjs,
nextjsMiddlewareRedirect,
} from "@convex-dev/auth/nextjs/server";
import createIntlMiddleware from "next-intl/middleware";

import { LOCALES } from "./i18n/locales";

export default convexAuthNextjsMiddleware((request) => {
// Create and call the next-intl middleware
const handleI18nRouting = createIntlMiddleware({
// A list of all locales that are supported
locales: LOCALES,
// Used when no locale matches
defaultLocale: "en",
});

const response = handleI18nRouting(request);

const locale = new URL(
response.headers.get("location") || request.url,
).pathname.split("/")[1];

const isAuthPage = createRouteMatcher([`/${locale}/auth(.*)`]);
const isProtectedRoute = createRouteMatcher([`/${locale}/dashboard(.*)`]);

if (isAuthPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}`);
}
if (isProtectedRoute(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}/auth/login`);
}
return response;
});

export const config = {
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - images (assets inside images folder under public)
* - favicon.ico (favicon file)
*/
matcher: ["/((?!api|_next/static|_next/image|images|assets|favicon.ico).*)"],
};
Michal Srb
Michal Srb•5mo ago
@Dorji Tshering your middleware looks good, except your config seems to be ruling out /api paths, which is where the middleware is proxying the signIn action by default. You can use createRouteMatcher to scope down the paths for which you'll apply the handleI18nRouting middleware.
Dorji Tshering
Dorji Tshering•5mo ago
@Michal Srb Oh wow, how did i miss that! Yup that was the issue, now working thanks a lot!!!! @Michal Srb I would like to continue here since it is also related to the OP question. I have deployed my app to vercel and I am getting the error EDGE_FUNCTION_INVOCATION_FAILED. Below is my log:
Error: [Request ID: 2bc8eabac770964d] Server Error
at (node_modules/convex/dist/esm/browser/http_client.js:258:0)
at (node_modules/@convex-dev/auth/dist/nextjs/server/proxy.js:21:0)
at (node_modules/@convex-dev/auth/dist/nextjs/server/index.js:49:0)
at (node_modules/next/dist/esm/server/web/adapter.js:158:0)
Error: [Request ID: 2bc8eabac770964d] Server Error
at (node_modules/convex/dist/esm/browser/http_client.js:258:0)
at (node_modules/@convex-dev/auth/dist/nextjs/server/proxy.js:21:0)
at (node_modules/@convex-dev/auth/dist/nextjs/server/index.js:49:0)
at (node_modules/next/dist/esm/server/web/adapter.js:158:0)
. And the request path for the error is /api/auth. Below is my middleware as it stands if it would help provide more context:
import {
convexAuthNextjsMiddleware,
createRouteMatcher,
isAuthenticatedNextjs,
nextjsMiddlewareRedirect,
} from "@convex-dev/auth/nextjs/server";
import { NextResponse } from "next/server";
import createIntlMiddleware from "next-intl/middleware";

import { LOCALES } from "./i18n/locales";

export default convexAuthNextjsMiddleware((request) => {
const isApiRoute = createRouteMatcher(["/api(.*)"]);

if (isApiRoute(request)) {
return NextResponse.next();
} else {
const handleI18nRouting = createIntlMiddleware({
locales: LOCALES,
defaultLocale: "en",
});

const response = handleI18nRouting(request);

const locale = new URL(
response.headers.get("location") || request.url,
).pathname.split("/")[1];

const isAuthPage = createRouteMatcher([`/${locale}/auth(.*)`]);
const isProtectedRoute = createRouteMatcher([`/${locale}/dashboard(.*)`]);

if (isAuthPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}`);
}
if (isProtectedRoute(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}`);
}
return response;
}
});

export const config = {
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - images (assets inside images folder under public)
* - favicon.ico (favicon file)
*/
matcher: ["/((?!_next/static|_next/image|images|assets|favicon.ico).*)"],
};
import {
convexAuthNextjsMiddleware,
createRouteMatcher,
isAuthenticatedNextjs,
nextjsMiddlewareRedirect,
} from "@convex-dev/auth/nextjs/server";
import { NextResponse } from "next/server";
import createIntlMiddleware from "next-intl/middleware";

import { LOCALES } from "./i18n/locales";

export default convexAuthNextjsMiddleware((request) => {
const isApiRoute = createRouteMatcher(["/api(.*)"]);

if (isApiRoute(request)) {
return NextResponse.next();
} else {
const handleI18nRouting = createIntlMiddleware({
locales: LOCALES,
defaultLocale: "en",
});

const response = handleI18nRouting(request);

const locale = new URL(
response.headers.get("location") || request.url,
).pathname.split("/")[1];

const isAuthPage = createRouteMatcher([`/${locale}/auth(.*)`]);
const isProtectedRoute = createRouteMatcher([`/${locale}/dashboard(.*)`]);

if (isAuthPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}`);
}
if (isProtectedRoute(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, `/${locale}`);
}
return response;
}
});

export const config = {
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - images (assets inside images folder under public)
* - favicon.ico (favicon file)
*/
matcher: ["/((?!_next/static|_next/image|images|assets|favicon.ico).*)"],
};
By the way everything is working locally, signin, signup, signout.
Michal Srb
Michal Srb•5mo ago
@Dorji Tshering what do you Convex logs show? (on your dashboard, you can search for 2bc8eabac770964d)
Dorji Tshering
Dorji Tshering•5mo ago
@Michal Srb This is the error I am getting: Uncaught Error: Missing JWT_PRIVATE_KEY environment variable. For development, I see those env but for production I don't see those, I don't exactly know how dev envs were generated. Should I be using same key for both? Plus I also see other two envs under dev deployment i.e. JWKS and SITE_URL. Do i need to define those in prod deployment as well?
Michal Srb
Michal Srb•5mo ago
Production - Convex Auth
Authentication library for your Convex backend
Dorji Tshering
Dorji Tshering•5mo ago
@Michal Srb I was searching through the convex docs instead auth docs my bad. Thank you so much for your help. Feels really confident going forward with convex. Really appreciate for your time!!!
hrutik_k
hrutik_k•5mo ago
I fixed the middleware location as well as version but still facing same issue cc @Michal Srb
Michal Srb
Michal Srb•5mo ago
@hrutik_k please open a new thread with all details (what's not working for you, what's your setup)
hrutik_k
hrutik_k•5mo ago
done

Did you find this page helpful?