Vike bug while playing around with AuthJS
Closed this issue · 2 comments
Description
This is my Bâti config I used to generate my workspace:
I want to redirect the user to a login page (that works) but now I want to render that login page. That would be done like this https://authjs.dev/guides/pages/signin
Please note: I have no skill in Vike nor Authjs, I'm discovering while figuring stuff out. This code is mainly AI generated because I do not know anything so I'm trying to learn :)
Here's my code for my login +Page.tsx:
import React, { useState } from 'react';
import { providerMap } from '@/server/authjs-handler';
export function Page() {
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const handleCredentialsSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setIsLoading(true);
setError(null);
const formData = new FormData(event.currentTarget);
try {
const response = await fetch('/api/auth/callback/credentials', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: formData.get('email'),
password: formData.get('password'),
})
});
if (!response.ok) {
const errorData = await response.json();
setError(errorData.message || 'Authentication failed');
return;
}
window.location.href = '/';
} catch (err) {
setError('An unexpected error occurred');
console.error(err);
} finally {
setIsLoading(false);
}
};
return (
<div className="max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md">
{error && (
<div className="mb-4 p-4 bg-red-100 text-red-700 rounded">
{error}
</div>
)}
<form onSubmit={handleCredentialsSubmit} className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email
</label>
<input
type="email"
id="email"
name="email"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
disabled={isLoading}
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
Password
</label>
<input
type="password"
id="password"
name="password"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
disabled={isLoading}
/>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
{isLoading ? 'Signing in...' : 'Sign in'}
</button>
</form>
<div className="mt-6">
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-white text-gray-500">Or continue with</span>
</div>
</div>
<div className="mt-6 space-y-2">
{Object.values(providerMap).map((provider) => (
<button
key={provider.id}
onClick={() => {
window.location.href = `/api/auth/signin/${provider.id}`;
}}
className="w-full py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Sign in with {provider.name}
</button>
))}
</div>
</div>
</div>
);
}
// Optional - Define page metadata
Page.title = 'Sign In';
// For Vike, we need to export the component as default
export default Page;
and here's the authjs-handler.ts file:
import { Auth, type AuthConfig, createActionURL, setEnvDefaults } from "@auth/core";
import CredentialsProvider from "@auth/core/providers/credentials";
import DiscordProvider from "@auth/core/providers/discord";
import type { Session } from "@auth/core/types";
import type { Provider } from "@auth/core/providers";
// TODO: stop using universal-middleware and directly integrate server middlewares instead. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.)
import type { Get, UniversalHandler, UniversalMiddleware } from "@universal-middleware/core";
const env: Record<string, string | undefined> =
typeof process?.env !== "undefined"
? process.env
: import.meta && "env" in import.meta
? (import.meta as ImportMeta & { env: Record<string, string | undefined> }).env
: {};
if (!globalThis.crypto) {
/**
* Polyfill needed if Auth.js code runs on node18
*/
Object.defineProperty(globalThis, "crypto", {
value: await import("node:crypto").then((crypto) => crypto.webcrypto as Crypto),
writable: false,
configurable: true,
});
}
const providers: Provider[] = [
// TODO: Choose and implement providers
CredentialsProvider({
name: "Credentials",
credentials: {
username: { label: "Username", type: "text", placeholder: "jsmith" },
password: { label: "Password", type: "password" },
},
async authorize() {
// Add logic here to look up the user from the credentials supplied
const user = { id: "1", name: "J Smith", email: "jsmith@example.com" };
// Any object returned will be saved in `user` property of the JWT
// If you return null then an error will be displayed advising the user to check their details.
// You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
return user ?? null;
},
}),
DiscordProvider({
clientId: process.env.DISCORD_CLIENT_ID ?? "",
clientSecret: process.env.DISCORD_CLIENT_SECRET ?? "",
}),
];
export const providerMap = providers
.map((provider) => {
if (typeof provider === "function") {
const providerData = provider()
return { id: providerData.id, name: providerData.name }
} else {
return { id: provider.id, name: provider.name }
}
})
.filter((provider) => provider.id !== "credentials")
const authjsConfig = {
pages: {
signIn: "/login",
signOut: "/logout",
},
basePath: "/api/auth",
trustHost: Boolean(env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production"),
// TODO: Replace secret {@see https://authjs.dev/reference/core#secret}
secret: "MY_SECRET",
providers,
} satisfies Omit<AuthConfig, "raw">;
/**
* Retrieve Auth.js session from Request
*/
export async function getSession(req: Request, config: Omit<AuthConfig, "raw">): Promise<Session | null> {
setEnvDefaults(process.env, config);
const requestURL = new URL(req.url);
const url = createActionURL("session", requestURL.protocol, req.headers, process.env, config);
const response = await Auth(new Request(url, { headers: { cookie: req.headers.get("cookie") ?? "" } }), config);
const { status = 200 } = response;
const data = await response.json();
if (!data || !Object.keys(data).length) return null;
if (status === 200) return data;
throw new Error(data.message);
}
/**
* Add Auth.js session to context
* @link {@see https://authjs.dev/getting-started/session-management/get-session}
**/
export const authjsSessionMiddleware: Get<[], UniversalMiddleware> = () => async (request, context) => {
try {
return {
...context,
session: await getSession(request, authjsConfig),
};
} catch (error) {
console.debug("authjsSessionMiddleware:", error);
return {
...context,
session: null,
};
}
};
/**
* Auth.js route
* @link {@see https://authjs.dev/getting-started/installation}
**/
export const authjsHandler = (() => async (request) => {
return Auth(request, authjsConfig);
}) satisfies Get<[], UniversalHandler>;
and here's the error I get:
9:42:34 PM [vike@0.4.201][request(5)][Bug] You stumbled upon a Vike bug. Go to https://github.com/vikejs/vike/issues/new and copy-paste this error. A maintainer will fix the bug (usually under 24 hours).
at file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/shared/page-configs/serialize/parsePageConfigs.js:115:17
at Array.forEach (<anonymous>)
at parseValueSerialized (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/shared/page-configs/serialize/parsePageConfigs.js:111:38)
at file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/shared/page-configs/serialize/parsePageConfigs.js:71:44
at Array.forEach (<anonymous>)
at parseConfigValuesSerialized_tmp (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/shared/page-configs/serialize/parsePageConfigs.js:54:44)
at parseConfigValuesSerialized (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/shared/page-configs/serialize/parsePageConfigs.js:8:26)
at loadConfigValues (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/shared/page-configs/loadConfigValues.js:11:26)
at loadPageUserFiles (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/runtime/renderPage/loadUserFilesServerSide.js:100:51)
at async Promise.all (index 0)
at loadUserFilesServerSide (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/runtime/renderPage/loadUserFilesServerSide.js:13:55)
at renderPageAlreadyRouted (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js:32:31)
at renderPageNominal (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/runtime/renderPage.js:286:36)
at renderPageAlreadyPrepared (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/runtime/renderPage.js:116:45)
at renderPageAndPrepare (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/runtime/renderPage.js:96:12)
at file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/plugin/shared/getHttpRequestAsyncStore.js:54:35
at Module.renderPage (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/node/runtime/renderPage.js:43:35)
at eval (/Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/server/vike-handler.ts:7:23)
at dispatch (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/hono/dist/compose.js:30:17)
at dispatch (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/hono/dist/compose.js:30:17)
at dispatch (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/hono/dist/compose.js:30:17)
at file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/hono/dist/hono-base.js:195:25
at getRequestListener.overrideGlobalObjects (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/@hono/vite-dev-server/dist/dev-server.js:93:32)
at responseViaResponseObject (file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/@hono/node-server/dist/index.mjs:333:15)
9:42:34 PM [vike][request(5)] HTTP response /login 500
I hope this is fixable, thank you for your time!
After removing the top import React
part (just keeping the useState import) it seems like it works? I'm closing the issue while I'm trying to figure if it really worked
9:42:34 PM [vike@0.4.201][request(5)][Bug] You stumbled upon a Vike bug. Go to https://github.com/vikejs/vike/issues/new and copy-paste this error. A maintainer will fix the bug (usually under 24 hours).
at file:///Volumes/KubikDisk/OpenVoxelStudios/VoxelCloud/node_modules/vike/dist/esm/shared/page-configs/serialize/parsePageConfigs.js:115:17
Fixed.