@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 +38 -2
- package/package.json +1 -1
- package/src/auth.js +100 -20
- package/src/config.js +30 -8
- package/src/index.js +8 -5
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
|
-
|
|
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
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
14
|
-
|
|
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(
|
|
20
|
-
return await fs.readJson(
|
|
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
|
-
|
|
29
|
-
|
|
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:
|
|
650
|
+
await context.storageState({ path: filePath });
|
|
574
651
|
|
|
575
|
-
await fs.writeJson(
|
|
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 ${
|
|
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
|
-
|
|
593
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
.
|
|
54
|
-
|
|
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
|
|