Auth0 с электроном, работающим в разработке, но не в упаковке

#node.js #webpack #electron #auth0 #electron-builder

Вопрос:

Я использую Auth0 в приложении electron для управления системой входа в систему. Я ссылался на этот учебник здесь: https://auth0.com/blog/securing-electron-applications-with-openid-connect-and-oauth-2/ чтобы начать использовать его.

Auth0 отлично работал, когда я работал в разработке, но по какой-то причине не работает после того, как я вызываю «пакет пряжи» для создания приложения. В моем приложении electron использовался шаблон electron-react (https://github.com/electron-react-boilerplate/electron-react-boilerplate).

Вот важные файлы:

 // imports
...
import {
  getAuthenticationURL,
  refreshTokens,
  loadTokens,
  logout,
  getLogOutUrl,
  getProfile,
  getResponse,
} from './services/authservice';

export default class AppUpdater {
  constructor() {
    ...
  }
}

let mainWindow: BrowserWindow | null = null;

if (process.env.NODE_ENV === 'production') {
  const sourceMapSupport = require('source-map-support');
  sourceMapSupport.install();
}

if (
  process.env.NODE_ENV === 'development' ||
  process.env.DEBUG_PROD === 'true'
) {
  require('electron-debug')();
}

const installExtensions = async () => {
  ...
};

const createWindow = async () => {
  console.log('now starting the main process');
  if (
    process.env.NODE_ENV === 'development' ||
    process.env.DEBUG_PROD === 'true'
  ) {
    await installExtensions();
  }

  const RESOURCES_PATH = app.isPackaged
    ? path.join(process.resourcesPath, 'assets')
    : path.join(__dirname, '../assets');

  const getAssetPath = (...paths: string[]): string => {
    return path.join(RESOURCES_PATH, ...paths);
  };

  mainWindow = new BrowserWindow({
    show: true,
    width: 1024,
    height: 728,
    titleBarStyle: 'hidden', // add this line
    frame: false,
    //icon: getAssetPath('icon.png'),
    webPreferences: {
      nodeIntegration: true,
    },
  });
  mainWindow.loadURL(`file://${__dirname}/index.html`);
  const devtools = new BrowserWindow();

  mainWindow.webContents.setDevToolsWebContents(devtools.webContents);
  mainWindow.webContents.openDevTools({ mode: 'detach' });

};

/**
 * Add event listeners...
 */

let win = null;

function createAuthWindow() {
  destroyAuthWin();

  win = new BrowserWindow({
    width: 1000,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      enableRemoteModule: false,
    },
  });
  console.log(getAuthenticationURL());
  win.loadURL(getAuthenticationURL());

  const {
    session: { webRequest },
  } = win.webContents;

  const filter = {
    urls: [
      'file:///callback*',
    ],
  };

  webRequest.onBeforeRequest(filter, async ({ url }) => {
    console.log(url);
    await loadTokens(url)
      .then((res) => {
        console.log(res);
      })
      .catch(console.log);
    console.log('from web request');
    createWindow();
    return destroyAuthWin();
  });

  win.on('authenticated', () => {
    console.log('WE HAVE AUTHENTICATED');
    destroyAuthWin();
  });

  win.on('closed', () => {
    win = null;
  });
}

function destroyAuthWin() {
  if (!win) return;
  win.close();
  win = null;
}

// logout logic: removed for simplicity 

ipcMain.on('profileRequest', (event, arg) => {
  //event.reply('profileResponse', getProfile());
  event.returnValue = getProfile();
});

const showWindow = async () => {
  try {
    await refreshTokens();
    return createWindow();
  } catch (err) {
    createAuthWindow();
  }
};


 

это мой файл служб аутентификации:

 
let accessToken = null;
let profile = {};
let refreshToken = null;

export function getAccessToken() {
  return accessToken;
}
export function getProfile() {
  return profile;
}

export function getAuthenticationURL() {
  return (
    'https://'  
    auth0Domain  
    '/authorize?'  
    'scope=openid profile offline_accessamp;'  
    'response_type=codeamp;'  
    'client_id='  
    clientId  
    'amp;'  
    'redirect_uri='  
    redirectUri
  );
}

