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 →
MEDIUM AI PREVALENCE: MEDIUM auth.nextauth.session-token-leak

The NextAuth/Auth.js session callback copies an OAuth token onto the session object.

Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.

Why this matters

Whatever the session callback returns is serialized and sent to the browser (and readable by client-side JavaScript), so assigning session.accessToken = token.accessToken exposes a bearer token to every script on the page, including any XSS. Keep access and refresh tokens in the encrypted JWT (the token argument) or a server-side store, and put only non-sensitive fields such as session.user.id or session.user.role on the session.

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

export const authConfig: NextAuthConfig = {
  providers: [],
  callbacks: {
    async session({ session, token }) {
      // ruleid: auth.nextauth.session-token-leak
      session.accessToken = token.accessToken;
      return session;
    },
  },
};

export const { handlers } = NextAuth({
  providers: [],
  callbacks: {
    session: ({ session, token }) => {
      // ruleid: auth.nextauth.session-token-leak
      session.user.refreshToken = token.refresh_token;
      return session;
    },
  },
});
SAFE
safe.ts
import NextAuth from 'next-auth';
import type { NextAuthConfig } from 'next-auth';

export const authConfig: NextAuthConfig = {
  providers: [],
  callbacks: {
    // ok: auth.nextauth.session-token-leak -- only non-sensitive fields on the session
    async session({ session, token }) {
      session.user.id = token.sub as string;
      session.user.role = token.role as string;
      return session;
    },
  },
};

export const { handlers } = NextAuth({
  providers: [],
  callbacks: {
    // ok: auth.nextauth.session-token-leak -- token kept in the JWT, not exposed
    async jwt({ token, account }) {
      if (account) token.accessToken = account.access_token;
      return token;
    },
  },
});

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.session-token-leak -- <reason>

References

https://authjs.dev/guides/extending-the-session ↗https://cwe.mitre.org/data/definitions/522.html ↗