How to persist next-intl locale during social-provider sign-up?

I can’t figure out how to reliably save the user’s locale during social-provider sign-up (Google, GitHub, etc.). My app uses next-intl, so the locale is always a path param (/en/…, /cs/…). With email+password I simply send locale in the body, but OAuth redirects lose this value. Current attempt Client (Next.js 15 + better-auth 1.3.2):
onClick={async () => {
await authClient.signIn.social({
provider: link.id,
newUserCallbackURL: `/dashboard?locale=${locale}`,
});
}}
onClick={async () => {
await authClient.signIn.social({
provider: link.id,
newUserCallbackURL: `/dashboard?locale=${locale}`,
});
}}
Server after hook:
if (ctx.path.startsWith("/sign-in/social")) {
const locale =
(ctx.query?.state as string)
?.split("&")
.find((p) => p.startsWith("locale="))
?.split("=")[1] || "en";
console.log(locale);
const newSession = ctx.context.session;
console.log(newSession);
if (newSession) {
await userRepository.updateUserLocale(newSession.user.id, locale);
}
}
if (ctx.path.startsWith("/sign-in/social")) {
const locale =
(ctx.query?.state as string)
?.split("&")
.find((p) => p.startsWith("locale="))
?.split("=")[1] || "en";
console.log(locale);
const newSession = ctx.context.session;
console.log(newSession);
if (newSession) {
await userRepository.updateUserLocale(newSession.user.id, locale);
}
}
What I’m missing -How should I forward the locale so the callback handler can read it? -Which exact path (/callback/:provider?) and which field (query.state, query.locale, body?) should I check? -Is there a built-in way to pass arbitrary data (like locale) through the OAuth flow, or must I use cookies / custom state? Happy to refactor the code—just need the canonical pattern. Thanks!
Solution:
Solution: ``` if (ctx.path.startsWith("/callback/")) { //Here just use a /callback/ to catch all OAuth routes const newSession = ctx.context.newSession ?? ctx.context.session; if (newSession) {...
Jump to solution
13 Replies
Vlad
Vlad2mo ago
I think the best solution is to store the user's locale in cookies So you'll be able to get it in any request
ddev
ddevOP2mo ago
I store in a cookies but i am not able to get locale from it. I will show this approach also a bit later, not at home @Vlad i had a function
import { cookies } from "next/headers";
const getLocaleFromCookies = async (): Promise<string> => {
try {
const cookieStore = await cookies();
const localeCookie = cookieStore.get("NEXT_LOCALE");
console.log(cookieStore);
return localeCookie?.value || "en";
} catch (error) {
console.warn("Failed to get locale from cookies:", error);
return "en";
}
};
import { cookies } from "next/headers";
const getLocaleFromCookies = async (): Promise<string> => {
try {
const cookieStore = await cookies();
const localeCookie = cookieStore.get("NEXT_LOCALE");
console.log(cookieStore);
return localeCookie?.value || "en";
} catch (error) {
console.warn("Failed to get locale from cookies:", error);
return "en";
}
};
Which i was using to get cookies inside my after hook
if (ctx.path.startsWith("/sign-in/social")) {
const newSession = ctx.context.newSession;
if (newSession) {
try {
const locale = await getLocaleFromCookies();
await userRepository.updateUserLocale(newSession.user.id, locale);
console.log(
`OAuth user ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set OAuth user locale:", error);
}
}
}
if (ctx.path.startsWith("/sign-in/social")) {
const newSession = ctx.context.newSession;
if (newSession) {
try {
const locale = await getLocaleFromCookies();
await userRepository.updateUserLocale(newSession.user.id, locale);
console.log(
`OAuth user ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set OAuth user locale:", error);
}
}
}
But it have not worked, i was getting a default "en" locale in all cases
Vlad
Vlad2mo ago
You should do const locale = ctx.getCookie("NEXT_LOCALE")
Vlad
Vlad2mo ago
No description
ddev
ddevOP2mo ago
so not to use a custom function but instead try to access cookies from a context? I would try
Vlad
Vlad2mo ago
Cookies(), headers(), etc work only in server actions or route handlers Yeah
ddev
ddevOP2mo ago
@Vlad getting null from newSession
if (ctx.path.startsWith("/sign-in/social")) {
const newSession = ctx.context.newSession;
console.log(newSession);
if (newSession) {
try {
console.log(ctx.getCookie("NEXT_LOCALE"));
const locale = ctx.getCookie("NEXT_LOCALE") || "en";
await userRepository.updateUserLocale(newSession.user.id, locale);
console.log(
`OAuth user ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set OAuth user locale:", error);
}
}
}
if (ctx.path.startsWith("/sign-in/social")) {
const newSession = ctx.context.newSession;
console.log(newSession);
if (newSession) {
try {
console.log(ctx.getCookie("NEXT_LOCALE"));
const locale = ctx.getCookie("NEXT_LOCALE") || "en";
await userRepository.updateUserLocale(newSession.user.id, locale);
console.log(
`OAuth user ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set OAuth user locale:", error);
}
}
}
someone? 😦
Vlad
Vlad2mo ago
Try to log all context and see what's there
ddev
ddevOP2mo ago
in this code i dont even see a logs lol. may it be coz i am not hitting path "/callback/google" or console.log(ctx.context) runs on a client so while i am getting fast redirect i cant see a logs in a console?
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path.startsWith("/sign-up")) {
const newSession = ctx.context.newSession;
if (newSession) {
try {
const locale = ctx.body?.locale || "en";

await userRepository.updateUserLocale(newSession.user.id, locale);

console.log(
`User ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set user locale:", error);
}
}
}

if (ctx.path === "/callback/google") {
console.log(ctx.context);
const locale = (ctx.query?.locale as string) || "en";
const newSession = ctx.context.newSession ?? ctx.context.session;
if (newSession) {
await userRepository.updateUserLocale(newSession.user.id, locale);
}
}
}),
},
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path.startsWith("/sign-up")) {
const newSession = ctx.context.newSession;
if (newSession) {
try {
const locale = ctx.body?.locale || "en";

await userRepository.updateUserLocale(newSession.user.id, locale);

console.log(
`User ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set user locale:", error);
}
}
}

if (ctx.path === "/callback/google") {
console.log(ctx.context);
const locale = (ctx.query?.locale as string) || "en";
const newSession = ctx.context.newSession ?? ctx.context.session;
if (newSession) {
await userRepository.updateUserLocale(newSession.user.id, locale);
}
}
}),
},
Vlad
Vlad2mo ago
First you check if it starts with sign-up, then you check if it equals, that's why it never runs that if Just log the context at the start of the function
ddev
ddevOP2mo ago
thank you, it helped a lot to understand how ctx works. i found out that my path was wrong and now i successfully can update a locale by cookies. thank you man
Vlad
Vlad2mo ago
Пожалуйста)
Solution
ddev
ddev2mo ago
Solution:
if (ctx.path.startsWith("/callback/")) { //Here just use a /callback/ to catch all OAuth routes
const newSession = ctx.context.newSession ?? ctx.context.session;
if (newSession) {
try {
const locale = ctx.getCookie("NEXT_LOCALE") || "en";
await userRepository.updateUserLocale(newSession.user.id, locale);
console.log(
`OAuth user ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set OAuth user locale:", error);
}
}
}
if (ctx.path.startsWith("/callback/")) { //Here just use a /callback/ to catch all OAuth routes
const newSession = ctx.context.newSession ?? ctx.context.session;
if (newSession) {
try {
const locale = ctx.getCookie("NEXT_LOCALE") || "en";
await userRepository.updateUserLocale(newSession.user.id, locale);
console.log(
`OAuth user ${newSession.user.id} created with locale: ${locale}`
);
} catch (error) {
console.error("Failed to set OAuth user locale:", error);
}
}
}

Did you find this page helpful?