How can I use auth0 getSession() from nextjs middleware function, or is there some other way to get user particulars via middleware - next.js

I have this code in /pages/api/_middleware.js:
import { getSession } from '#auth0/nextjs-auth0'
export default async function middleware(req, ev) {
const session = await getSession(req)
console.log(session)
return NextResponse.next()
}
Whenever I run an API call that hits this I get this message:
error - node_modules#auth0\nextjs-auth0\dist\index.browser.js?b875 (11:0) # Object.getSession
Error: The getSession method can only be used from the server side

I'm not sure it's possible with the #auth0/nextjs-auth0 lib, but I'm lazily just checking if the appSession cookie is in storage like so:
import type { NextRequest } from 'next/server'
export function middleware(req: NextRequest) {
if (req.nextUrl.pathname === '/' && req.cookies.appSession) {
return Response.redirect('/app')
}
if (req.nextUrl.pathname === '/app' && !req.cookies.appSession) {
return Response.redirect('/')
}
}

you can get the session inside of the middleware like this.
import { NextRequest, NextResponse } from 'next/server';
import { withMiddlewareAuthRequired, getSession } from '#auth0/nextjs-auth0/edge';
export default withMiddlewareAuthRequired(async (req: NextRequest) => {
const res = NextResponse.next();
const user = await getSession(req, res);
if (user) {
// Do what you want...
}
return res;
});
// only work on the '/' path
export const config = {
matcher: '/',
};
Found it here, hope it helps!
https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md

Related

Next.js treating pages/API route as a client component?

