@msout/microsoft-webauth 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  A tool to authenticate against microsoft online (live or professionnal tenants)
4
4
  We do not rely on GraphAPI.
5
- e.g:
5
+
6
+ e.g:
6
7
  - https://onenote.cloud.microsoft/notebooks
7
8
  - https://outlook.live.com/mail/
8
9
  - and by extension https://login.microsoft.com
@@ -18,6 +19,13 @@ This is a standalone CLI tool for authenticating with Microsoft accounts using P
18
19
  - MFA/2FA support (OTC codes, number matching)
19
20
  - Session persistence
20
21
 
22
+ ## Why this project ?
23
+
24
+ While this let you authenticate this is a part of a bigger purpose,
25
+ primary aim is to offer people a simple way to get out of Microsoft OneNote, because you regardless of what ms documentation states
26
+ => https://learn.microsoft.com/en-us/answers/questions/2276682/onenote-api-fails-with-large-sharepoint-document-l
27
+
28
+ in essence you want to search for microsoft-onenote-list-notebook, microsoft-onenote-exporter
21
29
 
22
30
  ## Available on npmjs
23
31
 
@@ -43,6 +51,12 @@ npm install @msout/microsoft-webauth
43
51
  microsoft-webauth login --email your@email.com --password yourpassword
44
52
  ```
45
53
 
54
+ With custom auth file path:
55
+
56
+ ```bash
57
+ microsoft-webauth login --email your@email.com --password yourpassword --auth-file /path/to/authfile.json
58
+ ```
59
+
46
60
  ### Login (Manual/Interactive)
47
61
 
48
62
  ```bash
@@ -51,18 +65,36 @@ microsoft-webauth login
51
65
 
52
66
  This will open a browser window. Log in manually, then press Enter when you see the notebooks list.
53
67
 
68
+ With custom auth file path:
69
+
70
+ ```bash
71
+ microsoft-webauth login --auth-file /path/to/authfile.json
72
+ ```
73
+
54
74
  ### Check Authentication Status
55
75
 
56
76
  ```bash
57
77
  microsoft-webauth check
58
78
  ```
59
79
 
80
+ With custom auth file path:
81
+
82
+ ```bash
83
+ microsoft-webauth check --auth-file /path/to/authfile.json
84
+ ```
85
+
60
86
  ### Logout
61
87
 
62
88
  ```bash
63
89
  microsoft-webauth logout
64
90
  ```
65
91
 
92
+ With custom auth file path:
93
+
94
+ ```bash
95
+ microsoft-webauth logout --auth-file /path/to/authfile.json
96
+ ```
97
+
66
98
  ## Options
67
99
 
68
100
  | Option | Description |
@@ -71,13 +103,16 @@ microsoft-webauth logout
71
103
  | `--password <password>` | Microsoft account password (for automated login) |
72
104
  | `--notheadless` | Run in visible browser mode (disable headless) |
73
105
  | `--dodump` | Dump HTML content to files for debugging |
106
+ | `--auth-file <path>` | Path to auth file (default: ~/.microsoft-webauth/auth-file.json) |
74
107
 
75
108
  ## Output
76
109
 
77
- Authentication state is saved to `auth.json` in the project directory. A metadata file `auth-meta.json` stores:
110
+ Authentication state is saved to the specified auth file path. By default, it uses `~/.microsoft-webauth/auth-file.json`. A metadata file `{auth-file-prefix}-meta.json` stores:
78
111
  - Email used for login
79
112
  - Login timestamp
80
113
 
114
+ When files already exist, they are automatically backed up with `.old` suffix. If `.old` files already exist, a warning is displayed before overwriting.
115
+
81
116
  ## Testing
82
117
 