export async function refreshTokens() {
  const refreshToken = await keytar.getPassword(keytarService, keytarAccount);

  if (refreshToken) {
    const refreshOptions = {
      method: 'POST',
      url: `https://${auth0Domain}/oauth/token`,
      headers: { 'content-type': 'application/json' },
      data: {
        grant_type: 'refresh_token',
        client_id: clientId,
        refresh_token: refreshToken,
      },
    };

    try {
      const response = await axios(refreshOptions);
      res = response;

      accessToken = response.data.access_token;
      profile = jwtDecode(response.data.id_token);
    } catch (error) {
      await logout();

      throw error;
    }
  } else {
    throw new Error('No available refresh token.');
  }
}

export async function loadTokens(callbackURL) {
  console.log('loading tokens:');
  console.log(callbackURL);
  res = callbackURL;

  const urlParts = url.parse(callbackURL, true);
  const query = urlParts.query;

  console.log(query);

  const exchangeOptions = {
    grant_type: 'authorization_code',
    client_id: clientId,
    code: query.code,
    redirect_uri: redirectUri,
  };

  const options = {
    method: 'POST',
    url: `https://${auth0Domain}/oauth/token`,
    headers: {
      'content-type': 'application/json',
    },
    data: JSON.stringify(exchangeOptions),
  };

  try {
    const response = await axios(options);

    console.log('from token:');
    console.log(response);
    res = response;

    accessToken = response.data.access_token;
    profile = jwtDecode(response.data.id_token);
    refreshToken = response.data.refresh_token;

    console.log(getProfile());

    if (refreshToken) {
      await keytar.setPassword(keytarService, keytarAccount, refreshToken);
    }
  } catch (error) {
    await logout();

    throw error;
  }
}
 

I have a file in my components folder called «Auth.jsx» which has a «get profile» methods which interacts with the main process to get the profile

 const getProfile = () => {
  return ipcRenderer.sendSync('profileRequest', true);
};
 

After I package the electron app, the getProfile method always returns null/undefined.

Here are the auth0 logs:
Auth0 Logs
It shows that there is a successful Login and Exchange.

Finally, here’s my webpack file: «webpack.config.main.prod.babel»

 /**
 * Webpack config for production electron main process
 */

import path from 'path';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import TerserPlugin from 'terser-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import baseConfig from './webpack.config.base';
import CheckNodeEnv from '../scripts/CheckNodeEnv';
import DeleteSourceMaps from '../scripts/DeleteSourceMaps';

import dotenv from 'dotenv';

CheckNodeEnv('production');
DeleteSourceMaps();

const devtoolsConfig =
  process.env.DEBUG_PROD === 'true'
    ? {
        devtool: 'source-map',
      }
    : {};

export default merge(baseConfig, {
  ...devtoolsConfig,

  mode: 'production',

  target: 'electron-main',

  entry: './src/main.dev.ts',

  output: {
    path: path.join(__dirname, '../../'),
    filename: './src/main.prod.js',
  },

  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: true,
      }),
    ],
  },

  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode:
        process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled',
      openAnalyzer: process.env.OPEN_ANALYZER === 'true',
    }),

    new webpack.EnvironmentPlugin({
      NODE_ENV: 'production',
      DEBUG_PROD: true,
      START_MINIMIZED: false,
      /*
          other environment variables, including auth0 domain name and clientID
      */
    }),
  ],

  /**
   * Disables webpack processing of __dirname and __filename.
   * If you run the bundle in node.js it falls back to these values of node.js.
   * https://github.com/webpack/webpack/issues/2010
   */
  node: {
    __dirname: false,
    __filename: false,
  },
});

 

Я подозреваю, что проблема может быть связана с webpack, поскольку это только упакованная версия приложения, которая работает неправильно. Я не уверен точно, в чем проблема, есть ли проблема в коде или мне нужно что-то специально изменить в моей панели управления Auth0. Если у вас есть какие-либо предложения или идеи о том, как отлаживать, дайте мне знать!