brightspace-mcp-server 3.8.11 → 3.8.12
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/build/auth/purdue-sso.js +39 -10
- package/package.json +1 -1
package/build/auth/purdue-sso.js
CHANGED
|
@@ -24,6 +24,9 @@ const FIELD_POLL_MS = 250;
|
|
|
24
24
|
const NUMBER_MATCH_SELECTOR = "#idRichContext_DisplaySign";
|
|
25
25
|
const MFA_CODE_SELECTORS = ["#idTxtBx_SAOTCC_OTC", 'input[name="otc"]'];
|
|
26
26
|
const MFA_CODE_SUBMIT_SELECTORS = ["#idSubmit_SAOTCC_Continue", "#idSIButton9"];
|
|
27
|
+
// Entra rejects a wrong code in place: the field stays and this message appears.
|
|
28
|
+
const MFA_CODE_ERROR_SELECTOR = "#idSpan_SAOTCC_Error_OTC";
|
|
29
|
+
const MAX_MFA_CODE_ATTEMPTS = 3;
|
|
27
30
|
/**
|
|
28
31
|
* Entra's "Don't ask again for N days" checkbox: the number-match page's id
|
|
29
32
|
* first, then the verification-code page's, then the label text for a tenant
|
|
@@ -95,8 +98,15 @@ function signInName(username, baseUrl) {
|
|
|
95
98
|
export class PurdueSSOFlow {
|
|
96
99
|
config;
|
|
97
100
|
accountHintSubmitted = false;
|
|
98
|
-
/**
|
|
99
|
-
|
|
101
|
+
/** Authenticator codes asked for during this login. See submitMfaCode. */
|
|
102
|
+
mfaCodeAttempts = 0;
|
|
103
|
+
/**
|
|
104
|
+
* True for exactly one poll after a code is resubmitted in response to a
|
|
105
|
+
* rejection. Entra's rejection span can still be in the DOM on that very
|
|
106
|
+
* next poll - it hasn't re-rendered yet, so what's on screen is still the
|
|
107
|
+
* PREVIOUS attempt's verdict, not this one's. See submitMfaCode.
|
|
108
|
+
*/
|
|
109
|
+
awaitingMfaCodeSettle = false;
|
|
100
110
|
/** Set once per login, the first time Entra's MFA page is handled. */
|
|
101
111
|
rememberMfa;
|
|
102
112
|
duoMfa;
|
|
@@ -446,18 +456,37 @@ export class PurdueSSOFlow {
|
|
|
446
456
|
return false;
|
|
447
457
|
if (this.config.headless === false)
|
|
448
458
|
return false;
|
|
449
|
-
// Ask
|
|
450
|
-
// commonly leaves the field on screen while
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
459
|
+
// Ask again only after Entra's rejection message. This runs on every
|
|
460
|
+
// two-second poll, and Microsoft commonly leaves the field on screen while
|
|
461
|
+
// it validates, so a lingering field alone would give a correct code a
|
|
462
|
+
// second prompt on the next tick. That prompt blocks on stdin, and the
|
|
463
|
+
// deadline is only checked between iterations, so the five-minute budget
|
|
464
|
+
// can never fire while parked there.
|
|
465
|
+
const isRetry = this.mfaCodeAttempts > 0;
|
|
466
|
+
if (isRetry) {
|
|
467
|
+
// The rejection span can still be the stale one from the attempt we
|
|
468
|
+
// just resubmitted in reaction to - Entra hasn't re-rendered yet, so
|
|
469
|
+
// this poll can't tell that span apart from a genuine rejection of the
|
|
470
|
+
// new attempt. Give it exactly one more poll (like cuny-sso.ts requiring
|
|
471
|
+
// the URL to change before trusting its own error state) before trusting
|
|
472
|
+
// the span again.
|
|
473
|
+
if (this.awaitingMfaCodeSettle) {
|
|
474
|
+
this.awaitingMfaCodeSettle = false;
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
if (!await page.locator(MFA_CODE_ERROR_SELECTOR).first().isVisible().catch(() => false))
|
|
478
|
+
return false;
|
|
479
|
+
if (this.mfaCodeAttempts >= MAX_MFA_CODE_ATTEMPTS) {
|
|
480
|
+
throw new BrowserAuthError(`Microsoft rejected ${this.mfaCodeAttempts} authenticator codes. Run \`${AUTH_COMMAND}\` to try again.`, "mfa_code");
|
|
481
|
+
}
|
|
482
|
+
log("WARN", "Microsoft rejected that code. Enter the current one from your authenticator app.");
|
|
483
|
+
}
|
|
456
484
|
if (!this.config.requestMfaCode) {
|
|
457
485
|
throw new UnsupportedAuthenticationError(`This MFA method requires a code. Run \`${AUTH_COMMAND}\` in a terminal to enter it.`);
|
|
458
486
|
}
|
|
459
487
|
await this.rememberMfaDevice(page);
|
|
460
|
-
this.
|
|
488
|
+
this.mfaCodeAttempts += 1;
|
|
489
|
+
this.awaitingMfaCodeSettle = isRetry;
|
|
461
490
|
const code = await this.config.requestMfaCode();
|
|
462
491
|
if (!/^\d{6,8}$/.test(code))
|
|
463
492
|
throw new UnsupportedAuthenticationError("The MFA code must contain 6-8 digits.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brightspace-mcp-server",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.12",
|
|
4
4
|
"mcpName": "io.github.rohanmuppa/brightspace",
|
|
5
5
|
"description": "MCP server for Brightspace (D2L). Check grades, due dates, assignments, announcements, syllabus, rosters and more via Claude, ChatGPT, Cursor, Windsurf, or any MCP client.",
|
|
6
6
|
"type": "module",
|