83
118
  ```bash
@@ -108,3 +143,4 @@ microsoft-webauth-playwright/
108
143
  ## License
109
144
 
110
145
  ISC — same as MSOneNote Exporter.
146
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@msout/microsoft-webauth",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Microsoft web authentication module, using playwright to automate the login process and retrieve cookies for authenticated sessions.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/auth.js CHANGED
@@ -6,27 +6,94 @@
6
6
  const { chromium } = require('playwright');
7
7
  const fs = require('fs-extra');
8
8
  const logger = require('./utils/logger');
9
- const { AUTH_FILE, ONENOTE_URL, OUTLOOK_URL } = require('./config');
9
+ const { DEFAULT_AUTH_FILE, getAuthMetaFilePath, ensureAuthDir, ONENOTE_URL } = require('./config');
10
10
  const path = require('path');
11
11
  const readline = require('readline');
12
12
 
13
- // Companion metadata file — stores email + login time for display
14
- const AUTH_META_FILE = AUTH_FILE.replace('auth.json', 'auth-meta.json');
13
+ /** Returns the auth file path, defaulting to DEFAULT_AUTH_FILE */
14
+ function getAuthFilePath(authFilePath) {
15
+ return authFilePath || DEFAULT_AUTH_FILE;
16
+ }
15
17
 
16
18
  /** Returns { email, loginTime } from auth-meta.json, or null if not found. */
17
- async function getAuthMeta() {
19
+ async function getAuthMeta(authFilePath) {
20
+ const filePath = getAuthFilePath(authFilePath);
21
+ const metaPath = getAuthMetaFilePath(filePath);
18
22
  try {
19
- if (await fs.pathExists(AUTH_META_FILE)) {
20
- return await fs.readJson(AUTH_META_FILE);
23
+ if (await fs.pathExists(metaPath)) {
24
+ return await fs.readJson(metaPath);
21
25
  }
22
26
  } catch (e) { }
23
27
  return null;
24
28
  }
25
29
 
30
+ /** Generates backup path by appending .old to file */
31
+ function getBackupPath(filePath) {
32
+ return filePath + '.old';
33
+ }
34
+
35
+ /**
36
+ * Checks if files exist and handles backup logic
37
+ * Returns object with { authFileExists, metaFileExists, willOverwriteOld }
38
+ */
39
+ async function checkAndPrepareFiles(authFilePath) {
40
+ const metaFilePath = getAuthMetaFilePath(authFilePath);
41
+
42
+ const authFileExists = await fs.pathExists(authFilePath);
43
+ const metaFileExists = await fs.pathExists(metaFilePath);
44
+
45
+ // Check if .old versions exist
46
+ const authOldPath = getBackupPath(authFilePath);
47
+ const metaOldPath = getBackupPath(metaFilePath);
48
+ const authOldExists = await fs.pathExists(authOldPath);
49
+ const metaOldExists = await fs.pathExists(metaOldPath);
50
+
51
+ let willOverwriteOld = false;
52
+
53
+ // If current files exist, backup them
54
+ if (authFileExists || metaFileExists) {
55
+ // Warn if .old files already exist (they will be erased)
56
+ if (authOldExists || metaOldExists) {
57
+ logger.warn(`Warning: Backup files (.old) already exist and will be erased:`);
58
+ if (authOldExists) logger.warn(` ${authOldPath}`);
59
+ if (metaOldExists) logger.warn(` ${metaOldPath}`);
60
+ willOverwriteOld = true;
61
+ }
62
+
63
+ // Create backup directory if it doesn't exist
64
+ const dir = path.dirname(authFilePath);
65
+ await ensureAuthDir(authFilePath);
66
+
67
+ // Backup existing files
68
+ if (authFileExists) {
69
+ await fs.move(authFilePath, authOldPath, { overwrite: true });
70
+ logger.info(`Backed up ${authFilePath} to ${authOldPath}`);
71
+ }
72
+ if (metaFileExists) {
73
+ await fs.move(metaFilePath, metaOldPath, { overwrite: true });
74
+ logger.info(`Backed up ${metaFilePath} to ${metaOldPath}`);
75
+ }
76
+ } else {
77
+ // Ensure directory exists for new files
78
+ await ensureAuthDir(authFilePath);
79
+ }
80
+
81
+ return {
82
+ authFileExists,
83
+ metaFileExists,
84
+ willOverwriteOld,
85
+ authOldPath,
86
+ metaOldPath
87
+ };
88
+ }
89
+
26
90
  /** Deletes auth.json and auth-meta.json (full logout). */
27
- async function logout() {
28
- await fs.remove(AUTH_FILE);
29
- await fs.remove(AUTH_META_FILE);
91
+ async function logout(authFilePath) {
92
+ const filePath = getAuthFilePath(authFilePath);
93
+ const metaPath = getAuthMetaFilePath(filePath);
94
+
95
+ await fs.remove(filePath);
96
+ await fs.remove(metaPath);
30
97
  }
31
98
 
32
99
  /**
@@ -130,15 +197,22 @@ async function dismissFidoPage(page, logger) {
130
197
  }
131
198
 
132
199
  async function login(credentials = {}) {
133
- const { email, password, targetUrl } = credentials;
200
+ const { email, password, targetUrl, authFile } = credentials;
134
201
  const isAutomated = !!(email && password);
135
202
  const headless = !credentials.notheadless && isAutomated;
136
203
  // Use targetUrl if provided, otherwise default to ONENOTE_URL for backward compatibility
137
204
  const finalTargetUrl = targetUrl || ONENOTE_URL;
138
205
 
206
+ // Get the auth file path (use provided or default)
207
+ const filePath = getAuthFilePath(authFile);
208
+ const metaPath = getAuthMetaFilePath(filePath);
209
+
139
210
  // Added to verify version on user's machine
140
211
  logger.debug('Authentication Module: Version 4.4-DEBUG starting...');
141
212
 
213
+ logger.debug(`Using auth file path: ${filePath}`);
214
+ logger.debug(`Using meta file path: ${metaPath}`);
215
+
142
216
  if (isAutomated) {
143
217
  logger.info(`Attempting automated login for ${email}...`);
144
218
  } else {
@@ -147,6 +221,9 @@ async function login(credentials = {}) {
147
221
  const serviceName = finalTargetUrl.includes('outlook') ? 'Outlook' : 'OneNote';
148
222
  logger.warn(`The script will wait until you successfully reach the ${serviceName} interface.`);
149
223
  }
224
+
225
+ // Prepare files (backup existing if needed, create directory)
226
+ await checkAndPrepareFiles(filePath);
150
227
 
151
228
  const browser = await chromium.launch({ headless: !!headless });
152
229
  const context = await browser.newContext({
@@ -570,14 +647,14 @@ async function login(credentials = {}) {
570
647
  }
571
648
 
572
649
  logger.info('Saving authentication state...');
573
- await context.storageState({ path: AUTH_FILE });
650
+ await context.storageState({ path: filePath });
574
651
 
575
- await fs.writeJson(AUTH_META_FILE, {
652
+ await fs.writeJson(metaPath, {
576
653
  email: email || 'manual login',
577
654
  loginTime: new Date().toISOString()
578
655
  });
579
656
 
580
- logger.success(`Authentication successful! State saved to ${AUTH_FILE}`);
657
+ logger.success(`Authentication successful! State saved to ${filePath}`);
581
658
  } catch (error) {
582
659
  logger.error('Authentication failed or cancelled:', error);
583
660
  if (isAutomated) {
@@ -588,16 +665,19 @@ async function login(credentials = {}) {
588
665
  }
589
666
  }
590
667
 
591
- async function getAuthenticatedContext(browser) {
592
- if (await fs.pathExists(AUTH_FILE)) {
593
- return browser.newContext({ storageState: AUTH_FILE });
668
+ async function getAuthenticatedContext(browser, authFilePath) {
669
+ const filePath = getAuthFilePath(authFilePath);
670
+ if (await fs.pathExists(filePath)) {
671
+ return browser.newContext({ storageState: filePath });
594
672
  } else {
595
673
  throw new Error('No authentication state found. Please run "login" command first.');
596
674
  }
597
675
  }
598
676
 
599
- async function checkAuth(targetUrl = ONENOTE_URL) {
600
- if (!(await fs.pathExists(AUTH_FILE))) {
677
+ async function checkAuth(targetUrl = ONENOTE_URL, authFilePath) {
678
+ const filePath = getAuthFilePath(authFilePath);
679
+
680
+ if (!(await fs.pathExists(filePath))) {
601
681
  return false;
602
682
  }
603
683
 
@@ -605,7 +685,7 @@ async function checkAuth(targetUrl = ONENOTE_URL) {
605
685
  try {
606
686
  logger.debug('Verifying authentication session...');
607
687
  browser = await chromium.launch({ headless: true });
608
- const context = await browser.newContext({ storageState: AUTH_FILE });
688
+ const context = await browser.newContext({ storageState: filePath });
609
689
  const page = await context.newPage();
610
690
 
611
691
  await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 15000 });
@@ -618,7 +698,7 @@ async function checkAuth(targetUrl = ONENOTE_URL) {
618
698
 
619
699
  if (isLoginUrl) {
620
700
  logger.warn('Authentication session has expired. Deleting stale auth state.');
621
- await logout();
701
+ await logout(authFilePath);
622
702
  return false;
623
703
  }
624
704
 
package/src/config.js CHANGED
@@ -4,24 +4,46 @@
4
4
  * @copyright 2026 phptr,enoola,msout
5
5
  */
6
6
  const path = require('path');
7
+ const os = require('os');
8
+ const fs = require('fs-extra');
7
9
 
8
10
  /**
9
- * Returns the directory where auth files (auth.json, auth-meta.json) are stored.
10
- * For this standalone CLI tool, we use the project root.
11
+ * Returns the default auth file path (~/.microsoft-webauth/auth-file.json)
11
12
  */
12
- function getUserDataDir() {
13
- return path.resolve(__dirname, '..');
13
+ function getDefaultAuthFilePath() {
14
+ const homeDir = os.homedir();
15
+ const appDir = path.join(homeDir, '.microsoft-webauth');
16
+ return path.join(appDir, 'auth-file.json');
14
17
  }
15
18
 
16
- const USER_DATA_DIR = getUserDataDir();
19
+ /**
20
+ * Generates the meta file path from auth file path
21
+ * e.g., /path/to/auth-file.json -> /path/to/auth-file-meta.json
22
+ */
23
+ function getAuthMetaFilePath(authFilePath) {
24
+ const dir = path.dirname(authFilePath);
25
+ const name = path.basename(authFilePath);
26
+ // Remove .json extension if present, then add -meta.json
27
+ const baseName = name.replace(/\.json$/, '');
28
+ return path.join(dir, `${baseName}-meta.json`);
29
+ }
30
+
31
+ /**
32
+ * Ensures the directory for the auth file exists, creates it if needed
33
+ */
34
+ async function ensureAuthDir(authFilePath) {
35
+ const dir = path.dirname(authFilePath);
36
+ await fs.ensureDir(dir);
37
+ }
17
38
 
18
- const AUTH_FILE = path.join(USER_DATA_DIR, 'auth.json');
39
+ const DEFAULT_AUTH_FILE = getDefaultAuthFilePath();
19
40
  const ONENOTE_URL = 'https://onenote.cloud.microsoft/en-us';
20
41
  const OUTLOOK_URL = 'https://outlook.cloud.microsoft/mail/';
21
42
 
22
43
  module.exports = {
23
- AUTH_FILE,
44
+ DEFAULT_AUTH_FILE,
45
+ getAuthMetaFilePath,
46
+ ensureAuthDir,
24
47
  ONENOTE_URL,
25
48
  OUTLOOK_URL,
26
- USER_DATA_DIR,
27
49
  };
package/src/index.js CHANGED
@@ -7,7 +7,7 @@
7
7
  const { program } = require('commander');
8
8
  const logger = require('./utils/logger');
9
9
  const { login, checkAuth, getAuthMeta, logout } = require('./auth');
10
- const { ONENOTE_URL, OUTLOOK_URL } = require('./config');
10
+ const { DEFAULT_AUTH_FILE, ONENOTE_URL, OUTLOOK_URL } = require('./config');
11
11
 
12
12
  program
13
13
  .name('webauth')
@@ -22,6 +22,7 @@ program
22
22
  .option('--notheadless', 'Run in visible browser mode for debugging')
23
23
  .option('--dodump', 'Dump HTML content to files for debugging')
24
24
  .option('--against <target>', 'Target service: onenote (default) or outlook', 'onenote')
25
+ .option('--auth-file <path>', 'Path to auth file (default: ~/.microsoft-webauth/auth-file.json)', DEFAULT_AUTH_FILE)
25
26
  .action(async (options) => {
26
27
  const targetUrl = options.against === 'outlook' ? OUTLOOK_URL : ONENOTE_URL;
27
28
  await login({ ...options, targetUrl });
@@ -31,12 +32,13 @@ program
31
32
  .command('check')
32
33
  .description('Check if authenticated')
33
34
  .option('--against <target>', 'Target service: onenote (default) or outlook', 'onenote')
35
+ .option('--auth-file <path>', 'Path to auth file (default: ~/.microsoft-webauth/auth-file.json)', DEFAULT_AUTH_FILE)
34
36
  .action(async (options) => {
35
37
  const targetUrl = options.against === 'outlook' ? OUTLOOK_URL : ONENOTE_URL;
36
- const isAuth = await checkAuth(targetUrl);
38
+ const isAuth = await checkAuth(targetUrl, options.authFile);
37
39
  if (isAuth) {
38
40
  logger.success('Authentication file found. You are authenticated.');
39
- const meta = await getAuthMeta();
41
+ const meta = await getAuthMeta(options.authFile);
40
42
  if (meta && meta.email) {
41
43
  const loginTime = new Date(meta.loginTime).toLocaleString();
42
44
  logger.info(`Logged in as: ${meta.email}`);
@@ -50,8 +52,9 @@ program
50
52
  program
51
53
  .command('logout')
52
54
  .description('Clear authentication state')
53
- .action(async () => {
54
- await logout();
55
+ .option('--auth-file <path>', 'Path to auth file (default: ~/.microsoft-webauth/auth-file.json)', DEFAULT_AUTH_FILE)
56
+ .action(async (options) => {
57
+ await logout(options.authFile);
55
58
  logger.success('Logged out successfully. Authentication state cleared.');
56
59
  });
57
60