v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
HIGH AI PREVALENCE: HIGH auth.nextauth.redirect-open

The NextAuth/Auth.js redirect callback returns the incoming url without validating it against baseUrl.

Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.

Why this matters

url is attacker-controllable (it comes from the callbackUrl request parameter), so returning it verbatim turns your sign-in flow into an open redirect: /api/auth/signin?callbackUrl=https://evil.example lands the user on the attacker's site after login. Only return a url you have confirmed is local, for example return url.startsWith(baseUrl) ? url : baseUrl (and resolve relative paths against baseUrl yourself).

VULNERABLE
vulnerable.ts
import NextAuth from 'next-auth';
import type { NextAuthConfig } from 'next-auth';

export const authConfig: NextAuthConfig = {
  providers: [],
  callbacks: {
    // ruleid: auth.nextauth.redirect-open
    async redirect({ url, baseUrl }) {
      return url;
    },
  },
};

export const { handlers } = NextAuth({
  providers: [],
  callbacks: {
    // ruleid: auth.nextauth.redirect-open
    redirect: ({ url, baseUrl }) => url,
  },
});

export const authOptions = {
  providers: [],
  callbacks: {
    // ruleid: auth.nextauth.redirect-open
    redirect: async ({ url }) => {
      return url;
    },
  },
};
SAFE
safe.ts
import NextAuth from 'next-auth';
import type { NextAuthConfig } from 'next-auth';

export const authConfig: NextAuthConfig = {
  providers: [],
  callbacks: {
    // ok: auth.nextauth.redirect-open -- validates the url against baseUrl first
    async redirect({ url, baseUrl }) {
      if (url.startsWith('/')) return `${baseUrl}${url}`;
      if (new URL(url).origin === baseUrl) return url;
      return baseUrl;
    },
  },
};

export const { handlers } = NextAuth({
  providers: [],
  callbacks: {
    // ok: auth.nextauth.redirect-open -- only ever returns a local path
    redirect: ({ url, baseUrl }) => (url.startsWith(baseUrl) ? url : baseUrl),
  },
});

// ok: auth.nextauth.redirect-open -- an unrelated helper, not a NextAuth callback
const router = {
  redirect: ({ url }: { url: string }) => url,
};

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

// oauthlint-disable-next-line auth.nextauth.redirect-open -- <reason>

References

https://authjs.dev/reference/core/types#redirect ↗https://cwe.mitre.org/data/definitions/601.html ↗