I have a simple API route, src/pages/api/me.ts that simply echoes if the user is logged in or not.
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from '../../lib/session'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
let user = getSession();
res.status(200).end((user) ? 'yes' : 'no')
}
The import, ../../lib/session (`src/lib/session.ts):
import { cookies } from 'next/headers';
import jwt from 'jsonwebtoken'
export const getSession = () => {
const nxtCookies = cookies();
if (nxtCookies.has('wp_session')) {
const cookie = nxtCookies.get('wp_session');
// prints on the server
console.log('this is happening on the server')
let sessionData = jwt.verify(cookie.value, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return false;
return user;
});
if (sessionData) return sessionData;
}
return false;
}
When I try to call getSession() from pages/api/me.ts, I get an error:
./src/lib/session.ts
You're importing a component that needs next/headers. That only works
in a Server Component but one of its parents is marked with "use
client", so it's a Client Component.
import { cookies } from 'next/headers'; :
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
One of these is marked as a client entry with "use client":
src\lib\session.ts src\pages\api\me.ts
How is this possible? Both are server-sided code.
I even have a server component that uses getSession() to display user information on the website, and this error is not thrown. I even verified via console.log that within both that component and getSession(), that the console prints to the server console. So I am not sure how this is possible.
Specifically, the issue here seems to be the cookie import from next/headers.
I believe cookies from next/headers is only for use in server components. src/pages/api/me.ts
is on the server but is not a server component.
You can access the cookies from a request through req.cookies, and pass it to your getSession function.
A possible implementation of this would be:
// src/pages/api/me.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { getSession } from '../../lib/session'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const user = getSession(req.cookies);
res.status(200).end((user) ? 'yes' : 'no');
}
// src/lib/session.ts
import jwt from 'jsonwebtoken';
export const getSession = (cookies: Partial<Record<string, string>>) => {
const session = cookies.wp_session;
if (session) {
// prints on the server
console.log('this is happening on the server');
const sessionData = jwt.verify(session, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return false;
return user;
});
return sessionData;
}
}

How to use async custom hook in Next.js middleware?

I have this custom hook:
const useRedirects = async () => {
const response = await fetch('/redirect/list')
const redirects = await response.json()
const checkRedirection = (url) => {
return {
isRedirected: // logic to check redirection
newUrl: // logic to form the new URL
}
}
return {
checkRedirection
}
}
export default useRedirects
And this is my middleware.js code:
import { NextResponse } from 'next/server'
import { useRedirect } from './UseRedirect'
export async function middleware(request) {
if (request.nextUrl.pathname.startsWith('/_next')) {
return
}
const checkRedirection = await useRedirect()
const { isRedirected, newUrl } = checkRedirection(request.nextUrl)
if (isRedirected) {
return newUrl
}
}
The problem is that, I get this error:
TypeError: Cannot read properties of null (reading 'length')
at eval (webpack-internal:///../../next/node_modules/next/dist/client/dev/error-overlay/hot-dev-client.js:262:55)
error - ../next/node_modules/#emotion/cache/dist/emotion-cache.browser.esm.js (461:0) #
error - document is not defined
So, how can I use an async hook inside my next.js middleware?

MSW(Mock Service Worker) in Next js first render not working

I use msw with Next js. But at First render, cannot connect api
this is index.tsx
import { useQuery } from "#tanstack/react-query";
import axios from "axios";
const Home = () => {
const getFruit = async () => {
const { data } = await axios.get("/api");
return data;
};
const { data } = useQuery(["dfa"], getFruit);
console.log("data: ", data);
return <div>Hello world</div>;
};
export default Home;
And i capture log in dev tool
In terminal compiling /_error (client and server).. error is showing.
I write code in mocks/index.ts like
async function initMocks() {
if (typeof window === "undefined") {
const { server } = await import("./server");
server.listen();
} else {
const { worker } = await import("./browser");
worker.start();
}
}
initMocks();
export {};
Also I check this code is running before index.tsx.
I think msw work late then first rendering. Is it right? How can I solve this problem?

Get supabase `user` server side in next.js

I am attempting to get the current logged in supabase user while server side.
I have attempted to use const user = supabase.auth.user(); but I always get a null response.
I have also attempted const user = supabase.auth.getUserByCookie(req) but it also returns null. I think because I am not sending a cookie to the api when calling it from the hook.
I have tried passing the user.id from the hook to the api but the api is not receiving the parameters.
I also attempted this approach but the token is never fetched. It seems to not exist in req.cookies.
let supabase = createClient(supabaseUrl, supabaseKey);
let token = req.cookies['sb:token'];
if (!token) {
return
}
let authRequestResult = await fetch(`${supabaseUrl}/auth/v1/user`, {
headers: {
'Authorization': `Bearer ${token}`,
'APIKey': supabaseKey
}
});
`
Does anyone know how to get the current logged in user in server side code?
If you need to get the user in server-side, you need to set the Auth Cookie in the server using the given Next.js API.
// pages/api/auth.js
import { supabase } from "../path/to/supabaseClient/definition";
export default function handler(req, res) {
if (req.method === "POST") {
supabase.auth.api.setAuthCookie(req, res);
} else {
res.setHeader("Allow", ["POST"]);
res.status(405).json({
message: `Method ${req.method} not allowed`,
});
}
}
This endpoint needs to be called every time the state of the user is changed, i.e. the events SIGNED_IN and SIGNED_OUT
You can set up a useEffect in _app.js or probably in a User Context file.
// _app.js
import "../styles/globals.css";
import { supabase } from '../path/to/supabaseClient/def'
function MyApp({ Component, pageProps }) {
useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
handleAuthChange(event, session)
if (event === 'SIGNED_IN') {
// TODO: Actions to Perform on Sign In
}
if (event === 'SIGNED_OUT') {
// TODO: Actions to Perform on Logout
}
})
checkUser()
return () => {
authListener.unsubscribe()
}
}, [])
return <Component {...pageProps} />;
}
async function handleAuthChange(event, session) {
await fetch('/api/auth', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json' }),
credentials: 'same-origin',
body: JSON.stringify({ event, session }),
})
}
export default MyApp;
You can now handle this user with a state and pass it to the app or whichever way you'd like to.
You can get the user in the server-side in any Next.js Page
// pages/user_route.js
import { supabase } from '../path/to/supabaseClient/def'
export default function UserPage ({ user }) {
return (
<h1>Email: {user.email}</h1>
)
}
export async function getServerSideProps({ req }) {
const { user } = await supabase.auth.api.getUserByCookie(req)
if (!user) {
return { props: {}, redirect: { destination: '/sign-in' } }
}
return { props: { user } }
}
Here's a YouTube Tutorial from Nader Dabit - https://www.youtube.com/watch?v=oXWImFqsQF4
And his GitHub Repository - https://github.com/dabit3/supabase-nextjs-auth
supabase have a library of helpers for managing auth for both client- and server-side auth and fetching in a couple of frameworks including Next.js: https://github.com/supabase/auth-helpers and appears to be the recommended solution for similar problems based on this thread: https://github.com/supabase/supabase/issues/3783
This is how I'm using it in an API handler, but provided you have access to req, you can access the user object this way:
import { supabaseServerClient } from '#supabase/auth-helpers-nextjs';
const { user } = await supabaseServerClient({ req, res }).auth.api.getUser(req.cookies["sb-access-token"]);
Note that you will need to use the helper library supabaseClient and supabaseServerClient on the client and server side respectively for this to work as intended.
I was following a tutorial today and was having a similar issue and the below is how i managed to fix it.
I've got this package installed github.com/jshttp/cookie which is why i'm calling cookie.parse.
Supabase Instance:
`//../../../utils/supabase`
import { createClient } from "#supabase/supabase-js";
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_KEY
);
In my case this was my API page:
import { supabase } from "../../../utils/supabase";
import cookie from "cookie";
import initStripe from "stripe";
const handler = async (req, res) => {
const { user } = await supabase.auth.api.getUserByCookie(req);
if (!user) {
return res.status(401).send("Unathorized");
}
const token = cookie.parse(req.headers.cookie)["sb-access-token"];
supabase.auth.session = () => ({
access_token: token,
});`
const {
data: { stripe_customer },
} = await supabase
.from("profile")
.select("stripe_customer")
.eq("id", user.id)
.single();
For anyone who tries to figure out how to get the user server side with the new #supabase/auth-helpers-nextjs, Michele gave the answer.
Just a note: If you're trying to get the user on nextJs's Middleware, instead of:
... req.cookies["sb-access-token"]
You have to use: req.cookies.get('sb-access-token')
For example:
import { supabaseServerClient } from '#supabase/auth-helpers-nextjs';
const { user } = await supabaseServerClient({ req, res }).auth.api.getUser(req.cookies.get('sb-access-token'))
UPDATE: 2023. Available now on Supabase Docs here
import { createServerSupabaseClient } from '#supabase/auth-helpers-nextjs'
export default function Profile({ user }) {
return <div>Hello {user.name}</div>
}
export const getServerSideProps = async (ctx) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return {
redirect: {
destination: '/',
permanent: false,
},
}
return {
props: {
initialSession: session,
user: session.user,
},
}
}

Correct way of reusing functions in Composition API

I use Vue3 Composition API and am trying to explore its reusability possibilities. But I feel that I don't understand how it should be used.
For example, I extracted the login function to a file, to use it on login, and also after registration.
#/services/authorization:
import { useRoute, useRouter } from "vue-router";
import { useStore } from "#/store";
import { notify } from "#/services/notify";
const router = useRouter(); // undefined
const route = useRoute(); // undefined
const store = useStore(); // good, but there is no provide/inject here.
export async function login(credentials: Credentials) {
store
.dispatch("login", credentials)
.then(_result => {
const redirectUrl =
(route.query.redirect as string | undefined) || "users";
router.push(redirectUrl);
})
.catch(error => {
console.error(error);
notify.error(error.response.data.message);
});
}
interface Credentials {
email: string;
password: string;
}
#/views/Login:
import { defineComponent, reactive } from "vue";
import { useI18n } from "#/i18n";
import { login } from "#/services/authorization";
export default defineComponent({
setup() {
const i18n = useI18n();
const credentials = reactive({
email: null,
password: null
});
return { credentials, login, i18n };
}
});
And the problem is that route and router are both undefined, because they use provide/inject, which can be called only during setup() method. I understand why this is happening, but I don't get what is correct way to do this.
Currently, I use this workaround #/services/authorization:
let router;
let route;
export function init() {
if (!router) router = useRouter();
if (!route) route = useRoute();
}
And in Login (and also Register) component's setup() i call init(). But I feel that it's not how it's supposed to work.

Resources