Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue: Stack-auth and Localization Redirection Conflict #239

Open
diogoparente opened this issue Sep 11, 2024 · 6 comments
Open

Issue: Stack-auth and Localization Redirection Conflict #239

diogoparente opened this issue Sep 11, 2024 · 6 comments

Comments

@diogoparente
Copy link

diogoparente commented Sep 11, 2024

Hey everyone 👋

I’m using stack-auth successfully in my non-localized app, and the localized version works fine without stack-auth. However, after merging both, I’m encountering an issue: after signing in, I’m redirected back to /handler/sign-in instead of the afterSignInUrl I’ve defined.

Is there any documentation or guidance on handling this scenario? I checked the middleware examples, but they didn’t seem to resolve the issue.

I'm looking to be redirected to the specified afterSignIn but localised.

Thanks!

@diogoparente
Copy link
Author

diogoparente commented Sep 11, 2024

So, I've fixed my use-case with some workarounds and route hacking, but I'm still looking for a better approach, if possible.

Here's how I did it:


import { i18n } from "./i18n.config";

import { match as matchLocale } from "@formatjs/intl-localematcher";
import Negotiator from "negotiator";

import { NextResponse } from "next/server";

function getLocale(request: NextRequest): string | undefined {
  // Negotiator expects plain object so we need to transform headers
  const negotiatorHeaders: Record<string, string> = {};
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));

  // @ts-ignore locales are readonly
  const locales: string[] = i18n.locales;

  // Use negotiator and intl-localematcher to get best locale
  let languages = new Negotiator({ headers: negotiatorHeaders }).languages(
    locales
  );

  const locale = matchLocale(languages, locales, i18n.defaultLocale);

  return locale;
}
export async function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;

  // List of routes that should bypass locale redirection
  const nonLocalizedPaths = [
    "/handler/signin",
    "/handler/sign-in",
    "/handler/signup",
    "/handler/sign-up",
    "/handler/oauth-callback",
    "/handler/account-settings",
  ];

  // Check if the pathname matches any non-localized paths
  if (nonLocalizedPaths.includes(pathname)) {
    return NextResponse.next();
  }

  // Iterate over all locales, check and strip the locale prefix
  const localeMatch = i18n.locales.find((locale) =>
    pathname.startsWith(`/${locale}/`)
  );

  if (localeMatch) {
    const strippedPath = pathname.replace(`/${localeMatch}`, "");

    // Handle specific paths after stripping the locale
    if (
      strippedPath === "/handler/sign-in" ||
      strippedPath === "/handler/sign-up" ||
      strippedPath === "/handler/oauth-callback"
    ) {
      request.nextUrl.pathname = strippedPath.replace("/sign-in", "/signin"); // Normalize path
      return NextResponse.next();
    }
  }

  // Ignore specific files (manifest, favicon, PNGs)
  if (
    ["/manifest.json", "/favicon.ico"].includes(pathname) ||
    /\/.*\.png$/.test(pathname)
  ) {
    return;
  }

  // If pathname is missing a locale, redirect
  const pathnameIsMissingLocale = i18n.locales.every(
    (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  );

  if (pathnameIsMissingLocale) {
    const locale = getLocale(request);
    return NextResponse.redirect(
      new URL(
        `/${locale}${pathname.startsWith("/") ? "" : "/"}${pathname}`,
        request.url
      )
    );
  }

  return NextResponse.next();
}

export const config = {
  // Matcher ignoring `/_next/` and `/api/`
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};

@Chenalejandro
Copy link
Contributor

I don't have any idea on how to solve your issue, but just FYI: you can try https://github.com/amannn/next-intl, it has first class support for next.js and it is being used in big projects like https://nodejs.org. I'm also using it and the afterSignInUrl works flawlessly.

@fomalhautb
Copy link
Contributor

One solution for this is that we can make all the URLs like afterSignIn an optional function so you can decide dynamically where to redirect them. Does that maybe solve your problem?

@diogoparente
Copy link
Author

I don't have any idea on how to solve your issue, but just FYI: you can try https://github.com/amannn/next-intl, it has first class support for next.js and it is being used in big projects like https://nodejs.org. I'm also using it and the afterSignInUrl works flawlessly.

Thanks for the help. I was also using it before, but decided to move on without a library for that purpose.
If this ends up persisting, I might go back to next-intl and check if it solves the problem.

@diogoparente
Copy link
Author

One solution for this is that we can make all the URLs like afterSignIn an optional function so you can decide dynamically where to redirect them. Does that maybe solve your problem?

Hey there. If there is no way to predict a localised url yet, I guess it would help indeed.

It would also be cool do have the same support for the sign-in and sign-up urls so we don't have to run this hardcoded routing handling in middleware.ts.

What do you think?

@fomalhautb
Copy link
Contributor

One solution for this is that we can make all the URLs like afterSignIn an optional function so you can decide dynamically where to redirect them. Does that maybe solve your problem?

Hey there. If there is no way to predict a localised url yet, I guess it would help indeed.

It would also be cool do have the same support for the sign-in and sign-up urls so we don't have to run this hardcoded routing handling in middleware.ts.

What do you think?

That is the idea, I think we are going to rework the redirect process to make it more generalized

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants