brightspace-mcp-server 1.6.2 → 2.0.0
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 +34 -7
- package/build/api/client.d.ts +5 -3
- package/build/api/client.d.ts.map +1 -1
- package/build/api/client.js +71 -79
- package/build/api/client.js.map +1 -1
- package/build/api/errors.d.ts +4 -0
- package/build/api/errors.d.ts.map +1 -1
- package/build/api/errors.js +7 -0
- package/build/api/errors.js.map +1 -1
- package/build/auth/auth-cooldown.d.ts +16 -0
- package/build/auth/auth-cooldown.d.ts.map +1 -0
- package/build/auth/auth-cooldown.js +47 -0
- package/build/auth/auth-cooldown.js.map +1 -0
- package/build/auth/auth-lock.d.ts +7 -0
- package/build/auth/auth-lock.d.ts.map +1 -0
- package/build/auth/auth-lock.js +114 -0
- package/build/auth/auth-lock.js.map +1 -0
- package/build/auth/auth-runner.d.ts +17 -7
- package/build/auth/auth-runner.d.ts.map +1 -1
- package/build/auth/auth-runner.js +122 -26
- package/build/auth/auth-runner.js.map +1 -1
- package/build/auth/browser-auth.d.ts +19 -67
- package/build/auth/browser-auth.d.ts.map +1 -1
- package/build/auth/browser-auth.js +258 -574
- package/build/auth/browser-auth.js.map +1 -1
- package/build/auth/browser-state-store.d.ts +19 -0
- package/build/auth/browser-state-store.d.ts.map +1 -0
- package/build/auth/browser-state-store.js +101 -0
- package/build/auth/browser-state-store.js.map +1 -0
- package/build/auth/credential-store.d.ts +29 -0
- package/build/auth/credential-store.d.ts.map +1 -0
- package/build/auth/credential-store.js +233 -0
- package/build/auth/credential-store.js.map +1 -0
- package/build/auth/encrypted-store.d.ts +25 -0
- package/build/auth/encrypted-store.d.ts.map +1 -0
- package/build/auth/encrypted-store.js +61 -0
- package/build/auth/encrypted-store.js.map +1 -0
- package/build/auth/legacy-profile.d.ts +3 -0
- package/build/auth/legacy-profile.d.ts.map +1 -0
- package/build/auth/legacy-profile.js +41 -0
- package/build/auth/legacy-profile.js.map +1 -0
- package/build/auth/legacy-state.d.ts +14 -0
- package/build/auth/legacy-state.d.ts.map +1 -0
- package/build/auth/legacy-state.js +107 -0
- package/build/auth/legacy-state.js.map +1 -0
- package/build/auth/purdue-sso.d.ts +18 -36
- package/build/auth/purdue-sso.d.ts.map +1 -1
- package/build/auth/purdue-sso.js +165 -192
- package/build/auth/purdue-sso.js.map +1 -1
- package/build/auth/session-store.d.ts +16 -38
- package/build/auth/session-store.d.ts.map +1 -1
- package/build/auth/session-store.js +157 -152
- package/build/auth/session-store.js.map +1 -1
- package/build/auth/sso-flow.d.ts +14 -3
- package/build/auth/sso-flow.d.ts.map +1 -1
- package/build/auth/sso-flow.js +16 -1
- package/build/auth/sso-flow.js.map +1 -1
- package/build/auth/suny-sso.d.ts +3 -2
- package/build/auth/suny-sso.d.ts.map +1 -1
- package/build/auth/suny-sso.js +15 -24
- package/build/auth/suny-sso.js.map +1 -1
- package/build/auth/token-manager.d.ts +10 -3
- package/build/auth/token-manager.d.ts.map +1 -1
- package/build/auth/token-manager.js +62 -26
- package/build/auth/token-manager.js.map +1 -1
- package/build/auth/token-mint.d.ts +2 -2
- package/build/auth/token-mint.d.ts.map +1 -1
- package/build/auth/token-mint.js +31 -9
- package/build/auth/token-mint.js.map +1 -1
- package/build/auth-cli.d.ts +1 -7
- package/build/auth-cli.d.ts.map +1 -1
- package/build/auth-cli.js +29 -73
- package/build/auth-cli.js.map +1 -1
- package/build/index.js +8 -4
- package/build/index.js.map +1 -1
- package/build/setup.js +14 -8
- package/build/setup.js.map +1 -1
- package/build/types/index.d.ts +6 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/utils/config-store.d.ts +1 -0
- package/build/utils/config-store.d.ts.map +1 -1
- package/build/utils/config-store.js +3 -0
- package/build/utils/config-store.js.map +1 -1
- package/build/utils/config.d.ts +2 -1
- package/build/utils/config.d.ts.map +1 -1
- package/build/utils/config.js +30 -10
- package/build/utils/config.js.map +1 -1
- package/build/utils/secure-config.d.ts +6 -0
- package/build/utils/secure-config.d.ts.map +1 -0
- package/build/utils/secure-config.js +57 -0
- package/build/utils/secure-config.js.map +1 -0
- package/package.json +4 -2
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Licensed under MIT — see LICENSE file for details.
|
|
5
5
|
*/
|
|
6
6
|
import type { TokenData } from "../types/index.js";
|
|
7
|
+
import { SessionStore } from "./session-store.js";
|
|
7
8
|
import { mintAccessToken } from "./token-mint.js";
|
|
8
9
|
export interface TokenManagerOptions {
|
|
9
10
|
sessionDir?: string;
|
|
@@ -13,6 +14,8 @@ export interface TokenManagerOptions {
|
|
|
13
14
|
tokenTtl?: number;
|
|
14
15
|
/** Injection seam for tests. */
|
|
15
16
|
mint?: typeof mintAccessToken;
|
|
17
|
+
/** Persistence injection keeps unit tests independent of native credentials. */
|
|
18
|
+
sessionStore?: Pick<SessionStore, "load" | "save" | "clear" | "saveIfCurrent" | "clearIfCurrent">;
|
|
16
19
|
}
|
|
17
20
|
/**
|
|
18
21
|
* TokenManager manages token lifecycle with in-memory caching and disk persistence.
|
|
@@ -26,6 +29,7 @@ export declare class TokenManager {
|
|
|
26
29
|
private readonly mint;
|
|
27
30
|
/** Single in-flight mint, so concurrent callers share one request. */
|
|
28
31
|
private mintInFlight;
|
|
32
|
+
private rejectedAccessToken;
|
|
29
33
|
constructor(sessionDir?: string);
|
|
30
34
|
constructor(options: TokenManagerOptions);
|
|
31
35
|
/**
|
|
@@ -33,10 +37,9 @@ export declare class TokenManager {
|
|
|
33
37
|
* Checks memory cache first, then loads from disk if needed.
|
|
34
38
|
* Returns null if token is expired or within refresh buffer.
|
|
35
39
|
*/
|
|
36
|
-
getToken(): Promise<TokenData | null>;
|
|
40
|
+
getToken(rejectedAccessToken?: string): Promise<TokenData | null>;
|
|
37
41
|
/**
|
|
38
|
-
*
|
|
39
|
-
* or null when neither can be used to mint.
|
|
42
|
+
* Only use persisted session material, including updates from other processes.
|
|
40
43
|
*/
|
|
41
44
|
private pickMintable;
|
|
42
45
|
/**
|
|
@@ -53,6 +56,10 @@ export declare class TokenManager {
|
|
|
53
56
|
* Clear the token from memory and disk.
|
|
54
57
|
*/
|
|
55
58
|
clearToken(): Promise<void>;
|
|
59
|
+
private isUsable;
|
|
60
|
+
private matchesTenant;
|
|
61
|
+
private sameToken;
|
|
62
|
+
private afterConcurrentChange;
|
|
56
63
|
/**
|
|
57
64
|
* Check if a token is valid (not expired and outside refresh buffer).
|
|
58
65
|
* A token is valid if it expires more than REFRESH_BUFFER_MS from now.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAalD,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,eAAe,CAAC;IAC9B,gFAAgF;IAChF,YAAY,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,GAAG,gBAAgB,CAAC,CAAC;CACnG;AAED;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmD;IAChF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyB;IAC9C,sEAAsE;IACtE,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,mBAAmB,CAAuB;gBAEtC,UAAU,CAAC,EAAE,MAAM;gBACnB,OAAO,EAAE,mBAAmB;IAaxC;;;;OAIG;IACG,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA8BvE;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;;OAGG;YACW,eAAe;YAaf,OAAO;IAmDrB;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,SAAS;YAKH,qBAAqB;IAMnC;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO;IAiBlC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;CAIvC"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { SessionStore } from "./session-store.js";
|
|
7
7
|
import { mintAccessToken } from "./token-mint.js";
|
|
8
8
|
import { log } from "../utils/logger.js";
|
|
9
|
+
import { TokenRefreshError } from "../api/errors.js";
|
|
9
10
|
/**
|
|
10
11
|
* Token refresh buffer - tokens within this time of expiry are considered invalid.
|
|
11
12
|
* This prevents using tokens that might expire during a request.
|
|
@@ -25,12 +26,13 @@ export class TokenManager {
|
|
|
25
26
|
mint;
|
|
26
27
|
/** Single in-flight mint, so concurrent callers share one request. */
|
|
27
28
|
mintInFlight = null;
|
|
29
|
+
rejectedAccessToken = null;
|
|
28
30
|
constructor(sessionDirOrOptions) {
|
|
29
31
|
const options = typeof sessionDirOrOptions === "string" || sessionDirOrOptions === undefined
|
|
30
32
|
? { sessionDir: sessionDirOrOptions }
|
|
31
33
|
: sessionDirOrOptions;
|
|
32
|
-
this.sessionStore = new SessionStore(options.sessionDir);
|
|
33
|
-
this.baseUrl = options.baseUrl;
|
|
34
|
+
this.sessionStore = options.sessionStore ?? new SessionStore(options.sessionDir);
|
|
35
|
+
this.baseUrl = options.baseUrl ? new URL(options.baseUrl).origin : undefined;
|
|
34
36
|
this.tokenTtl = options.tokenTtl ?? DEFAULT_TOKEN_TTL_SECONDS;
|
|
35
37
|
this.mint = options.mint ?? mintAccessToken;
|
|
36
38
|
}
|
|
@@ -39,22 +41,26 @@ export class TokenManager {
|
|
|
39
41
|
* Checks memory cache first, then loads from disk if needed.
|
|
40
42
|
* Returns null if token is expired or within refresh buffer.
|
|
41
43
|
*/
|
|
42
|
-
async getToken() {
|
|
44
|
+
async getToken(rejectedAccessToken) {
|
|
45
|
+
if (rejectedAccessToken)
|
|
46
|
+
this.rejectedAccessToken = rejectedAccessToken;
|
|
43
47
|
// Check memory cache first
|
|
44
|
-
if (this.cachedToken && this.
|
|
48
|
+
if (this.cachedToken && this.isUsable(this.cachedToken)) {
|
|
45
49
|
log("DEBUG", "Returning cached token");
|
|
46
50
|
return this.cachedToken;
|
|
47
51
|
}
|
|
48
52
|
// Try loading from disk
|
|
49
53
|
const storedToken = await this.sessionStore.load();
|
|
50
|
-
if (storedToken && this.
|
|
54
|
+
if (storedToken && this.isUsable(storedToken)) {
|
|
51
55
|
log("DEBUG", "Loaded valid token from session store");
|
|
52
56
|
this.cachedToken = storedToken;
|
|
53
57
|
return storedToken;
|
|
54
58
|
}
|
|
55
59
|
// The token is stale, but if it carried the session material we can trade
|
|
56
60
|
// that for a fresh JWT over plain HTTP instead of relaunching the browser.
|
|
57
|
-
|
|
61
|
+
// Disk may have been refreshed by another MCP process. Never prefer stale
|
|
62
|
+
// cached cookies over the current persisted session.
|
|
63
|
+
const mintable = this.pickMintable(storedToken);
|
|
58
64
|
if (mintable) {
|
|
59
65
|
const minted = await this.mintFromSession(mintable);
|
|
60
66
|
if (minted)
|
|
@@ -64,17 +70,10 @@ export class TokenManager {
|
|
|
64
70
|
return null;
|
|
65
71
|
}
|
|
66
72
|
/**
|
|
67
|
-
*
|
|
68
|
-
* or null when neither can be used to mint.
|
|
73
|
+
* Only use persisted session material, including updates from other processes.
|
|
69
74
|
*/
|
|
70
|
-
pickMintable(
|
|
71
|
-
|
|
72
|
-
return null;
|
|
73
|
-
for (const candidate of candidates) {
|
|
74
|
-
if (candidate?.cookieHeader && candidate.csrfToken)
|
|
75
|
-
return candidate;
|
|
76
|
-
}
|
|
77
|
-
return null;
|
|
75
|
+
pickMintable(candidate) {
|
|
76
|
+
return this.baseUrl && candidate && this.matchesTenant(candidate) && candidate.cookieHeader && candidate.csrfToken ? candidate : null;
|
|
78
77
|
}
|
|
79
78
|
/**
|
|
80
79
|
* Mint a fresh token from the stale one's session material, collapsing
|
|
@@ -93,39 +92,60 @@ export class TokenManager {
|
|
|
93
92
|
}
|
|
94
93
|
async runMint(stale) {
|
|
95
94
|
log("DEBUG", "Trying to mint an access token from the session cookie");
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
let result;
|
|
96
|
+
try {
|
|
97
|
+
result = await this.mint({
|
|
98
|
+
baseUrl: this.baseUrl,
|
|
99
|
+
cookieHeader: stale.cookieHeader,
|
|
100
|
+
csrfToken: stale.csrfToken,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
throw new TokenRefreshError("token service request failed", error instanceof Error ? error : undefined);
|
|
105
|
+
}
|
|
106
|
+
// The auth CLI or another MCP process can finish a login while HTTP minting
|
|
107
|
+
// is in flight. Its newer session wins over either result of this request.
|
|
108
|
+
const current = await this.sessionStore.load();
|
|
109
|
+
if (current && !this.sameToken(current, stale)) {
|
|
110
|
+
this.cachedToken = current;
|
|
111
|
+
if (this.isUsable(current))
|
|
112
|
+
return current;
|
|
113
|
+
throw new TokenRefreshError("saved authentication changed during refresh");
|
|
114
|
+
}
|
|
101
115
|
if (result.ok) {
|
|
102
116
|
const now = Date.now();
|
|
103
117
|
const token = {
|
|
104
118
|
accessToken: result.accessToken,
|
|
119
|
+
tenantOrigin: this.baseUrl,
|
|
105
120
|
capturedAt: now,
|
|
106
121
|
expiresAt: now + this.tokenTtl * 1000,
|
|
107
122
|
source: "browser",
|
|
108
123
|
cookieHeader: stale.cookieHeader,
|
|
109
124
|
csrfToken: stale.csrfToken,
|
|
110
125
|
};
|
|
111
|
-
await this.
|
|
126
|
+
if (!await this.sessionStore.saveIfCurrent(token, stale))
|
|
127
|
+
return this.afterConcurrentChange();
|
|
128
|
+
this.cachedToken = token;
|
|
129
|
+
this.rejectedAccessToken = null;
|
|
112
130
|
log("INFO", "Minted a fresh access token from the session cookie");
|
|
113
131
|
return token;
|
|
114
132
|
}
|
|
115
133
|
if (result.reason === "sessionExpired") {
|
|
116
134
|
log("INFO", "The session cookie has expired, a browser login is needed");
|
|
117
|
-
await this.
|
|
135
|
+
if (!await this.sessionStore.clearIfCurrent(stale))
|
|
136
|
+
return this.afterConcurrentChange();
|
|
137
|
+
this.cachedToken = null;
|
|
118
138
|
return null;
|
|
119
139
|
}
|
|
120
|
-
|
|
121
|
-
return null;
|
|
140
|
+
throw new TokenRefreshError(result.detail ?? "unexpected token service response");
|
|
122
141
|
}
|
|
123
142
|
/**
|
|
124
143
|
* Set a new token, caching in memory and persisting to disk.
|
|
125
144
|
*/
|
|
126
145
|
async setToken(token) {
|
|
127
|
-
this.cachedToken = token;
|
|
128
146
|
await this.sessionStore.save(token);
|
|
147
|
+
this.cachedToken = token;
|
|
148
|
+
this.rejectedAccessToken = null;
|
|
129
149
|
log("DEBUG", "Token cached and persisted");
|
|
130
150
|
}
|
|
131
151
|
/**
|
|
@@ -136,6 +156,22 @@ export class TokenManager {
|
|
|
136
156
|
await this.sessionStore.clear();
|
|
137
157
|
log("DEBUG", "Token cleared from memory and disk");
|
|
138
158
|
}
|
|
159
|
+
isUsable(token) {
|
|
160
|
+
return this.matchesTenant(token) && token.accessToken !== this.rejectedAccessToken && this.isValid(token);
|
|
161
|
+
}
|
|
162
|
+
matchesTenant(token) {
|
|
163
|
+
return this.baseUrl === undefined || token.tenantOrigin === this.baseUrl;
|
|
164
|
+
}
|
|
165
|
+
sameToken(a, b) {
|
|
166
|
+
return a.accessToken === b.accessToken && a.capturedAt === b.capturedAt &&
|
|
167
|
+
a.cookieHeader === b.cookieHeader && a.csrfToken === b.csrfToken && a.tenantOrigin === b.tenantOrigin;
|
|
168
|
+
}
|
|
169
|
+
async afterConcurrentChange() {
|
|
170
|
+
this.cachedToken = await this.sessionStore.load();
|
|
171
|
+
if (this.cachedToken && this.isUsable(this.cachedToken))
|
|
172
|
+
return this.cachedToken;
|
|
173
|
+
throw new TokenRefreshError("saved authentication changed during refresh");
|
|
174
|
+
}
|
|
139
175
|
/**
|
|
140
176
|
* Check if a token is valid (not expired and outside refresh buffer).
|
|
141
177
|
* A token is valid if it expires more than REFRESH_BUFFER_MS from now.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,8 +25,8 @@ export type MintResult = {
|
|
|
25
25
|
* Exchange the D2L session cookies for a fresh Bearer token in one request.
|
|
26
26
|
* This is the cheap alternative to relaunching Chromium when the JWT expires.
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* Only a 401 or the known login redirect proves session expiry. Server errors,
|
|
29
|
+
* permission failures and unknown response formats remain temporary failures.
|
|
30
30
|
*/
|
|
31
31
|
export declare function mintAccessToken({ baseUrl, cookieHeader, csrfToken, fetchImpl, timeoutMs, }: MintOptions): Promise<MintResult>;
|
|
32
32
|
//# sourceMappingURL=token-mint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-mint.d.ts","sourceRoot":"","sources":["../../src/auth/token-mint.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"token-mint.d.ts","sourceRoot":"","sources":["../../src/auth/token-mint.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAmB3E;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,EACpC,OAAO,EACP,YAAY,EACZ,SAAS,EACT,SAAiB,EACjB,SAA8B,GAC/B,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAyDnC"}
|
package/build/auth/token-mint.js
CHANGED
|
@@ -10,7 +10,7 @@ import { log } from "../utils/logger.js";
|
|
|
10
10
|
* carrying a script that redirects to /d2l/login?sessionExpired=1. There is no
|
|
11
11
|
* 401 on this path, so the marker, not the status, is the only honest signal.
|
|
12
12
|
*/
|
|
13
|
-
const EXPIRED_MARKER = "sessionExpired=1";
|
|
13
|
+
const EXPIRED_MARKER = "/d2l/login?sessionExpired=1";
|
|
14
14
|
/** Same identity the API client sends, see buildAuthHeaders in api/client.ts. */
|
|
15
15
|
const USER_AGENT = "BrightspaceMCP/1.0 (Rohan Muppa; github.com/rohanmuppa/brightspace-mcp-server)";
|
|
16
16
|
const DEFAULT_TIMEOUT_MS = 15000;
|
|
@@ -19,17 +19,30 @@ const transport = (detail) => ({
|
|
|
19
19
|
reason: "transport",
|
|
20
20
|
detail,
|
|
21
21
|
});
|
|
22
|
+
function expiredRedirect(location, tokenUrl) {
|
|
23
|
+
if (!location)
|
|
24
|
+
return false;
|
|
25
|
+
try {
|
|
26
|
+
const target = new URL(location, tokenUrl);
|
|
27
|
+
return target.origin === new URL(tokenUrl).origin &&
|
|
28
|
+
target.pathname === "/d2l/login" && target.searchParams.get("sessionExpired") === "1";
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
22
34
|
/**
|
|
23
35
|
* Exchange the D2L session cookies for a fresh Bearer token in one request.
|
|
24
36
|
* This is the cheap alternative to relaunching Chromium when the JWT expires.
|
|
25
37
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
38
|
+
* Only a 401 or the known login redirect proves session expiry. Server errors,
|
|
39
|
+
* permission failures and unknown response formats remain temporary failures.
|
|
28
40
|
*/
|
|
29
41
|
export async function mintAccessToken({ baseUrl, cookieHeader, csrfToken, fetchImpl = fetch, timeoutMs = DEFAULT_TIMEOUT_MS, }) {
|
|
30
42
|
const url = `${baseUrl.replace(/\/+$/, "")}/d2l/lp/auth/oauth2/token`;
|
|
31
43
|
let status;
|
|
32
44
|
let body;
|
|
45
|
+
let location;
|
|
33
46
|
try {
|
|
34
47
|
const response = await fetchImpl(url, {
|
|
35
48
|
method: "POST",
|
|
@@ -41,28 +54,37 @@ export async function mintAccessToken({ baseUrl, cookieHeader, csrfToken, fetchI
|
|
|
41
54
|
},
|
|
42
55
|
body: "scope=*:*:*",
|
|
43
56
|
signal: AbortSignal.timeout(timeoutMs),
|
|
57
|
+
redirect: "manual",
|
|
44
58
|
});
|
|
45
59
|
status = response.status;
|
|
46
60
|
body = await response.text();
|
|
61
|
+
location = response.headers?.get("location") ?? null;
|
|
47
62
|
}
|
|
48
63
|
catch (error) {
|
|
49
64
|
const message = error instanceof Error ? error.message : String(error);
|
|
50
65
|
return transport(message);
|
|
51
66
|
}
|
|
52
|
-
if (
|
|
67
|
+
if (status >= 500 || status === 429)
|
|
68
|
+
return transport(`HTTP ${status}`);
|
|
69
|
+
let payload = null;
|
|
70
|
+
try {
|
|
71
|
+
payload = JSON.parse(body);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// The known expired-session response is an HTML login redirect.
|
|
75
|
+
}
|
|
76
|
+
if (status === 401 || (!payload && body.includes(EXPIRED_MARKER)) ||
|
|
77
|
+
expiredRedirect(location, url)) {
|
|
53
78
|
log("DEBUG", "The token mint answered with the session-expired stub");
|
|
54
79
|
return { ok: false, reason: "sessionExpired" };
|
|
55
80
|
}
|
|
56
81
|
if (status < 200 || status >= 300) {
|
|
57
82
|
return transport(`HTTP ${status}`);
|
|
58
83
|
}
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
accessToken = JSON.parse(body).access_token;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
84
|
+
if (!payload) {
|
|
64
85
|
return transport("the token mint returned an unparseable body");
|
|
65
86
|
}
|
|
87
|
+
const accessToken = payload.access_token;
|
|
66
88
|
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
|
67
89
|
return transport("the token mint returned no access_token");
|
|
68
90
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-mint.js","sourceRoot":"","sources":["../../src/auth/token-mint.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,cAAc,GAAG,
|
|
1
|
+
{"version":3,"file":"token-mint.js","sourceRoot":"","sources":["../../src/auth/token-mint.ts"],"names":[],"mappings":"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"}
|
package/build/auth-cli.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Brightspace MCP Server
|
|
4
|
-
* Copyright (c) 2026 Rohan Muppa. All rights reserved.
|
|
5
|
-
* Licensed under MIT — see LICENSE file for details.
|
|
6
|
-
*
|
|
7
|
-
* https://github.com/rohanmuppa/brightspace-mcp-server
|
|
8
|
-
*/
|
|
2
|
+
/** Brightspace MCP Server. Copyright (c) 2026 Rohan Muppa. MIT licensed. */
|
|
9
3
|
export {};
|
|
10
4
|
//# sourceMappingURL=auth-cli.d.ts.map
|
package/build/auth-cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-cli.d.ts","sourceRoot":"","sources":["../src/auth-cli.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"auth-cli.d.ts","sourceRoot":"","sources":["../src/auth-cli.ts"],"names":[],"mappings":";AACA,4EAA4E"}
|
package/build/auth-cli.js
CHANGED
|
@@ -1,93 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Brightspace MCP Server
|
|
4
|
-
* Copyright (c) 2026 Rohan Muppa. All rights reserved.
|
|
5
|
-
* Licensed under MIT — see LICENSE file for details.
|
|
6
|
-
*
|
|
7
|
-
* https://github.com/rohanmuppa/brightspace-mcp-server
|
|
8
|
-
*/
|
|
9
|
-
import * as fs from "node:fs/promises";
|
|
2
|
+
/** Brightspace MCP Server. Copyright (c) 2026 Rohan Muppa. MIT licensed. */
|
|
10
3
|
import { readFileSync } from "node:fs";
|
|
11
4
|
import * as path from "node:path";
|
|
12
5
|
import { fileURLToPath } from "node:url";
|
|
13
6
|
import dotenv from "dotenv";
|
|
14
7
|
import { loadConfig } from "./utils/config.js";
|
|
15
8
|
import { BrowserAuth, TokenManager } from "./auth/index.js";
|
|
16
|
-
|
|
9
|
+
import { NativeCredentialStoreError } from "./auth/credential-store.js";
|
|
10
|
+
import { retireLegacyProfile } from "./auth/legacy-profile.js";
|
|
17
11
|
dotenv.config({ quiet: true });
|
|
18
|
-
const
|
|
19
|
-
try {
|
|
20
|
-
const pkg = JSON.parse(readFileSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8"));
|
|
21
|
-
return pkg.version ?? "unknown";
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return "unknown";
|
|
25
|
-
}
|
|
26
|
-
})();
|
|
12
|
+
const pkg = JSON.parse(readFileSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
|
|
27
13
|
async function main() {
|
|
14
|
+
const automatic = process.argv.includes("--automatic");
|
|
28
15
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
console.log(`\n=== Brightspace Authentication v${pkgVersion} - by Rohan Muppa ===\n`);
|
|
33
|
-
// Check for credentials and provide status
|
|
34
|
-
if (config.username && config.password) {
|
|
35
|
-
console.log(`Authenticating as: ${config.username}`);
|
|
36
|
-
console.log("Approve the sign-in request in Microsoft Authenticator when prompted.");
|
|
37
|
-
console.log("If it shows a number, it is printed below: enter that number in the app.");
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
console.log("No credentials. Opening browser for manual login.");
|
|
41
|
-
}
|
|
42
|
-
console.log("\nStarting authentication...\n");
|
|
43
|
-
// Create BrowserAuth with config
|
|
44
|
-
const browserAuth = new BrowserAuth(config);
|
|
45
|
-
// Authenticate and get token
|
|
46
|
-
const token = await browserAuth.authenticate();
|
|
47
|
-
// Create TokenManager and persist token
|
|
16
|
+
const config = await loadConfig();
|
|
17
|
+
console.error(`\n=== Brightspace Authentication v${pkg.version} ===\n`);
|
|
18
|
+
console.error("Authentication runs headlessly. If Microsoft requests MFA, the number appears here.");
|
|
48
19
|
const tokenManager = new TokenManager({
|
|
49
20
|
sessionDir: config.sessionDir,
|
|
50
21
|
baseUrl: config.baseUrl,
|
|
51
22
|
tokenTtl: config.tokenTtl,
|
|
52
23
|
});
|
|
53
|
-
await
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
await fs.access(sessionFile);
|
|
66
|
-
console.log("Retry succeeded — session.json saved.");
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
console.error("Retry failed. Check directory permissions on", config.sessionDir);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Print success
|
|
74
|
-
console.log("\n=== Authentication successful! ===");
|
|
75
|
-
console.log(`Session saved to ${sessionFile}`);
|
|
76
|
-
console.log("\nThe MCP server will use this token automatically.");
|
|
77
|
-
console.log("You can now add the server to your Claude Desktop configuration.\n");
|
|
78
|
-
process.exit(0);
|
|
24
|
+
await new BrowserAuth(config).authenticate({
|
|
25
|
+
automatic,
|
|
26
|
+
onAuthenticated: async (token) => {
|
|
27
|
+
await tokenManager.setToken(token);
|
|
28
|
+
await retireLegacyProfile(config.sessionDir);
|
|
29
|
+
if (config.legacyBrowserStateMigrated && config.sessionRoot && config.sessionRoot !== config.sessionDir) {
|
|
30
|
+
await retireLegacyProfile(config.sessionRoot);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
console.error("\nAuthentication successful. Your encrypted session is ready for the MCP server.");
|
|
79
35
|
}
|
|
80
36
|
catch (error) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.error("
|
|
88
|
-
console.error("
|
|
89
|
-
|
|
37
|
+
const code = error?.code;
|
|
38
|
+
process.exitCode = error instanceof NativeCredentialStoreError ? 5
|
|
39
|
+
: code === "AUTH_IN_PROGRESS" ? 2
|
|
40
|
+
: code === "AUTH_COOLDOWN" ? 3
|
|
41
|
+
: code === "AUTH_UNSUPPORTED" ? 4
|
|
42
|
+
: code === "AUTH_TRANSPORT" ? 6 : 1;
|
|
43
|
+
console.error("\nAuthentication failed:", error instanceof Error ? error.message : "Unknown authentication error");
|
|
44
|
+
console.error("Run `npx brightspace-mcp-server setup` to update saved credentials.");
|
|
45
|
+
console.error("Run `npx brightspace-mcp-server auth` to retry explicitly. This bypasses the automatic MFA cooldown.");
|
|
90
46
|
}
|
|
91
47
|
}
|
|
92
|
-
main();
|
|
48
|
+
await main();
|
|
93
49
|
//# sourceMappingURL=auth-cli.js.map
|
package/build/auth-cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-cli.js","sourceRoot":"","sources":["../src/auth-cli.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"auth-cli.js","sourceRoot":"","sources":["../src/auth-cli.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -47,14 +47,14 @@ else {
|
|
|
47
47
|
async function main() {
|
|
48
48
|
try {
|
|
49
49
|
// Load configuration
|
|
50
|
-
const config = loadConfig();
|
|
50
|
+
const config = await loadConfig();
|
|
51
51
|
log("DEBUG", "Configuration loaded", { sessionDir: config.sessionDir });
|
|
52
52
|
// Create MCP server instance
|
|
53
53
|
const server = new McpServer({
|
|
54
54
|
name: "brightspace",
|
|
55
55
|
version: PKG_VERSION,
|
|
56
56
|
description: "Brightspace MCP Server — by Rohan Muppa (github.com/rohanmuppa/brightspace-mcp-server)",
|
|
57
|
-
});
|
|
57
|
+
}, { capabilities: { logging: {} } });
|
|
58
58
|
log("INFO", "");
|
|
59
59
|
log("INFO", "========================================");
|
|
60
60
|
log("INFO", ` Brightspace MCP Server v${PKG_VERSION}`);
|
|
@@ -69,7 +69,11 @@ else {
|
|
|
69
69
|
tokenTtl: config.tokenTtl,
|
|
70
70
|
});
|
|
71
71
|
// Create AuthRunner for auto-reauthentication
|
|
72
|
-
const authRunner = new AuthRunner(
|
|
72
|
+
const authRunner = new AuthRunner({
|
|
73
|
+
onProgress: (message) => {
|
|
74
|
+
void server.sendLoggingMessage({ level: "info", logger: "brightspace-auth", data: message }).catch(() => { });
|
|
75
|
+
},
|
|
76
|
+
});
|
|
73
77
|
// Create D2L API Client with auto-reauth support
|
|
74
78
|
const apiClient = new D2LApiClient({
|
|
75
79
|
baseUrl: config.baseUrl,
|
|
@@ -111,7 +115,7 @@ else {
|
|
|
111
115
|
type: "text",
|
|
112
116
|
text: "Not authenticated. Auto-reauthentication was attempted but failed. " +
|
|
113
117
|
"Please run `brightspace-auth` manually in your terminal to log in. " +
|
|
114
|
-
"
|
|
118
|
+
"Run setup to update your saved credentials, and check your internet connection.",
|
|
115
119
|
},
|
|
116
120
|
];
|
|
117
121
|
const notice = getUpdateNotice();
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,oBAAoB,EACpB,0BAA0B,EAC1B,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACxF,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC,CAAC,EAAE,CAAC;AAEL,uEAAuE;AACvE,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;IAC3B,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;AAC7B,CAAC;KAAM,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;IACjC,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;AAChC,CAAC;KAAM,CAAC;IACN,uEAAuE;IAEvE,qFAAqF;IACrF,iBAAiB,EAAE,CAAC;IAEpB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;QAC1C,GAAG,CAAC,OAAO,EAAE,6BAA6B,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,KAAK,UAAU,IAAI;QACjB,IAAI,CAAC;YACH,qBAAqB;YACrB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|