@achieveai/azuredevops-mcp 1.2.3 → 1.2.4
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.
|
@@ -38,6 +38,9 @@ const identity_1 = require("@azure/identity");
|
|
|
38
38
|
const identity_2 = require("@azure/identity");
|
|
39
39
|
const identity_cache_persistence_1 = require("@azure/identity-cache-persistence");
|
|
40
40
|
const azdev = __importStar(require("azure-devops-node-api"));
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
41
44
|
// Enable persistent token cache (encrypted via DPAPI on Windows, Keychain on macOS, libsecret on Linux)
|
|
42
45
|
try {
|
|
43
46
|
(0, identity_2.useIdentityPlugin)(identity_cache_persistence_1.cachePersistencePlugin);
|
|
@@ -51,6 +54,53 @@ catch {
|
|
|
51
54
|
const AZURE_DEVOPS_SCOPE = "499b84ac-1321-427f-aa17-267ca6975798/.default";
|
|
52
55
|
/** Shared token cache options - enables silent re-auth on subsequent starts. */
|
|
53
56
|
const TOKEN_CACHE_OPTIONS = { enabled: true };
|
|
57
|
+
// --- AuthenticationRecord persistence helpers ---
|
|
58
|
+
/** Derive a filesystem-safe label from the org URL (e.g. "dev.azure.com/myorg" → "dev.azure.com-myorg"). */
|
|
59
|
+
function orgUrlToLabel() {
|
|
60
|
+
const orgUrl = process.env.AZURE_DEVOPS_ORG_URL || "default";
|
|
61
|
+
return orgUrl.replace(/^https?:\/\//, "").replace(/[/\\:*?"<>|]+/g, "-").replace(/-+$/, "");
|
|
62
|
+
}
|
|
63
|
+
function getAuthRecordDir() {
|
|
64
|
+
return path.join(os.homedir(), ".azuredevops-mcp");
|
|
65
|
+
}
|
|
66
|
+
function getAuthRecordPath(label) {
|
|
67
|
+
return path.join(getAuthRecordDir(), `auth-record-${label}.json`);
|
|
68
|
+
}
|
|
69
|
+
function loadAuthRecord(label) {
|
|
70
|
+
try {
|
|
71
|
+
const filePath = getAuthRecordPath(label);
|
|
72
|
+
if (!fs.existsSync(filePath))
|
|
73
|
+
return undefined;
|
|
74
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
75
|
+
return (0, identity_1.deserializeAuthenticationRecord)(raw);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function saveAuthRecord(label, record) {
|
|
82
|
+
try {
|
|
83
|
+
const dir = getAuthRecordDir();
|
|
84
|
+
if (!fs.existsSync(dir)) {
|
|
85
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
fs.writeFileSync(getAuthRecordPath(label), (0, identity_1.serializeAuthenticationRecord)(record), "utf-8");
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.error(`[Auth] Failed to save auth record for "${label}":`, err);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function deleteAuthRecord(label) {
|
|
94
|
+
try {
|
|
95
|
+
const filePath = getAuthRecordPath(label);
|
|
96
|
+
if (fs.existsSync(filePath)) {
|
|
97
|
+
fs.unlinkSync(filePath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// ignore
|
|
102
|
+
}
|
|
103
|
+
}
|
|
54
104
|
/**
|
|
55
105
|
* Generic auth handler that wraps any @azure/identity TokenCredential.
|
|
56
106
|
* Supports automatic token refresh and re-authentication on 401.
|
|
@@ -60,18 +110,14 @@ class TokenCredentialAuthHandler {
|
|
|
60
110
|
this.credential = credential;
|
|
61
111
|
}
|
|
62
112
|
/**
|
|
63
|
-
* Create handler using
|
|
64
|
-
* Uses InteractiveBrowserCredential with persistent token cache.
|
|
113
|
+
* Create handler using InteractiveBrowserCredential with persistent token cache.
|
|
65
114
|
* First run opens a browser for login; subsequent runs use the cached token silently.
|
|
66
115
|
*/
|
|
67
116
|
static async createEntra() {
|
|
68
|
-
|
|
117
|
+
return TokenCredentialAuthHandler.createWithAuthRecord(orgUrlToLabel(), {
|
|
69
118
|
redirectUri: "http://localhost",
|
|
70
119
|
tokenCachePersistenceOptions: TOKEN_CACHE_OPTIONS,
|
|
71
120
|
});
|
|
72
|
-
const handler = new TokenCredentialAuthHandler(credential);
|
|
73
|
-
await handler.ensureToken();
|
|
74
|
-
return handler;
|
|
75
121
|
}
|
|
76
122
|
/**
|
|
77
123
|
* Create handler from AzureCliCredential.
|
|
@@ -87,12 +133,49 @@ class TokenCredentialAuthHandler {
|
|
|
87
133
|
* Opens a browser window for user login with token caching and silent re-auth.
|
|
88
134
|
*/
|
|
89
135
|
static async createInteractive(options) {
|
|
90
|
-
|
|
136
|
+
return TokenCredentialAuthHandler.createWithAuthRecord(orgUrlToLabel(), {
|
|
91
137
|
...(options?.tenantId && { tenantId: options.tenantId }),
|
|
92
138
|
...(options?.clientId && { clientId: options.clientId }),
|
|
93
139
|
redirectUri: "http://localhost",
|
|
94
140
|
tokenCachePersistenceOptions: TOKEN_CACHE_OPTIONS,
|
|
95
141
|
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Shared helper: attempts silent auth with a saved AuthenticationRecord,
|
|
145
|
+
* falls back to interactive browser login and persists the new record.
|
|
146
|
+
*/
|
|
147
|
+
static async createWithAuthRecord(label, credentialOptions) {
|
|
148
|
+
// 1. Try loading a previously saved AuthenticationRecord
|
|
149
|
+
const existingRecord = loadAuthRecord(label);
|
|
150
|
+
if (existingRecord) {
|
|
151
|
+
try {
|
|
152
|
+
const credential = new identity_1.InteractiveBrowserCredential({
|
|
153
|
+
...credentialOptions,
|
|
154
|
+
authenticationRecord: existingRecord,
|
|
155
|
+
});
|
|
156
|
+
// Attempt silent token acquisition (no browser)
|
|
157
|
+
const token = await credential.getToken(AZURE_DEVOPS_SCOPE);
|
|
158
|
+
if (token) {
|
|
159
|
+
console.error(`[Auth] Silent authentication succeeded for "${label}".`);
|
|
160
|
+
const handler = new TokenCredentialAuthHandler(credential);
|
|
161
|
+
handler.token = token;
|
|
162
|
+
handler.authHandler = azdev.getHandlerFromToken(token.token);
|
|
163
|
+
return handler;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Silent auth failed — stale record, token revoked, etc.
|
|
168
|
+
console.error(`[Auth] Silent auth failed for "${label}", falling back to interactive login.`);
|
|
169
|
+
deleteAuthRecord(label);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// 2. No valid cached record — do interactive browser login
|
|
173
|
+
const credential = new identity_1.InteractiveBrowserCredential(credentialOptions);
|
|
174
|
+
const authRecord = await credential.authenticate(AZURE_DEVOPS_SCOPE);
|
|
175
|
+
if (authRecord) {
|
|
176
|
+
saveAuthRecord(label, authRecord);
|
|
177
|
+
console.error(`[Auth] Authentication record saved for "${label}".`);
|
|
178
|
+
}
|
|
96
179
|
const handler = new TokenCredentialAuthHandler(credential);
|
|
97
180
|
await handler.ensureToken();
|
|
98
181
|
return handler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntraAuthHandler.js","sourceRoot":"","sources":["../../src/Services/EntraAuthHandler.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"EntraAuthHandler.js","sourceRoot":"","sources":["../../src/Services/EntraAuthHandler.ts"],"names":[],"mappings":"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"}
|