@ronana/mcp 0.3.0 → 0.3.1
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/dist/oauth.js +29 -11
- package/package.json +1 -1
package/dist/oauth.js
CHANGED
|
@@ -26,15 +26,19 @@ export function readCredentials() {
|
|
|
26
26
|
function writeCredentials(value) {
|
|
27
27
|
entry().setPassword(JSON.stringify(value));
|
|
28
28
|
}
|
|
29
|
+
/** Ends a stored sign-in on the server, so it leaves the account's connections.
|
|
30
|
+
* Best effort: an unreachable server still lets the local step go ahead. */
|
|
31
|
+
async function revoke(credentials) {
|
|
32
|
+
await fetch(`${credentials.baseUrl}/api/auth/oauth2/revoke`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
35
|
+
body: new URLSearchParams({ token: credentials.refreshToken, client_id: credentials.clientId }),
|
|
36
|
+
}).catch(() => null);
|
|
37
|
+
}
|
|
29
38
|
export async function logout() {
|
|
30
39
|
const current = readCredentials();
|
|
31
|
-
if (current)
|
|
32
|
-
await
|
|
33
|
-
method: "POST",
|
|
34
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
35
|
-
body: new URLSearchParams({ token: current.refreshToken, client_id: current.clientId }),
|
|
36
|
-
}).catch(() => null);
|
|
37
|
-
}
|
|
40
|
+
if (current)
|
|
41
|
+
await revoke(current);
|
|
38
42
|
try {
|
|
39
43
|
entry().deletePassword();
|
|
40
44
|
}
|
|
@@ -83,11 +87,13 @@ export async function login(baseUrl = process.env.RONANA_BASE_URL ?? "https://ro
|
|
|
83
87
|
const close = () => { clearTimeout(timer); server.close(); };
|
|
84
88
|
const server = createServer((req, res) => {
|
|
85
89
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
// Anything but our own callback (a favicon, a prefetch, a stale tab from an
|
|
91
|
+
// earlier sign-in) is ignored, so it cannot end the sign-in it isn't part of.
|
|
92
|
+
if (url.pathname !== "/callback" || url.searchParams.get("state") !== state) {
|
|
93
|
+
res.writeHead(404).end();
|
|
94
|
+
return;
|
|
89
95
|
}
|
|
90
|
-
|
|
96
|
+
if (url.searchParams.get("error")) {
|
|
91
97
|
res.writeHead(400).end("Authorization was not completed.");
|
|
92
98
|
fail(new Error(url.searchParams.get("error_description") ?? url.searchParams.get("error")));
|
|
93
99
|
}
|
|
@@ -154,6 +160,9 @@ ${authorize}
|
|
|
154
160
|
});
|
|
155
161
|
if (typeof tokens.access_token !== "string" || typeof tokens.refresh_token !== "string")
|
|
156
162
|
throw new Error("OAuth response did not contain tokens.");
|
|
163
|
+
// Signing in again replaces the stored sign-in; end the old one too, or
|
|
164
|
+
// every login leaves another connection behind on the account.
|
|
165
|
+
const previous = readCredentials();
|
|
157
166
|
writeCredentials({
|
|
158
167
|
baseUrl,
|
|
159
168
|
clientId: registered.client_id,
|
|
@@ -161,6 +170,8 @@ ${authorize}
|
|
|
161
170
|
refreshToken: tokens.refresh_token,
|
|
162
171
|
expiresAt: Date.now() + Number(tokens.expires_in ?? 900) * 1000,
|
|
163
172
|
});
|
|
173
|
+
if (previous && previous.clientId !== registered.client_id)
|
|
174
|
+
await revoke(previous);
|
|
164
175
|
}
|
|
165
176
|
finally {
|
|
166
177
|
callback.close();
|
|
@@ -220,6 +231,13 @@ export async function accessToken() {
|
|
|
220
231
|
client_id: current.clientId,
|
|
221
232
|
refresh_token: current.refreshToken,
|
|
222
233
|
resource: `${current.baseUrl}/mcp`,
|
|
234
|
+
}).catch((error) => {
|
|
235
|
+
// A network failure (fetch's TypeError) may pass; a refusal means this
|
|
236
|
+
// sign-in is over (disconnected on the website, or expired), and the
|
|
237
|
+
// server's own words ("session not found") would read as something else.
|
|
238
|
+
if (error instanceof TypeError)
|
|
239
|
+
throw error;
|
|
240
|
+
throw new Error("Your Ronana sign-in has ended (disconnected or expired). Run `ronana auth login` again.");
|
|
223
241
|
});
|
|
224
242
|
if (typeof tokens.access_token !== "string")
|
|
225
243
|
throw new Error("OAuth refresh failed.");
|