sidebase / nuxt-auth

Authentication built for Nuxt 3! Easily add authentication via OAuth providers, credentials or Email Magic URLs!
https://auth.sidebase.io
MIT License
1.31k stars 164 forks source link

Callback url does not work when I set custom pages #882

Open alimozdemir opened 2 months ago

alimozdemir commented 2 months ago

Environment

Reproduction

It is hard to reproduce this error without sharing the secrets.

Describe the bug

import { decode } from 'jsonwebtoken'
import AzureAdProvider from 'next-auth/providers/azure-ad'
import { NuxtAuthHandler } from '#auth'

async function callRefreshToken(accessToken: any) {
  const url = `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}/oauth2/v2.0/token`;
  const req = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body:
      `grant_type=refresh_token` +
      `&client_secret=${process.env.AZURE_AD_CLIENT_SECRET}` +
      `&refresh_token=${accessToken.refreshToken}` +
      `&client_id=${process.env.AZURE_AD_CLIENT_ID}`,
  });
  const res = await req.json();
  return res;
}

async function refreshAccessToken(accessToken: any) {
  try {
    console.log('Previous token expires at', new Date(accessToken.accessTokenExpires));
    console.log('Refreshing access token...');
    const msToken = await callRefreshToken(accessToken);
    console.log('New token received');
    setAccessToken(accessToken, msToken);
    console.log('Access token refreshed');
    console.log('Next token expires at', new Date(accessToken.accessTokenExpires));
    return accessToken;
  } catch (error) {
    console.error(error);
    return {
      ...accessToken,
      error: 'RefreshAccessTokenError',
    };
  }
}

// Persist the access_token in the encrypted JWT.
function setAccessToken(jwt: any, msToken: any) {
  if (!msToken) {
    return;
  }

  if (msToken.access_token) {
    const decoded = decode(msToken.access_token)
    jwt.accessToken = msToken.access_token;
    if (decoded && typeof decoded !== 'string')
      jwt.roles = decoded.roles;
  }

  if (msToken.expires_at)
    jwt.accessTokenExpires = msToken.expires_at * 1000;
  else if (msToken.expires_in)
    jwt.accessTokenExpires = Date.now() + msToken.expires_in * 1000;

  jwt.refreshToken = msToken.refresh_token;
}

export default NuxtAuthHandler({
  // A secret string you define, to ensure correct encryption
  secret: process.env.AUTH_APP_SECRET,
  pages: {
    signIn: '/auth/signIn',
    signOut: '/auth/signOut',
    error: '/auth/error',
    verifyRequest: '/auth/verifyRequest',
    newUser: '/auth/new-user'
  },
  callbacks: {
    async jwt({ token, account, profile }) {
      setAccessToken(token, account);

      if (token.accessTokenExpires && Date.now() < token.accessTokenExpires) {
        return token;
      }

      return refreshAccessToken(token);
    },
    async session({ session, token }) {
      // Make access token available on the client.
      session.roles = token.roles;

      return session;
    },
  },
  providers: [
    // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point
    AzureAdProvider.default({
      clientId: process.env.AZURE_AD_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
      tenantId: process.env.AZURE_AD_TENANT_ID,
      authorization: {
        params: {
          scope: `openid offline_access profile api://Boss-Dev/Admin`,
        },
      },
    })

  ]
})

nuxt config

  auth: {
    originEnvKey: 'NUXT_AUTH_ORIGIN',
    provider: {
      type: "authjs",
      defaultProvider: "azure-ad"
    },
    globalAppMiddleware: {
      isEnabled: true,
    },
    sessionRefresh: {
        enablePeriodically: 30000,
        enableOnWindowFocus: true,
    }
 },

When I remove custom pages and use the pages from the package, the callback url is working ok and redirects me to the intended page.

I think it might be somehow related to #857

Additional context

When I set custom pages it attaches callbackUrl to the query string but built-in pages do not add callbackUrl to the query string. e.g.

http://localhost:3000/auth/signIn?callbackUrl=http://localhost:3000/my-protected-data

http://localhost:3000/api/auth/signin

It might be related to #883, since that body (URLSearchParams) also contains callbackUrl

Logs

No response

zoey-kaiser commented 1 month ago

Hi @alimozdemir 👋

We have resolved #883 since then. Has this issue been resolved?

alimozdemir commented 1 month ago

Hi @zoey-kaiser unfortunately, it still exist, its just evolved to something else. I shared the error message in the previous issue. I will share more details once I have time this week

alimozdemir commented 1 month ago

Hi @zoey-kaiser I was going to raise another issue for it but it looks like it has already been raised, #922

Still can't use the callback, I will investigate it in my project as well