@zuwiki/mcp 0.1.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/LICENSE +7 -0
- package/README.md +189 -0
- package/dist/api/categories.d.ts +4 -0
- package/dist/api/categories.js +14 -0
- package/dist/api/categories.js.map +1 -0
- package/dist/api/http.d.ts +8 -0
- package/dist/api/http.js +119 -0
- package/dist/api/http.js.map +1 -0
- package/dist/api/organizations.d.ts +14 -0
- package/dist/api/organizations.js +15 -0
- package/dist/api/organizations.js.map +1 -0
- package/dist/api/pages.d.ts +16 -0
- package/dist/api/pages.js +35 -0
- package/dist/api/pages.js.map +1 -0
- package/dist/api/types.d.ts +69 -0
- package/dist/api/types.js +13 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/wikis.d.ts +12 -0
- package/dist/api/wikis.js +25 -0
- package/dist/api/wikis.js.map +1 -0
- package/dist/auth/client.d.ts +54 -0
- package/dist/auth/client.js +162 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/discovery.d.ts +14 -0
- package/dist/auth/discovery.js +26 -0
- package/dist/auth/discovery.js.map +1 -0
- package/dist/auth/flow.d.ts +21 -0
- package/dist/auth/flow.js +188 -0
- package/dist/auth/flow.js.map +1 -0
- package/dist/auth/pkce.d.ts +7 -0
- package/dist/auth/pkce.js +20 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/registration.d.ts +21 -0
- package/dist/auth/registration.js +28 -0
- package/dist/auth/registration.js.map +1 -0
- package/dist/auth/tokens.d.ts +25 -0
- package/dist/auth/tokens.js +51 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +53 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/register.d.ts +12 -0
- package/dist/tools/register.js +244 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/shared.d.ts +5 -0
- package/dist/tools/shared.js +38 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/transports/http.d.ts +9 -0
- package/dist/transports/http.js +150 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +7 -0
- package/dist/transports/stdio.js +20 -0
- package/dist/transports/stdio.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { discoverAuthorizationServer } from "./discovery.js";
|
|
2
|
+
import { registerClient } from "./registration.js";
|
|
3
|
+
import { performAuthorizationCodeFlow, refreshAccessToken } from "./flow.js";
|
|
4
|
+
import { applyTokenResponse, defaultCredentialsPath, isTokenExpired, loadCredentials, saveCredentials, } from "./tokens.js";
|
|
5
|
+
export const DEFAULT_SCOPE = "openid profile email offline_access wiki:read wiki:write";
|
|
6
|
+
/**
|
|
7
|
+
* Manages OAuth credentials for the local server.
|
|
8
|
+
*
|
|
9
|
+
* On the first call to ensureAccessToken it runs discovery, dynamic client
|
|
10
|
+
* registration and the authorization code flow if needed. Later calls reuse
|
|
11
|
+
* the refresh token as long as possible.
|
|
12
|
+
*/
|
|
13
|
+
export class AuthManager {
|
|
14
|
+
baseUrl;
|
|
15
|
+
credentialsPath;
|
|
16
|
+
scope;
|
|
17
|
+
clientName;
|
|
18
|
+
fetchImpl;
|
|
19
|
+
loopbackPorts;
|
|
20
|
+
log;
|
|
21
|
+
openBrowser;
|
|
22
|
+
interactive;
|
|
23
|
+
credentials = null;
|
|
24
|
+
metadata = null;
|
|
25
|
+
inflight = null;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
28
|
+
this.credentialsPath = options.credentialsPath ?? defaultCredentialsPath();
|
|
29
|
+
this.scope = options.scope ?? DEFAULT_SCOPE;
|
|
30
|
+
this.clientName = options.clientName ?? "Zuwiki MCP Server";
|
|
31
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
32
|
+
this.loopbackPorts = options.loopbackPorts ?? [];
|
|
33
|
+
this.log = options.log ?? ((m) => process.stderr.write(m + "\n"));
|
|
34
|
+
this.openBrowser = options.openBrowser;
|
|
35
|
+
this.interactive = options.interactive ?? true;
|
|
36
|
+
}
|
|
37
|
+
getBaseUrl() {
|
|
38
|
+
return this.baseUrl;
|
|
39
|
+
}
|
|
40
|
+
async ensureAccessToken() {
|
|
41
|
+
if (this.inflight)
|
|
42
|
+
return this.inflight;
|
|
43
|
+
this.inflight = this.ensureAccessTokenInner().finally(() => {
|
|
44
|
+
this.inflight = null;
|
|
45
|
+
});
|
|
46
|
+
return this.inflight;
|
|
47
|
+
}
|
|
48
|
+
async ensureAccessTokenInner() {
|
|
49
|
+
const creds = await this.getCredentials();
|
|
50
|
+
if (creds.accessToken && !isTokenExpired(creds)) {
|
|
51
|
+
return creds.accessToken;
|
|
52
|
+
}
|
|
53
|
+
if (creds.refreshToken) {
|
|
54
|
+
try {
|
|
55
|
+
const metadata = await this.getMetadata();
|
|
56
|
+
const token = await refreshAccessToken({
|
|
57
|
+
tokenEndpoint: metadata.token_endpoint,
|
|
58
|
+
clientId: creds.clientId,
|
|
59
|
+
clientSecret: creds.clientSecret,
|
|
60
|
+
refreshToken: creds.refreshToken,
|
|
61
|
+
fetchImpl: this.fetchImpl,
|
|
62
|
+
});
|
|
63
|
+
const updated = applyTokenResponse(creds, token);
|
|
64
|
+
this.credentials = updated;
|
|
65
|
+
await saveCredentials(this.credentialsPath, updated);
|
|
66
|
+
return updated.accessToken;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
this.log(`Token refresh failed, falling back to a fresh sign in. Reason: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!this.interactive) {
|
|
73
|
+
throw new Error("No valid access token and interactive login is disabled. Run the server once in stdio mode to sign in.");
|
|
74
|
+
}
|
|
75
|
+
return this.runInitialAuth();
|
|
76
|
+
}
|
|
77
|
+
/** Forces a refresh, for example after a 401 response. */
|
|
78
|
+
async invalidateAndRefresh() {
|
|
79
|
+
const creds = await this.getCredentials();
|
|
80
|
+
this.credentials = { ...creds, accessToken: undefined, expiresAt: undefined };
|
|
81
|
+
return this.ensureAccessToken();
|
|
82
|
+
}
|
|
83
|
+
async getCredentials() {
|
|
84
|
+
if (this.credentials)
|
|
85
|
+
return this.credentials;
|
|
86
|
+
const loaded = await loadCredentials(this.credentialsPath);
|
|
87
|
+
if (loaded && loaded.baseUrl === this.baseUrl && loaded.clientId) {
|
|
88
|
+
this.credentials = loaded;
|
|
89
|
+
return loaded;
|
|
90
|
+
}
|
|
91
|
+
if (loaded && loaded.baseUrl !== this.baseUrl) {
|
|
92
|
+
this.log(`Stored credentials are for ${loaded.baseUrl} which does not match the current base URL ${this.baseUrl}. A fresh sign in is required.`);
|
|
93
|
+
}
|
|
94
|
+
const metadata = await this.getMetadata();
|
|
95
|
+
if (!metadata.registration_endpoint) {
|
|
96
|
+
throw new Error("The authorization server does not support dynamic client registration and no credentials are stored.");
|
|
97
|
+
}
|
|
98
|
+
const redirectPlaceholder = "http://127.0.0.1/callback";
|
|
99
|
+
this.log("Registering a new OAuth client with the Zuwiki auth server.");
|
|
100
|
+
const registration = await registerClient(metadata.registration_endpoint, {
|
|
101
|
+
client_name: this.clientName,
|
|
102
|
+
redirect_uris: [redirectPlaceholder],
|
|
103
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
104
|
+
response_types: ["code"],
|
|
105
|
+
token_endpoint_auth_method: "none",
|
|
106
|
+
scope: this.scope,
|
|
107
|
+
application_type: "native",
|
|
108
|
+
}, this.fetchImpl);
|
|
109
|
+
const fresh = {
|
|
110
|
+
baseUrl: this.baseUrl,
|
|
111
|
+
clientId: registration.client_id,
|
|
112
|
+
clientSecret: registration.client_secret,
|
|
113
|
+
registrationAccessToken: registration.registration_access_token,
|
|
114
|
+
registrationClientUri: registration.registration_client_uri,
|
|
115
|
+
};
|
|
116
|
+
this.credentials = fresh;
|
|
117
|
+
await saveCredentials(this.credentialsPath, fresh);
|
|
118
|
+
return fresh;
|
|
119
|
+
}
|
|
120
|
+
async getMetadata() {
|
|
121
|
+
if (this.metadata)
|
|
122
|
+
return this.metadata;
|
|
123
|
+
this.metadata = await discoverAuthorizationServer(this.baseUrl, this.fetchImpl);
|
|
124
|
+
return this.metadata;
|
|
125
|
+
}
|
|
126
|
+
async runInitialAuth() {
|
|
127
|
+
const creds = await this.getCredentials();
|
|
128
|
+
const metadata = await this.getMetadata();
|
|
129
|
+
this.log("Opening the browser for Zuwiki sign in. If no window appears, copy the printed URL.");
|
|
130
|
+
const tokenResponse = await performAuthorizationCodeFlow({
|
|
131
|
+
metadata,
|
|
132
|
+
clientId: creds.clientId,
|
|
133
|
+
clientSecret: creds.clientSecret,
|
|
134
|
+
scope: this.scope,
|
|
135
|
+
preferredPorts: this.loopbackPorts,
|
|
136
|
+
fetchImpl: this.fetchImpl,
|
|
137
|
+
openBrowser: this.openBrowser,
|
|
138
|
+
onAuthorizationUrl: (url) => this.log(`Authorize URL: ${url}`),
|
|
139
|
+
});
|
|
140
|
+
const updated = applyTokenResponse(creds, tokenResponse);
|
|
141
|
+
this.credentials = updated;
|
|
142
|
+
await saveCredentials(this.credentialsPath, updated);
|
|
143
|
+
return updated.accessToken;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Holds the active organization id and lets callers update it at runtime.
|
|
148
|
+
* Shared with the server so that zuwiki_set_active_organization can take effect.
|
|
149
|
+
*/
|
|
150
|
+
export class OrganizationState {
|
|
151
|
+
organizationId;
|
|
152
|
+
constructor(organizationId) {
|
|
153
|
+
this.organizationId = organizationId;
|
|
154
|
+
}
|
|
155
|
+
get() {
|
|
156
|
+
return this.organizationId;
|
|
157
|
+
}
|
|
158
|
+
set(id) {
|
|
159
|
+
this.organizationId = id && id.length > 0 ? id : null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAoC,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,eAAe,GAEhB,MAAM,aAAa,CAAC;AAErB,MAAM,CAAC,MAAM,aAAa,GAAG,0DAA0D,CAAC;AAgBxF;;;;;;GAMG;AACH,MAAM,OAAO,WAAW;IACL,OAAO,CAAS;IAChB,eAAe,CAAS;IACxB,KAAK,CAAS;IACd,UAAU,CAAS;IACnB,SAAS,CAAe;IACxB,aAAa,CAAW;IACxB,GAAG,CAA4B;IAC/B,WAAW,CAAqC;IAChD,WAAW,CAAU;IAE9B,WAAW,GAA6B,IAAI,CAAC;IAC7C,QAAQ,GAAuC,IAAI,CAAC;IACpD,QAAQ,GAA2B,IAAI,CAAC;IAEhD,YAAY,OAA2B;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,sBAAsB,EAAE,CAAC;QAC3E,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;QAC5C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC;QAC5D,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;QAC5C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;IACjD,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,sBAAsB;QAClC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAE1C,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC,WAAW,CAAC;QAC3B,CAAC;QAED,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC1C,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC;oBACrC,aAAa,EAAE,QAAQ,CAAC,cAAc;oBACtC,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC,CAAC;gBACH,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;gBACjD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;gBAC3B,MAAM,eAAe,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;gBACrD,OAAO,OAAO,CAAC,WAAY,CAAC;YAC9B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CACN,kEAAmE,GAAa,CAAC,OAAO,EAAE,CAC3F,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,wGAAwG,CACzG,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED,0DAA0D;IAC1D,KAAK,CAAC,oBAAoB;QACxB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;QAC9E,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAClC,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC3D,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACjE,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;YAC1B,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9C,IAAI,CAAC,GAAG,CACN,8BAA8B,MAAM,CAAC,OAAO,8CAA8C,IAAI,CAAC,OAAO,gCAAgC,CACvI,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;QACJ,CAAC;QACD,MAAM,mBAAmB,GAAG,2BAA2B,CAAC;QACxD,IAAI,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QACxE,MAAM,YAAY,GAAG,MAAM,cAAc,CACvC,QAAQ,CAAC,qBAAqB,EAC9B;YACE,WAAW,EAAE,IAAI,CAAC,UAAU;YAC5B,aAAa,EAAE,CAAC,mBAAmB,CAAC;YACpC,WAAW,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;YACpD,cAAc,EAAE,CAAC,MAAM,CAAC;YACxB,0BAA0B,EAAE,MAAM;YAClC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,gBAAgB,EAAE,QAAQ;SAC3B,EACD,IAAI,CAAC,SAAS,CACf,CAAC;QACF,MAAM,KAAK,GAAsB;YAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE,YAAY,CAAC,SAAS;YAChC,YAAY,EAAE,YAAY,CAAC,aAAa;YACxC,uBAAuB,EAAE,YAAY,CAAC,yBAAyB;YAC/D,qBAAqB,EAAE,YAAY,CAAC,uBAAuB;SAC5D,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,MAAM,eAAe,CAAC,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,KAAK,CAAC,WAAW;QACvB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,MAAM,2BAA2B,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAChF,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,GAAG,CACN,qFAAqF,CACtF,CAAC;QACF,MAAM,aAAa,GAAG,MAAM,4BAA4B,CAAC;YACvD,QAAQ;YACR,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,cAAc,EAAE,IAAI,CAAC,aAAa;YAClC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,GAAG,EAAE,CAAC;SAC/D,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACzD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;QAC3B,MAAM,eAAe,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QACrD,OAAO,OAAO,CAAC,WAAY,CAAC;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IACR;IAApB,YAAoB,cAA6B;QAA7B,mBAAc,GAAd,cAAc,CAAe;IAAG,CAAC;IAErD,GAAG;QACD,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,EAAiB;QACnB,IAAI,CAAC,cAAc,GAAG,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACxD,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AuthorizationServerMetadata {
|
|
2
|
+
issuer: string;
|
|
3
|
+
authorization_endpoint: string;
|
|
4
|
+
token_endpoint: string;
|
|
5
|
+
registration_endpoint?: string;
|
|
6
|
+
jwks_uri?: string;
|
|
7
|
+
scopes_supported?: string[];
|
|
8
|
+
response_types_supported?: string[];
|
|
9
|
+
grant_types_supported?: string[];
|
|
10
|
+
code_challenge_methods_supported?: string[];
|
|
11
|
+
token_endpoint_auth_methods_supported?: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function discoverAuthorizationServer(baseUrl: string, fetchImpl?: typeof fetch): Promise<AuthorizationServerMetadata>;
|
|
14
|
+
export declare function clearDiscoveryCache(): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const cache = new Map();
|
|
2
|
+
const TTL_MS = 5 * 60 * 1000;
|
|
3
|
+
export async function discoverAuthorizationServer(baseUrl, fetchImpl = fetch) {
|
|
4
|
+
const normalized = baseUrl.replace(/\/+$/, "");
|
|
5
|
+
const cached = cache.get(normalized);
|
|
6
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
7
|
+
return cached.value;
|
|
8
|
+
}
|
|
9
|
+
const url = `${normalized}/.well-known/oauth-authorization-server`;
|
|
10
|
+
const response = await fetchImpl(url, {
|
|
11
|
+
headers: { accept: "application/json" },
|
|
12
|
+
});
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`Authorization Server discovery failed (${response.status} ${response.statusText}) at ${url}`);
|
|
15
|
+
}
|
|
16
|
+
const metadata = (await response.json());
|
|
17
|
+
if (!metadata.authorization_endpoint || !metadata.token_endpoint) {
|
|
18
|
+
throw new Error("Discovery document is missing authorization_endpoint or token_endpoint");
|
|
19
|
+
}
|
|
20
|
+
cache.set(normalized, { value: metadata, expiresAt: Date.now() + TTL_MS });
|
|
21
|
+
return metadata;
|
|
22
|
+
}
|
|
23
|
+
export function clearDiscoveryCache() {
|
|
24
|
+
cache.clear();
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/auth/discovery.ts"],"names":[],"mappings":"AAaA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqE,CAAC;AAC3F,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,OAAe,EACf,YAA0B,KAAK;IAE/B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,UAAU,yCAAyC,CAAC;IACnE,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;QACpC,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;KACxC,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,0CAA0C,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,QAAQ,GAAG,EAAE,CAC9F,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAgC,CAAC;IAExE,IAAI,CAAC,QAAQ,CAAC,sBAAsB,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC5F,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AuthorizationServerMetadata } from "./discovery.js";
|
|
2
|
+
import type { TokenResponse } from "./tokens.js";
|
|
3
|
+
export interface AuthorizationCodeFlowOptions {
|
|
4
|
+
metadata: AuthorizationServerMetadata;
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecret?: string;
|
|
7
|
+
scope: string;
|
|
8
|
+
preferredPorts?: number[];
|
|
9
|
+
fetchImpl?: typeof fetch;
|
|
10
|
+
openBrowser?: (url: string) => Promise<unknown>;
|
|
11
|
+
onAuthorizationUrl?: (url: string) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function performAuthorizationCodeFlow(options: AuthorizationCodeFlowOptions): Promise<TokenResponse>;
|
|
14
|
+
export declare function refreshAccessToken(opts: {
|
|
15
|
+
tokenEndpoint: string;
|
|
16
|
+
clientId: string;
|
|
17
|
+
clientSecret?: string;
|
|
18
|
+
refreshToken: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
fetchImpl?: typeof fetch;
|
|
21
|
+
}): Promise<TokenResponse>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import open from "open";
|
|
3
|
+
import { generatePkcePair, generateState } from "./pkce.js";
|
|
4
|
+
export async function performAuthorizationCodeFlow(options) {
|
|
5
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
6
|
+
const opener = options.openBrowser ?? ((url) => open(url));
|
|
7
|
+
const pkce = generatePkcePair();
|
|
8
|
+
const state = generateState();
|
|
9
|
+
const { redirectUri, awaitCallback, closeServer } = await startLoopbackServer(options.preferredPorts);
|
|
10
|
+
const authorizeUrl = buildAuthorizationUrl({
|
|
11
|
+
endpoint: options.metadata.authorization_endpoint,
|
|
12
|
+
clientId: options.clientId,
|
|
13
|
+
redirectUri,
|
|
14
|
+
scope: options.scope,
|
|
15
|
+
state,
|
|
16
|
+
codeChallenge: pkce.challenge,
|
|
17
|
+
});
|
|
18
|
+
options.onAuthorizationUrl?.(authorizeUrl);
|
|
19
|
+
try {
|
|
20
|
+
await opener(authorizeUrl);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// The browser could not be opened automatically. The URL was already emitted via onAuthorizationUrl.
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const { code, state: returnedState } = await awaitCallback;
|
|
27
|
+
if (returnedState !== state) {
|
|
28
|
+
throw new Error("OAuth state does not match, possible CSRF attempt.");
|
|
29
|
+
}
|
|
30
|
+
return await exchangeCodeForToken({
|
|
31
|
+
tokenEndpoint: options.metadata.token_endpoint,
|
|
32
|
+
clientId: options.clientId,
|
|
33
|
+
clientSecret: options.clientSecret,
|
|
34
|
+
code,
|
|
35
|
+
redirectUri,
|
|
36
|
+
codeVerifier: pkce.verifier,
|
|
37
|
+
fetchImpl,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
closeServer();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function buildAuthorizationUrl(opts) {
|
|
45
|
+
const url = new URL(opts.endpoint);
|
|
46
|
+
url.searchParams.set("response_type", "code");
|
|
47
|
+
url.searchParams.set("client_id", opts.clientId);
|
|
48
|
+
url.searchParams.set("redirect_uri", opts.redirectUri);
|
|
49
|
+
url.searchParams.set("scope", opts.scope);
|
|
50
|
+
url.searchParams.set("state", opts.state);
|
|
51
|
+
url.searchParams.set("code_challenge", opts.codeChallenge);
|
|
52
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
53
|
+
return url.toString();
|
|
54
|
+
}
|
|
55
|
+
async function startLoopbackServer(preferredPorts) {
|
|
56
|
+
let resolveCallback;
|
|
57
|
+
let rejectCallback;
|
|
58
|
+
const awaitCallback = new Promise((resolve, reject) => {
|
|
59
|
+
resolveCallback = resolve;
|
|
60
|
+
rejectCallback = reject;
|
|
61
|
+
});
|
|
62
|
+
const server = createServer((req, res) => {
|
|
63
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
64
|
+
if (url.pathname !== "/callback") {
|
|
65
|
+
res.statusCode = 404;
|
|
66
|
+
res.end("Not Found");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const error = url.searchParams.get("error");
|
|
70
|
+
if (error) {
|
|
71
|
+
const description = url.searchParams.get("error_description") ?? "";
|
|
72
|
+
res.statusCode = 400;
|
|
73
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
74
|
+
res.end(renderHtml("Sign in failed", `The authorization server returned an error: <code>${escapeHtml(error)}</code>${description ? ` (${escapeHtml(description)})` : ""}.`));
|
|
75
|
+
rejectCallback(new Error(`Authorization server reported an error: ${error} ${description}`));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const code = url.searchParams.get("code");
|
|
79
|
+
const state = url.searchParams.get("state");
|
|
80
|
+
if (!code || !state) {
|
|
81
|
+
res.statusCode = 400;
|
|
82
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
83
|
+
res.end(renderHtml("Invalid response", "The callback is missing code or state."));
|
|
84
|
+
rejectCallback(new Error("Callback without code or state"));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
res.statusCode = 200;
|
|
88
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
89
|
+
res.end(renderHtml("Sign in successful", "You can close this window and return to the terminal."));
|
|
90
|
+
resolveCallback({ code, state });
|
|
91
|
+
});
|
|
92
|
+
const port = await listenOnFirstAvailablePort(server, preferredPorts ?? []);
|
|
93
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
94
|
+
return {
|
|
95
|
+
redirectUri,
|
|
96
|
+
awaitCallback,
|
|
97
|
+
closeServer: () => server.close(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function listenOnFirstAvailablePort(server, preferredPorts) {
|
|
101
|
+
const tryListen = (port) => new Promise((resolve, reject) => {
|
|
102
|
+
const onError = (err) => {
|
|
103
|
+
server.off("listening", onListening);
|
|
104
|
+
reject(err);
|
|
105
|
+
};
|
|
106
|
+
const onListening = () => {
|
|
107
|
+
server.off("error", onError);
|
|
108
|
+
const address = server.address();
|
|
109
|
+
resolve(address.port);
|
|
110
|
+
};
|
|
111
|
+
server.once("error", onError);
|
|
112
|
+
server.once("listening", onListening);
|
|
113
|
+
server.listen(port, "127.0.0.1");
|
|
114
|
+
});
|
|
115
|
+
for (const port of preferredPorts) {
|
|
116
|
+
try {
|
|
117
|
+
return await tryListen(port);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (err.code !== "EADDRINUSE") {
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return await tryListen(0);
|
|
126
|
+
}
|
|
127
|
+
async function exchangeCodeForToken(opts) {
|
|
128
|
+
const body = new URLSearchParams({
|
|
129
|
+
grant_type: "authorization_code",
|
|
130
|
+
code: opts.code,
|
|
131
|
+
redirect_uri: opts.redirectUri,
|
|
132
|
+
client_id: opts.clientId,
|
|
133
|
+
code_verifier: opts.codeVerifier,
|
|
134
|
+
});
|
|
135
|
+
if (opts.clientSecret) {
|
|
136
|
+
body.set("client_secret", opts.clientSecret);
|
|
137
|
+
}
|
|
138
|
+
const response = await opts.fetchImpl(opts.tokenEndpoint, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: {
|
|
141
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
142
|
+
accept: "application/json",
|
|
143
|
+
},
|
|
144
|
+
body: body.toString(),
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
const text = await response.text().catch(() => "");
|
|
148
|
+
throw new Error(`Token exchange failed (${response.status}): ${text}`);
|
|
149
|
+
}
|
|
150
|
+
return (await response.json());
|
|
151
|
+
}
|
|
152
|
+
export async function refreshAccessToken(opts) {
|
|
153
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
154
|
+
const body = new URLSearchParams({
|
|
155
|
+
grant_type: "refresh_token",
|
|
156
|
+
refresh_token: opts.refreshToken,
|
|
157
|
+
client_id: opts.clientId,
|
|
158
|
+
});
|
|
159
|
+
if (opts.clientSecret)
|
|
160
|
+
body.set("client_secret", opts.clientSecret);
|
|
161
|
+
if (opts.scope)
|
|
162
|
+
body.set("scope", opts.scope);
|
|
163
|
+
const response = await fetchImpl(opts.tokenEndpoint, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: {
|
|
166
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
167
|
+
accept: "application/json",
|
|
168
|
+
},
|
|
169
|
+
body: body.toString(),
|
|
170
|
+
});
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
const text = await response.text().catch(() => "");
|
|
173
|
+
throw new Error(`Token refresh failed (${response.status}): ${text}`);
|
|
174
|
+
}
|
|
175
|
+
return (await response.json());
|
|
176
|
+
}
|
|
177
|
+
function renderHtml(title, message) {
|
|
178
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(title)}</title><style>body{font-family:system-ui,sans-serif;max-width:520px;margin:64px auto;padding:0 24px;color:#1d1f23}h1{font-size:1.4rem;margin-bottom:0.5rem}p{line-height:1.5}</style></head><body><h1>${escapeHtml(title)}</h1><p>${message}</p></body></html>`;
|
|
179
|
+
}
|
|
180
|
+
function escapeHtml(input) {
|
|
181
|
+
return input
|
|
182
|
+
.replace(/&/g, "&")
|
|
183
|
+
.replace(/</g, "<")
|
|
184
|
+
.replace(/>/g, ">")
|
|
185
|
+
.replace(/"/g, """)
|
|
186
|
+
.replace(/'/g, "'");
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/auth/flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { randomBytes, createHash } from "node:crypto";
|
|
2
|
+
function base64UrlEncode(input) {
|
|
3
|
+
return input
|
|
4
|
+
.toString("base64")
|
|
5
|
+
.replace(/\+/g, "-")
|
|
6
|
+
.replace(/\//g, "_")
|
|
7
|
+
.replace(/=+$/, "");
|
|
8
|
+
}
|
|
9
|
+
export function generatePkcePair(byteLength = 32) {
|
|
10
|
+
if (byteLength < 32 || byteLength > 96) {
|
|
11
|
+
throw new Error("PKCE verifier byte length must be between 32 and 96.");
|
|
12
|
+
}
|
|
13
|
+
const verifier = base64UrlEncode(randomBytes(byteLength));
|
|
14
|
+
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
15
|
+
return { verifier, challenge, method: "S256" };
|
|
16
|
+
}
|
|
17
|
+
export function generateState(byteLength = 16) {
|
|
18
|
+
return base64UrlEncode(randomBytes(byteLength));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/auth/pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQtD,SAAS,eAAe,CAAC,KAAa;IACpC,OAAO,KAAK;SACT,QAAQ,CAAC,QAAQ,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAU,GAAG,EAAE;IAC9C,IAAI,UAAU,GAAG,EAAE,IAAI,UAAU,GAAG,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,eAAe,CAC/B,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAC/C,CAAC;IACF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAU,GAAG,EAAE;IAC3C,OAAO,eAAe,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ClientRegistrationRequest {
|
|
2
|
+
client_name: string;
|
|
3
|
+
redirect_uris: string[];
|
|
4
|
+
grant_types: string[];
|
|
5
|
+
response_types: string[];
|
|
6
|
+
token_endpoint_auth_method: "none" | "client_secret_basic" | "client_secret_post";
|
|
7
|
+
scope: string;
|
|
8
|
+
application_type?: "native" | "web";
|
|
9
|
+
}
|
|
10
|
+
export interface ClientRegistrationResponse {
|
|
11
|
+
client_id: string;
|
|
12
|
+
client_secret?: string;
|
|
13
|
+
client_id_issued_at?: number;
|
|
14
|
+
client_secret_expires_at?: number;
|
|
15
|
+
registration_access_token?: string;
|
|
16
|
+
registration_client_uri?: string;
|
|
17
|
+
redirect_uris?: string[];
|
|
18
|
+
token_endpoint_auth_method?: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function registerClient(registrationEndpoint: string, request: ClientRegistrationRequest, fetchImpl?: typeof fetch): Promise<ClientRegistrationResponse>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export async function registerClient(registrationEndpoint, request, fetchImpl = fetch) {
|
|
2
|
+
const response = await fetchImpl(registrationEndpoint, {
|
|
3
|
+
method: "POST",
|
|
4
|
+
headers: {
|
|
5
|
+
"content-type": "application/json",
|
|
6
|
+
accept: "application/json",
|
|
7
|
+
},
|
|
8
|
+
body: JSON.stringify(request),
|
|
9
|
+
});
|
|
10
|
+
if (!response.ok) {
|
|
11
|
+
const body = await safeReadText(response);
|
|
12
|
+
throw new Error(`Dynamic Client Registration failed (${response.status} ${response.statusText}): ${body}`);
|
|
13
|
+
}
|
|
14
|
+
const data = (await response.json());
|
|
15
|
+
if (!data.client_id) {
|
|
16
|
+
throw new Error("Registration response is missing client_id");
|
|
17
|
+
}
|
|
18
|
+
return data;
|
|
19
|
+
}
|
|
20
|
+
async function safeReadText(response) {
|
|
21
|
+
try {
|
|
22
|
+
return await response.text();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return "(response body unreadable)";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/auth/registration.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,oBAA4B,EAC5B,OAAkC,EAClC,YAA0B,KAAK;IAE/B,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,oBAAoB,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;SAC3B;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,uCAAuC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,IAAI,EAAE,CAC1F,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA+B,CAAC;IACnE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,QAAkB;IAC5C,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,4BAA4B,CAAC;IACtC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface StoredCredentials {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret?: string;
|
|
5
|
+
registrationAccessToken?: string;
|
|
6
|
+
registrationClientUri?: string;
|
|
7
|
+
accessToken?: string;
|
|
8
|
+
refreshToken?: string;
|
|
9
|
+
tokenType?: string;
|
|
10
|
+
scope?: string;
|
|
11
|
+
expiresAt?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface TokenResponse {
|
|
14
|
+
access_token: string;
|
|
15
|
+
token_type: string;
|
|
16
|
+
expires_in?: number;
|
|
17
|
+
refresh_token?: string;
|
|
18
|
+
scope?: string;
|
|
19
|
+
id_token?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function defaultCredentialsPath(): string;
|
|
22
|
+
export declare function loadCredentials(path: string): Promise<StoredCredentials | null>;
|
|
23
|
+
export declare function saveCredentials(path: string, creds: StoredCredentials): Promise<void>;
|
|
24
|
+
export declare function applyTokenResponse(base: StoredCredentials, token: TokenResponse, now?: number): StoredCredentials;
|
|
25
|
+
export declare function isTokenExpired(creds: StoredCredentials, now?: number): boolean;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile, chmod } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
export function defaultCredentialsPath() {
|
|
5
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
6
|
+
const base = xdg && xdg.length > 0 ? xdg : `${homedir()}/.config`;
|
|
7
|
+
return `${base}/zuwiki-mcp/credentials.json`;
|
|
8
|
+
}
|
|
9
|
+
export async function loadCredentials(path) {
|
|
10
|
+
try {
|
|
11
|
+
const raw = await readFile(path, "utf8");
|
|
12
|
+
return JSON.parse(raw);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
if (err.code === "ENOENT") {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function saveCredentials(path, creds) {
|
|
22
|
+
await mkdir(dirname(path), { recursive: true });
|
|
23
|
+
await writeFile(path, JSON.stringify(creds, null, 2) + "\n", "utf8");
|
|
24
|
+
try {
|
|
25
|
+
await chmod(path, 0o600);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// chmod is not available on Windows or non POSIX filesystems.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function applyTokenResponse(base, token, now = Date.now()) {
|
|
32
|
+
const expiresAt = token.expires_in
|
|
33
|
+
? now + Math.max(0, token.expires_in - 30) * 1000
|
|
34
|
+
: undefined;
|
|
35
|
+
return {
|
|
36
|
+
...base,
|
|
37
|
+
accessToken: token.access_token,
|
|
38
|
+
refreshToken: token.refresh_token ?? base.refreshToken,
|
|
39
|
+
tokenType: token.token_type ?? base.tokenType ?? "Bearer",
|
|
40
|
+
scope: token.scope ?? base.scope,
|
|
41
|
+
expiresAt,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function isTokenExpired(creds, now = Date.now()) {
|
|
45
|
+
if (!creds.accessToken)
|
|
46
|
+
return true;
|
|
47
|
+
if (!creds.expiresAt)
|
|
48
|
+
return false;
|
|
49
|
+
return creds.expiresAt <= now;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAwBlC,MAAM,UAAU,sBAAsB;IACpC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACxC,MAAM,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,CAAC;IAClE,OAAO,GAAG,IAAI,8BAA8B,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,KAAwB;IAC1E,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACrE,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,IAAuB,EACvB,KAAoB,EACpB,MAAc,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU;QAChC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,IAAI;QACjD,CAAC,CAAC,SAAS,CAAC;IACd,OAAO;QACL,GAAG,IAAI;QACP,WAAW,EAAE,KAAK,CAAC,YAAY;QAC/B,YAAY,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI,CAAC,YAAY;QACtD,SAAS,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,IAAI,QAAQ;QACzD,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK;QAChC,SAAS;KACV,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAwB,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IAC/E,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,KAAK,CAAC,SAAS,IAAI,GAAG,CAAC;AAChC,CAAC"}
|
package/dist/cli.d.ts
ADDED