Auth0 Endpoint "api/auth/me" returns a 404 Error in Next.js App - javascript

I have gone through the following tutorial to get my Next.js App integrated with Auth0.
I am able to log in and log out just fine but when trying to display user information on the page after login, the user object is unable to be returned. I have ensured that there is nothing wrong with the Profile.js page that is rendering the user object or the env.local file with my app's secret keys.
After further inspection I noticed that I get an error in the browser console that reads: Failed to Load Resource ... 404 Not Found: http://localhost:3000/api/auth/me.
This error gives me a gut feeling that there is a discrepancy in the mapping between my next.js app and Auth0 since I have modified the basepath in next.config.js:
module.exports = {
basePath: '/my_path',
webpack: (config) => {
return config
},
env: {
},
publicRuntimeConfig: {
BACKEND_API_URL: process.env.BACKEND_API_URL,
CONSENT_COOKIE_NAME: 'ConsentCookie'
},
}
Is there a way to add my basepath into the endpoint that the user object is being returned from? The end result would look something like: https://localhost:3000/my_path/api/auth/me
I am not 100% certain that this will fix my issue with getting the user object returned properly, so I am open to any other suggestions and willing to add more context surrounding specific files in my app.
Edit:
After bringing this issue up on the Auth0 forums (link), I was pointed towards this link, which is another example Next.js Auth0 sample app, except they have written their frontend with TypeScript (which I am not familiar with). They are manipulating the UserContext object and resetting the ProfileURL, which is what I am after; so what would be the JavaScript equivalent to this?
The same repsonse to the Auth0 forum post I mentioned also included another link to an example function that creates a custom URL for the login. This is very close to what I am after since again, I am trying to create a custom auth URL to retrieve the User object and get rid of the 404 ... /api/auth/me not found error.
Due to my inexperience with JS, my attempts at trying to create a similar function to the example stated previously have failed, so what would this look like?

I am feeling intense bittersweet emotions after finding an insultingly simple solution to this issue.
Found in the readme.md of the NextJS-Auth0 repository...
This small snippet of code fixed all of my issues after hours of searching for a solution -
// _app.js
function App({ Component, pageProps }) {
return (
<UserProvider loginUrl="/foo/api/auth/login" profileUrl="/foo/api/auth/me">
<Component {...pageProps} />
</UserProvider>
);
}
Now to get back to wiping the tears off my desk..

