Replies: 17 comments
-
Similar feedback: https://twitter.com/sovereth/status/1622608196824072194 |
Beta Was this translation helpful? Give feedback.
-
I think the best solution here is put a initial screen saying if they want to login as guest,using gmail or using their credentials if the user click guest mode put the default username and password, if not use the credentials provided by the user. I face the same issue today, but i came up with this approach. Its. really hard to tell if you do it in your home screen onload. |
Beta Was this translation helpful? Give feedback.
-
That might work in some cases, unfortunately for a large brand e-commerce site, compared to sites like Best Buy or Amazon that step would be problematic. The goal is to reduce as many steps as possible for sales. Really I just need a method to create the Next-Auth cookie on the server in getInitialProps. There we could check if one exists, validate it, and if it didn't pass or didn't exist we just create one with guest context all prior to the page load. So far we can check exists and validate, but can't create one without using sign-in client side. I haven't looked into this in a few months though so maybe something has changed in the new @auth package |
Beta Was this translation helpful? Give feedback.
-
I agree. A method to create a "guest" session with some default values that mirror whatever data a user session stores would be a huge help. |
Beta Was this translation helpful? Give feedback.
-
I just came across this use case and created a custom JWT via the exported import { encode } from "next-auth/jwt";
import { serialize } from "cookie";
// [...]
// inside an API route
const tokenName =
process.env.NODE_ENV === "production"
? "__Secure-next-auth.session-token"
: "next-auth.session-token";
const myCustomJWTToken = encode({
// just a simplified payload example, this would associate every anonymous visitor with the same dummy user
token: {
email: "",
name: "Unknown User",
picture: "",
sub: "idOfDummyDBUser",
},
secret: process.env.NEXTAUTH_SECRET!
});
res.setHeader(
"Set-Cookie",
serialize(tokenName, myCustomJWTToken, {
httpOnly: true,
path: "/",
sameSite: "lax",
secure: process.env.NODE_ENV === "production",
domain:
process.env.NODE_ENV === "production"
? "example.com"
: "localhost",
})
); Based on my first tests this seems to work — allowing you to set any custom payload in the JWT, including an "anonymous session" with any custom logic. Does that have any limitations that I'm missing? I basically just let the client do a simple fetch for an API route that creates this anonymous/dummy session. |
Beta Was this translation helpful? Give feedback.
-
Hey @maxeth did you find any major downsides to your solution yet? And could you please explain how you make the API call? export async function GET() {
// ... your coding example
return new Response("Hello", {
status: 200,
headers: {
"Set-Cookie": serialize(tokenName, myCustomJWTToken, {
httpOnly: true,
path: "/",
sameSite: "lax",
secure: process.env.NODE_ENV === "production",
domain: process.env.NODE_ENV === "production" ? "example.com" : "localhost",
}),
},
});
} When I first navigate to my page I make a fetch request to my route and get the "Hello" message returned. const session = await getServerSession(options);
if (!session) {
const res = await fetch("http://localhost:3000/api/default").then(res => res.text());
console.log(res);
} Unfortunately this does not set the cookie for me. Thanks a lot for your help :) |
Beta Was this translation helpful? Give feedback.
-
For my use case it works perfectly fine :)
I assume you're using the new |
Beta Was this translation helpful? Give feedback.
-
Cookies cannot be set in server components with Next.js app router. Instead, you can pass the request to a client component and have the client send the request which will set the cookies from the response. If you still want to set the cookies server-side, you can do this through the middleware. |
Beta Was this translation helpful? Give feedback.
-
This is really an important enchantment for us too. Waiting for this one |
Beta Was this translation helpful? Give feedback.
-
Interested |
Beta Was this translation helpful? Give feedback.
-
@maxeth I started with the same approach, but eventually moved over to the two-provider setup described in this issue. export const authOptions: AuthOptions = {
providers: [
CredentialsProvider({
name: "anonymous",
credentials: {},
async authorize(credentials, req) {
return createAnonymousUser();
},
}),
GithubProvider({
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
}),
],
...
}
const handler = NextAuth(authOptions);
export {handler as GET, handler as POST} You can then automatically wrap it inside your NextAuthProvider: export default function NextAuthProvider({...}) {
return (
<SessionProvider>
<AnonymousSessionProvider>
{children}
</AnonymousSessionProvider>
</SessionProvider>
);
} For anyone interested, I've released a complete example in this repo and I wrote a short explanation of the interesting bits in a blog post. |
Beta Was this translation helpful? Give feedback.
-
I use the following code in a server action, to create an anonymous user on the first interaction: import { cookies } from 'next/headers'
import { encode } from 'next-auth/jwt'
import { defaultCookies } from '../../node_modules/next-auth/core/lib/cookie'
async function getOrCreateUserId() {
const session = await getServerSession(authOptions)
if (session) {
return session.user.id
} else {
const id = await insert('users', {})
// Sign in the new user by generating a token manually...
const token = await encode({
token: { sub: id },
secret: env.NEXTAUTH_SECRET,
})
// ...and setting it as a cookie
const secure = process.env.NODE_ENV === 'production'
const { name, options } = defaultCookies(secure).sessionToken
cookies().set(name, token, options)
return id
}
} |
Beta Was this translation helpful? Give feedback.
-
I've implemented a similar solution to the ones described here using credentials provider but I don't think this is the best approach. This forces you into using the If I had to start over, I would consider building a separate solution for guest users that works in parallel to next-auth, i.e. store the guest user in a separate cookie. Then I would create an abstraction for API's like import { getServerSession as getNextAuthServerSession } from "next-auth/next"
const nextAuthConfig = {
// next-auth config
}
export async function getServerSession(req, res) {
const nextAuthSession = await getNextAuthServerSession(req, res, nextAuthConfig);
if (!nextAuthSession) {
return await getGuestUserSession(req, res);
}
return nextAuthSession;
} The abstraction simply allows you to fallback to a guest user if next-auth session doesn't exist. This way you can still use database sessions for authenticated users and makes it less of a headache if you ever decide to move away from next-auth. |
Beta Was this translation helpful? Give feedback.
-
Just to add to this, if you have already mounted the To make the front end pick up on the new signIn('email', { redirect: false }) Providing an actual email is not necessary, and omitting that also prevents in my case (magic link provider) unwanted verification emails being sent. |
Beta Was this translation helpful? Give feedback.
-
Spent some time on this problem as a take-home test, so here is the full solution: https://github.com/jadiaheno/vention-machine-cloud-test |
Beta Was this translation helpful? Give feedback.
-
With upgrade to NextAuth v5 it became easier to do. Here is an example of NextAuth v5 together with next-intl: // middleware.ts
import { NextAuthRequest } from 'next-auth/lib';
import createMiddleware from 'next-intl/middleware';
import { NextResponse } from 'next/server';
import { auth } from './config/auth';
import { i18n } from './config/locales';
import { localePrefix } from './config/navigation';
const intlMiddleware = createMiddleware({
locales: i18n.locales,
defaultLocale: i18n.defaultLocale,
localePrefix,
});
const protectedRoutes = ['orders', 'my-account', 'wishlist'];
export default auth(async function middleware(req: NextAuthRequest) {
const { pathname } = req.nextUrl;
const locale = pathname.split('/')[1];
const callbackUrl = encodeURIComponent(pathname);
const session = req.auth;
// Initialize session for anonymous user
if (!session && !pathname.includes('/guest')) {
return NextResponse.redirect(
new URL(`/${locale}/guest?callbackUrl=${callbackUrl}`, req.nextUrl)
);
}
const isProtectedRoute = protectedRoutes.some((route) =>
pathname.includes(route)
);
// Redirect to sign in page if the user is not authenticated
if (isProtectedRoute && !session?.user.customerData?.email) {
return NextResponse.redirect(
new URL(`/${locale}/sign-in?callbackUrl=${callbackUrl}`, req.nextUrl)
);
}
// We use next-intl. If you don't use that you can just replace with NextResponse.next()
return intlMiddleware(req);
});
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)'],
};
// app/[locale]/guest/route.ts
import { NextRequest } from 'next/server';
import { signIn } from '@/config/auth';
import { Locale } from '@/config/locales';
type Props = {
params: {
locale: Locale;
};
};
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest, { params: { locale } }: Props) {
const searchParams = new URL(req.url).searchParams;
return signIn('anonymous', {
locale,
redirectTo: searchParams.get('callbackUrl') ?? '',
});
}
// config/auth.ts
import NextAuth, { NextAuthConfig } from 'next-auth';
const config = {
providers: [
{
id: 'anonymous',
name: 'Anonymous',
type: 'credentials',
credentials: {},
async authorize(credentials) {
// your auth logic here
},
},
{
name: 'Authenticated',
id: 'authenticated',
type: 'credentials',
credentials: {},
async authorize(credentials) {
// your auth logic here
},
},
],
callbacks: {
async jwt({ token, user }) {
// your jwt logic here
},
async session({ session, token }) {
if (token && session?.user) {
session.user = {
...session.user,
...token,
email: session.user.email,
};
}
return session;
},
},
} satisfies NextAuthConfig;
export const { handlers, auth, signIn, signOut } = NextAuth(config); |
Beta Was this translation helpful? Give feedback.
-
This is how I'm accomplishing creating a user account and signing in using next-auth / authjs v5 in NextJS App router, by setting the session cookie manually: "use server";
import { type RegistrationRequest } from "@/lib/services/api";
import { type JWTAuthResponse } from "@/types/auth";
import { encode } from "next-auth/jwt";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { defaultCookies } from "node_modules/@auth/core/lib/utils/cookie";
const createAndSignIn = async (payload: RegistrationRequest, redirectTo = '/') => {
const response = createUser(payload)
.then(async (result) => {
console.log("createAndSignIn: result", result);
const { token } = result as JWTAuthResponse;
// Sign in the new user by generating a token manually...
const newToken = await encode({
token,
secret: env.NEXTAUTH_SECRET!,
salt:
process.env.NODE_ENV === "production"
? "__Secure-authjs.session-token"
: "authjs.session-token",
});
// ...and setting it as a cookie
const secure = process.env.NODE_ENV === "production";
const { name, options } = defaultCookies(secure).sessionToken;
console.log("createAndSignIn: name", name, options);
cookies().set(name, newToken, options);
return redirect(redirectTo);
})
.catch(async (error) => {
if (error.response) {
const response = await error.response.json();
console.error("Error creating vendor", response);
return response;
}
throw error;
});
return response;
}; I used this for help: #6649 |
Beta Was this translation helpful? Give feedback.
-
Description 📓
In the Headless E-commerce space there is a need to create "Guest" sessions to track user actions in Backend Services. Currently using next-auth and the only way to create a session before a user logs in manually (or reviving a session) is to wait until the client code has loaded.
Now that getSession() server side is stable it would nice to be able to check the status of a session and create a 'Guest' session if the session is empty or unauthenticated.
How to reproduce ☕️
The functionality doesn't exist currently to show an example. Credential methods are only fireable through client side code.
Would like something like below on server side:
Contributing 🙌🏽
No, I am afraid I cannot help regarding this
Beta Was this translation helpful? Give feedback.
All reactions