I have been having this issue too. What was happening for my Next app deployed on Vercel is that all the api/auth/* routes were not working in production but everything worked locally.
I'm using the Auth0 Universal Login Experience
// package.json
...
"dependencies": {
"#auth0/nextjs-auth0": "^1.9.2",
}
...
All I had before was the function
// api/auth/[...auth0].ts
import { handleAuth } from "#auth0/nextjs-auth0";
export default handleAuth();
So what I did is create all the paths I'd need in my application in their respective files. I think Next.js was not creating the dynamic files at [...auth0].ts
// api/auth/callback.ts
import { handleCallback } from "#auth0/nextjs-auth0";
import { NextApiRequest, NextApiResponse } from "next";
const callbackHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await handleCallback(req, res);
} catch (error) {
res.status(error.status || 400).end(error.message);
}
};
export default callbackHandler;
// api/auth/login.ts
import { handleLogin } from "#auth0/nextjs-auth0";
import { NextApiRequest, NextApiResponse } from "next";
const loginHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await handleLogin(req, res, {
authorizationParams: {
screen_hint: "login",
},
});
} catch (error) {
res.status(error.status || 400).end(error.message);
}
};
export default loginHandler;
// api/auth/logout.ts
import { handleLogout } from "#auth0/nextjs-auth0";
import { NextApiRequest, NextApiResponse } from "next";
const logoutHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await handleLogout(req, res);
} catch (error) {
res.status(error.status || 400).end(error.message);
}
};
export default logoutHandler;
// api/auth/me.ts
// not api/auth/profile.ts
import { handleProfile } from "#auth0/nextjs-auth0";
import { NextApiRequest, NextApiResponse } from "next";
const profileHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await handleProfile(req, res);
} catch (error) {
res.status(error.status || 400).end(error.message);
}
};
export default profileHandler;
// api/auth/signup.ts
import { handleLogin } from "#auth0/nextjs-auth0";
import { NextApiRequest, NextApiResponse } from "next";
const signupHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await handleLogin(req, res, {
authorizationParams: {
screen_hint: "signup",
},
});
} catch (error) {
res.status(error.status || 400).end(error.message);
}
};
export default signupHandler;

Related

Create T3 App Redirect inside a TRPC middleware if user is not signed

How can I trigger a redirect on the server side if a signed in user has not completed their profile page
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
// redirect to profile page if user has not completed profile
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
});
});
This is not currently possible in the way you are describing to the best of my knowledge.
Here are some alternatives that might be helpful:
In getServerSideProps
this only works if you want to redirect before the initial page load. You could also create a wrapper around gSSP to make this more DRY if you're going to use it on a lot of pages.
import { type GetServerSidePropsContext } from "next";
import { getServerAuthSession } from "../server/auth";
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const session = await getServerAuthSession(ctx);
if (!session) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
return {
props: {},
};
}
export default function AuthedPage() {
return <div>Authed</div>;
}
As part of a query or mutation clientside
this is useful for a query or mutation that is only fired after the page has loaded. Again this is a very simple example and could be DRYed, probably the easiest way would be to extract into a custom hook.
import { useRouter } from "next/router";
import { api } from "../utils/api";
export default function AuthedPage() {
const router = useRouter();
// `authedHello` is the example Create T3 App "hello" procedure
// but as a protectedProcedure, ie throws "UNAUTHORIZED" if no session.
// Replace this with a middleware that throws on whatever condition you need it to.
const authedHello = api.example.protectedHello.useQuery(
{ text: "world" },
{
retry: (_count, err) => {
// `onError` only runs once React Query stops retrying
if (err.data?.code === "UNAUTHORIZED") {
return false;
}
return true;
},
onError: (err) => {
if (err.data?.code === "UNAUTHORIZED") {
void router.push("/");
}
},
}
);
return (
<div>
<h1>Authed Page</h1>
<p>{authedHello.data?.greeting}</p>
</div>
);
}
Using Next.js middleware
This is easy to apply to a bunch of routes using the matcher, but it falls a bit outside of T3 conventions.
// pages/middleware.ts
import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "../server/auth";
import type { NextApiRequest, NextApiResponse } from "next";
export async function middleware(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession(req, res, authOptions);
if (!session?.user) {
return NextResponse.redirect(new URL("/", req.url));
}
}
export const config = {
matcher: ["/protectedPage", "/anotherProtectedPage"],
};
Using require in next-auth's useSession
this is useful if you want to guard a page but can't use getServerSideProps. It doesn't quite solve your specific problem, but might be useful to other people who find this. See: https://next-auth.js.org/getting-started/client#require-session

I am losing user information (session) on refreshing and between tabs while using iron-session and next.js

I am currently working on a project using iron-session and next.js. I don't lose my user on clicking Link tags. But if I refresh user becomes undefined. Cookie is set and doesn't get deleted on refresh. I don't know what is wrong.
Here is my login.ts code:
export default withIronSessionApiRoute(loginRoute, sessionOptions);
async function loginRoute(req: NextApiRequest, res: NextApiResponse) {
try {
const {
data: {tcId, admin, userName},
} = await axios('http://localhost:8080/user/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify(req.body),
});
const user = {tcId: tcId, userName: userName, admin: admin} as User;
req.session.user = user;
await req.session.save();
res.json(user);
} catch (error) {
res.status(500).json({message: (error as Error).message})
}}
Here is my session.ts code:
// this file is a wrapper with defaults to be used in both API routes and `getServerSideProps` functions
import type { IronSessionOptions } from 'iron-session'
import type { User } from '../pages/api/user'
export const sessionOptions: IronSessionOptions = {
password: process.env.SECRET_COOKIE_PASSWORD as string,
cookieName: 'eys-cookie',
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
cookieOptions: {
secure: process.env.NODE_ENV === 'production',
},
};
// This is where we specify the typings of req.session.*
declare module 'iron-session' {
interface IronSessionData {
user?: User
}
}
As I said previously. I don't lose my user. While routing around using Link tags from next.
Refreshing causes to lose my user. Also other tabs don't reach my user.
I can show more code if wanted. But I think problem is here.
I had a similar issue and solved the tab switching by setting:
revalidateOnFocus = false,
for the useSWR function
sample
const fetcher = (url: any) => fetch(url).then((res) => res.json());
const options = {
revalidateOnFocus: false,
};
const {
data: session,
error,
mutate: mutateUser,
} = useSWR<Session>("/api/session", fetcher, options);
you can also explore other options here https://swr.vercel.app/docs/options
Probably you forgot to add SWRconfig on the index or app component:
import { AppProps } from 'next/app'
import { SWRConfig } from 'swr'
import fetchJson from 'lib/fetchJson'
function MyApp({ Component, pageProps }: AppProps) {
return (
<SWRConfig
value={{
fetcher: fetchJson,
onError: (err) => {
console.error(err)
},
}}
>
<Component {...pageProps} />
</SWRConfig>
)
}
export default MyApp

Shopify script tag not rendering

the problem
I created a Shopify node.js app using the Shopify CLI and I want to display a simple bar under the header using a script tag. I used the script tag API to add a script tag
"script_tags": [
{
"id": 174240039086,
"src": "https://xxxxx.ngrok.io/script_tag",
}
]
And I also added a <div id="script-app"></div> into the theme, under the header.
Here is my script_tag.js file, located in /pages/script_tag.js
import ReactDOM from 'react-dom';
class TestScriptTag extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
<p>this is a bar</p>
</div>
);
}
}
ReactDOM.render(<TestScriptTag />, document.getElementById('script-app'));
export default TestScriptTag;
Lastly, here is my server.js (most of it is what came with the CLI):
import "#babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "#shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "#shopify/shopify-api";
import Koa from "koa";
import next from "next";
import Router from "koa-router";
import { flushSync } from "react-dom";
const fs = require('fs');
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8083;
const dev = process.env.NODE_ENV !== "production";
const app = next({
dev,
});
const handle = app.getRequestHandler();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: false,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
// Storing the currently active shops in memory will force them to re-login when your server restarts. You should
// persist this object in your app.
const ACTIVE_SHOPIFY_SHOPS = {};
app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
async afterAuth(ctx) {
console.log("here")
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>
delete ACTIVE_SHOPIFY_SHOPS[shop],
});
if (!response.success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`);
},
})
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.get("/", async (ctx) => {
const shop = ctx.query.shop;
// This shop hasn't been seen yet, go through OAuth to create a session
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
router.get("/script_tag", (ctx) => {
handleRequest(ctx);
});
router.get("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
router.get("(.*)", verifyRequest(), handleRequest); // Everything else must have sessions
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
I am getting the error: document not defined.
What I've tried
I thought this is due to server side rendering, so I thought I could get around it by doing this:
if (typeof window !== "undefined") {
ReactDOM.render(<TestScriptTag />, document.getElementById('script-app'));
}
But still nothing renders and I get this when I inspect the shop page.
I've also tried changing the routing to this:
router.get("/script_tag", (ctx) => {
ctx.type = "module";
ctx.body = fs.createReadStream('./pages/script_tag.js')
});
But then I get an error about the import statement in script_tag.js - SyntaxError: Unexpected identifier '{'. import call expects exactly one argument.
I'm not sure what the proper way is to serve the javascript file I want to inject into the header. I feel like I'm missing something stupid. Please help!!

Redirect in Next.js from uppercase to lowercase URL

I need to redirect visitors from /Contact to /contact.
When I am doing it as described in the docs I get an infinite redirect loop.
This is what I tried:
// next.config.js
async redirects() {
return [
{
source: '/Contact',
destination: '/contact',
permanent: true
}
]
}
With Next.JS >=12 you can use custom middleware.
Create file named middleware.js under root folder and use this code:
import { NextResponse } from 'next/server';
const Middleware = (req) => {
if (req.nextUrl.pathname === req.nextUrl.pathname.toLowerCase())
return NextResponse.next();
return NextResponse.redirect(new URL(req.nextUrl.origin + req.nextUrl.pathname.toLowerCase()));
};
export default Middleware;
The following will redirect all paths that don't exist to a lower case path. If the path is not found, it will show a 404.
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import Error from 'next/error'
export default function ResolveRoute() {
const router = useRouter()
useEffect(() => {
const { pathname } = router;
if (pathname !== pathname.toLowerCase()) {
router.push(pathname.toLowerCase())
}
},[router])
return <Error statusCode={404} />
}
Putting this file name as "[route].js" in the root of your pages folder will act as a catch all (unless the page exists). This will redirect to lower case. If already ready lower case then it means the page is a 404.
Following this suggestion from Reddit https://www.reddit.com/r/nextjs/comments/hk2qmk/nextjs_routing_case_sensitivity_issue/fwt29n2?utm_source=share&utm_medium=web2x&context=3
I fixed it using the _error.js page component. Like this:
import { hasUpperCase } from '../lib/string';
...
Error.getInitialProps = ({ asPath, err, res }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
if (asPath && hasUpperCase(asPath)) {
res.writeHead(307, { Location: asPath.toLowerCase() });
res.end();
}
return { statusCode };
};
export default Error;
I would also prefer doing this with redirects like your example though.
I found this helpful link that cover many solution for this issue:
https://www.youtube.com/watch?v=_vSMITiXAik
Solution 1:
use rewirte function in your next.config.js
return [
{
source: "(c|C)(o|O)(n|N)(t|T)(a|A)(c|C)(t|T)",
destination: "/Contact", // here you need to add the exact page contact or Contact
},
];
},
use the _middleware feature:
inside you pages folder add a _middleware.ts file.
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname === request.nextUrl.pathname.toLocaleLowerCase())
return NextResponse.next();
return NextResponse.redirect(
`${request.nextUrl.origin}${request.nextUrl.pathname.toLocaleLowerCase()}`
);
}
with this solution you can need to rename your page to be lowercase.
use middleware feature with a folder for each page.
you need to keep the _middleware.ts the same and you need to do those steps:
create a folder contact all lowercase.
move your page inside this folder.
add an index.ts that point to your page.
its conent should be something like this:
export { default } from "./contact";
rename all your page with this extension: .page.tsx or page.ts if you use typescript and .page.jsx or page.js if javascript.
in next.config.js you need to add this pageExtensions: ["page.tsx", "page.ts"] if you use typescript and pageExtensions: ["page.jsx", "page.js"] if you use javascript.
you need to do this for all your pages.
Here's a very elegant solution that will also carry over any search/query params:
import { NextResponse } from 'next/server';
const Middleware = (req) => {
if (req.nextUrl.pathname !== req.nextUrl.pathname.toLowerCase()) {
const url = req.nextUrl.clone()
url.pathname = url.pathname.toLowerCase()
return NextResponse.redirect(url)
}
return NextResponse.next();
};
export default Middleware;
When I ran into this problem, I noticed NextJS appends a trailing slash to the paths. When I looked at my network console in the browser, I saw requests with alternating status codes of 307 and 308. Once I added a trailing slash to the destination path, I saw a lot of requests with a status of 307, but still getting errors.
Once I added a trailing slash to both the source and destination paths, the redirect happened as expected:
// next.config.js
async redirects() {
return [
{
source: '/Contact/',
destination: '/contact/',
permanent: true
}
]
}

NuxtJS set Cookie in Middleware

I'm building a nuxtjs app and try to set a cookie from a global middleware. I found this contribution on GitHub which shows a method to do this.
So I implemented my middleware like this
export default function ({ isServer, res, query }) {
if (query.lang) {
if (isServer) {
res.setHeader("Set Cookie", [`lang=${query.lang}`]);
} else {
document.cookie = `lang=${query.lang}`;
}
}
}
My problem is that when I visit my app with ?lang=xxx as a parameter, I'm always running into the else block of my if condition. So I get the error
document is not defined
Has anyone a idea what is wrong with my code. I can't see a difference to the code published on github.
You should use cookie-universal-nuxt.
Add this in your module section in nuxt.config.js:
['cookie-universal-nuxt', { alias: 'cookiz' }],
You can use it directly in the store with nuxtServerInit:
async nuxtServerInit({ commit, state, dispatch },
{ app, store, route, req, res, error, redirect }
) {
app.$cookiz.set('lang', route.query.lang)
})
Or in a middleware:
export default function ({ app, res, query }) {
if (query.lang) {
app.$cookiz.set('lang', query.lang)
}
}
You can using helpers from nuxt by using setCookie and custom middleware
https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/
middlewares/cookies.ts
export default function (req, res, next) {
let cookie = getCookie(req, '_id') || 'random_value'
setCookie(res, '_id', cookie)
// Don't forget to call next at the end if your middleware is not an endpoint
next()
}
Also update your nuxt.config.ts
export default defineNextConfig({
// ...
router: {
middleware: ["cookies"],
}
})
In Nuxt 3 and Nuxt 2 Bridge you can use useCookie
Nuxt provides an SSR-friendly composable to read and write cookies.
const lang = useCookie('lang')
lang.value = ''
export function getCookie(name, stringCookie) {
const matches = stringCookie.match(
new RegExp(
`(?:^|; )${name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1')}=([^;]*)`,
),
);
return matches ? decodeURIComponent(matches[1]) : undefined;
}

Categories

Resources