@dain-os/mcp-server 0.12.0 → 0.13.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.
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The paste-PAT consent page served at GET /authorize.
3
+ *
4
+ * This is the one human-facing surface of the OAuth bridge. The user mints a
5
+ * DainOS API token, pastes it here, and the form POSTs back to /authorize,
6
+ * which issues an authorization code. All copy follows the project copy-style
7
+ * rule: UK English, no em/en dashes, plain ASCII punctuation.
8
+ *
9
+ * Every interpolated value originates from the OAuth client request, so it is
10
+ * HTML-escaped before it lands in an attribute. The PAT field is never
11
+ * pre-filled and is rendered as a password input.
12
+ */
13
+ import { PAT_SETTINGS_URL } from './config.js';
14
+ function escapeHtml(value) {
15
+ return value
16
+ .replace(/&/g, '&')
17
+ .replace(/</g, '&lt;')
18
+ .replace(/>/g, '&gt;')
19
+ .replace(/"/g, '&quot;')
20
+ .replace(/'/g, '&#39;');
21
+ }
22
+ function hiddenField(name, value) {
23
+ if (value === undefined)
24
+ return '';
25
+ return `<input type="hidden" name="${escapeHtml(name)}" value="${escapeHtml(value)}" />`;
26
+ }
27
+ export function renderConsentPage(params) {
28
+ const errorBanner = params.error
29
+ ? `<p class="error" role="alert">${escapeHtml(params.error)}</p>`
30
+ : '';
31
+ return `<!DOCTYPE html>
32
+ <html lang="en-GB">
33
+ <head>
34
+ <meta charset="utf-8" />
35
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
36
+ <meta name="robots" content="noindex" />
37
+ <title>Connect Claude to DainOS</title>
38
+ <style>
39
+ :root { color-scheme: light dark; }
40
+ * { box-sizing: border-box; }
41
+ body {
42
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
43
+ margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
44
+ background: #0b1120; color: #e2e8f0; padding: 24px;
45
+ }
46
+ .card {
47
+ width: 100%; max-width: 460px; background: #111827; border: 1px solid #1f2937;
48
+ border-radius: 14px; padding: 32px; box-shadow: 0 10px 40px rgba(0,0,0,0.4);
49
+ }
50
+ h1 { font-size: 20px; margin: 0 0 8px; }
51
+ p { font-size: 14px; line-height: 1.5; color: #94a3b8; margin: 0 0 16px; }
52
+ ol { font-size: 14px; line-height: 1.6; color: #cbd5e1; padding-left: 20px; margin: 0 0 20px; }
53
+ a { color: #60a5fa; }
54
+ label { display: block; font-size: 13px; font-weight: 600; margin: 0 0 6px; color: #e2e8f0; }
55
+ input[type="password"] {
56
+ width: 100%; padding: 11px 12px; font-size: 14px; border-radius: 8px;
57
+ border: 1px solid #334155; background: #0b1120; color: #e2e8f0; font-family: monospace;
58
+ }
59
+ input[type="password"]:focus { outline: 2px solid #2563eb; border-color: #2563eb; }
60
+ button {
61
+ width: 100%; margin-top: 18px; padding: 12px; font-size: 15px; font-weight: 600;
62
+ border: none; border-radius: 8px; background: #2563eb; color: white; cursor: pointer;
63
+ }
64
+ button:hover { background: #1d4ed8; }
65
+ .error {
66
+ background: #7f1d1d; color: #fecaca; border: 1px solid #b91c1c;
67
+ border-radius: 8px; padding: 10px 12px; font-size: 13px; margin: 0 0 16px;
68
+ }
69
+ .hint { font-size: 12px; color: #64748b; margin-top: 16px; }
70
+ </style>
71
+ </head>
72
+ <body>
73
+ <div class="card">
74
+ <h1>Connect Claude to DainOS</h1>
75
+ ${errorBanner}
76
+ <p>Claude is asking to connect to your DainOS account. Paste a personal access token to allow it.</p>
77
+ <ol>
78
+ <li>Open your <a href="${escapeHtml(PAT_SETTINGS_URL)}" target="_blank" rel="noopener">DainOS API tokens</a> page.</li>
79
+ <li>Create a token and copy it.</li>
80
+ <li>Paste it below, then choose Allow access.</li>
81
+ </ol>
82
+ <form method="POST" action="/authorize" autocomplete="off">
83
+ <label for="pat">DainOS API token</label>
84
+ <input id="pat" name="pat" type="password" placeholder="dain_pat_..." required autofocus />
85
+ ${hiddenField('client_id', params.clientId)}
86
+ ${hiddenField('redirect_uri', params.redirectUri)}
87
+ ${hiddenField('code_challenge', params.codeChallenge)}
88
+ ${hiddenField('code_challenge_method', params.codeChallengeMethod)}
89
+ ${hiddenField('state', params.state)}
90
+ ${hiddenField('scope', params.scope)}
91
+ <button type="submit">Allow access</button>
92
+ </form>
93
+ <p class="hint">Your token is encrypted into the connection and used only to act as you in DainOS. You can revoke it any time from the API tokens page.</p>
94
+ </div>
95
+ </body>
96
+ </html>`;
97
+ }
98
+ //# sourceMappingURL=consent-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-page.js","sourceRoot":"","sources":["../../src/oauth/consent-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAa/C,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,KAAyB;IAC1D,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,8BAA8B,UAAU,CAAC,IAAI,CAAC,YAAY,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAyB;IACzD,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK;QAC9B,CAAC,CAAC,iCAAiC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;QACjE,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA4CH,WAAW;;;+BAGc,UAAU,CAAC,gBAAgB,CAAC;;;;;;;QAOnD,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC;QACzC,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,WAAW,CAAC;QAC/C,WAAW,CAAC,gBAAgB,EAAE,MAAM,CAAC,aAAa,CAAC;QACnD,WAAW,CAAC,uBAAuB,EAAE,MAAM,CAAC,mBAAmB,CAAC;QAChE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;QAClC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;;;;;;QAMlC,CAAC;AACT,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Stateless token minting + verification for the OAuth bridge.
3
+ *
4
+ * Every artefact the authorization server hands out (authorization code,
5
+ * access token, refresh token, and even the dynamically-registered client_id)
6
+ * is an encrypted JWT (JWE, `dir` + `A256GCM`) under the key derived from
7
+ * MCP_OAUTH_SECRET. Encryption (not mere signing) matters because the payload
8
+ * embeds the user's PAT: a leaked token is no worse than a leaked PAT, which is
9
+ * the same blast radius as today's raw-Bearer model.
10
+ *
11
+ * `typ` discriminates the four token kinds so an access token can never be
12
+ * replayed as an authorization code, etc. `jose.jwtDecrypt` validates `exp`
13
+ * for us, so expired tokens throw on decrypt.
14
+ */
15
+ type TokenType = 'code' | 'access' | 'refresh' | 'client';
16
+ export interface CodeClaims {
17
+ pat: string;
18
+ code_challenge: string;
19
+ client_id: string;
20
+ redirect_uri: string;
21
+ }
22
+ export interface AccessClaims {
23
+ pat: string;
24
+ }
25
+ export interface RefreshClaims {
26
+ pat: string;
27
+ }
28
+ export interface ClientClaims {
29
+ redirect_uris: string[];
30
+ }
31
+ export declare function issueAuthorizationCode(claims: CodeClaims): Promise<string>;
32
+ export declare function readAuthorizationCode(token: string): Promise<CodeClaims & {
33
+ typ: TokenType;
34
+ }>;
35
+ export declare function issueAccessToken(claims: AccessClaims): Promise<string>;
36
+ export declare function readAccessToken(token: string): Promise<AccessClaims>;
37
+ export declare function issueRefreshToken(claims: RefreshClaims): Promise<string>;
38
+ export declare function readRefreshToken(token: string): Promise<RefreshClaims>;
39
+ export declare function issueClientId(claims: ClientClaims): Promise<string>;
40
+ export declare function readClientId(token: string): Promise<ClientClaims>;
41
+ /**
42
+ * Verify a PKCE `code_verifier` against the stored S256 `code_challenge`.
43
+ *
44
+ * challenge == base64url( SHA-256( verifier ) ). Compared in constant time.
45
+ */
46
+ export declare function verifyPkceS256(verifier: string, challenge: string): boolean;
47
+ export {};
48
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/oauth/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,KAAK,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAuBD,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhF;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG;IAAE,GAAG,EAAE,SAAS,CAAA;CAAE,CAAC,CAEnG;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5E;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAE1E;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAE9E;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAE5E;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAEzE;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAEvE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAO3E"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Stateless token minting + verification for the OAuth bridge.
3
+ *
4
+ * Every artefact the authorization server hands out (authorization code,
5
+ * access token, refresh token, and even the dynamically-registered client_id)
6
+ * is an encrypted JWT (JWE, `dir` + `A256GCM`) under the key derived from
7
+ * MCP_OAUTH_SECRET. Encryption (not mere signing) matters because the payload
8
+ * embeds the user's PAT: a leaked token is no worse than a leaked PAT, which is
9
+ * the same blast radius as today's raw-Bearer model.
10
+ *
11
+ * `typ` discriminates the four token kinds so an access token can never be
12
+ * replayed as an authorization code, etc. `jose.jwtDecrypt` validates `exp`
13
+ * for us, so expired tokens throw on decrypt.
14
+ */
15
+ import { EncryptJWT, jwtDecrypt } from 'jose';
16
+ import { createHash, timingSafeEqual } from 'node:crypto';
17
+ import { ACCESS_TOKEN_TTL_SECONDS, AUTH_CODE_TTL_SECONDS, CLIENT_REG_TTL_SECONDS, REFRESH_TOKEN_TTL_SECONDS, getEncryptionKey, } from './config.js';
18
+ async function encrypt(typ, claims, ttlSeconds) {
19
+ const nowSeconds = Math.floor(Date.now() / 1000);
20
+ return new EncryptJWT({ ...claims, typ })
21
+ .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
22
+ .setIssuedAt(nowSeconds)
23
+ .setExpirationTime(nowSeconds + ttlSeconds)
24
+ .encrypt(getEncryptionKey());
25
+ }
26
+ async function decrypt(token, expectedTyp) {
27
+ const { payload } = await jwtDecrypt(token, getEncryptionKey());
28
+ if (payload['typ'] !== expectedTyp) {
29
+ throw new Error(`Token type mismatch: expected ${expectedTyp}, got ${String(payload['typ'])}`);
30
+ }
31
+ return payload;
32
+ }
33
+ export async function issueAuthorizationCode(claims) {
34
+ return encrypt('code', { ...claims }, AUTH_CODE_TTL_SECONDS);
35
+ }
36
+ export async function readAuthorizationCode(token) {
37
+ return decrypt(token, 'code');
38
+ }
39
+ export async function issueAccessToken(claims) {
40
+ return encrypt('access', { ...claims }, ACCESS_TOKEN_TTL_SECONDS);
41
+ }
42
+ export async function readAccessToken(token) {
43
+ return decrypt(token, 'access');
44
+ }
45
+ export async function issueRefreshToken(claims) {
46
+ return encrypt('refresh', { ...claims }, REFRESH_TOKEN_TTL_SECONDS);
47
+ }
48
+ export async function readRefreshToken(token) {
49
+ return decrypt(token, 'refresh');
50
+ }
51
+ export async function issueClientId(claims) {
52
+ return encrypt('client', { ...claims }, CLIENT_REG_TTL_SECONDS);
53
+ }
54
+ export async function readClientId(token) {
55
+ return decrypt(token, 'client');
56
+ }
57
+ /**
58
+ * Verify a PKCE `code_verifier` against the stored S256 `code_challenge`.
59
+ *
60
+ * challenge == base64url( SHA-256( verifier ) ). Compared in constant time.
61
+ */
62
+ export function verifyPkceS256(verifier, challenge) {
63
+ if (!verifier || !challenge)
64
+ return false;
65
+ const computed = createHash('sha256').update(verifier).digest('base64url');
66
+ const a = Buffer.from(computed);
67
+ const b = Buffer.from(challenge);
68
+ if (a.length !== b.length)
69
+ return false;
70
+ return timingSafeEqual(a, b);
71
+ }
72
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/oauth/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * OAuth 2.1 endpoint handlers for the paste-PAT bridge.
3
+ *
4
+ * Endpoints:
5
+ * GET /.well-known/oauth-protected-resource metadata (RFC 9728)
6
+ * GET /.well-known/oauth-authorization-server metadata (RFC 8414)
7
+ * POST /register Dynamic Client Registration (RFC 7591)
8
+ * GET /authorize consent page (paste PAT)
9
+ * POST /authorize issue authorization code
10
+ * POST /token code -> token, refresh -> token
11
+ *
12
+ * The pure cores (validateAuthorizeRequest, createAuthorizationCode,
13
+ * exchangeAuthorizationCode, refreshTokens, recoverPat) are exported and unit
14
+ * tested directly; the handle* wrappers only do req/res plumbing.
15
+ */
16
+ import type { IncomingMessage, ServerResponse } from 'node:http';
17
+ /** An OAuth error renderable as an RFC 6749 error response. */
18
+ export declare class OAuthError extends Error {
19
+ readonly error: string;
20
+ readonly status: number;
21
+ constructor(error: string, description: string, status?: number);
22
+ toBody(): {
23
+ error: string;
24
+ error_description: string;
25
+ };
26
+ }
27
+ export interface TokenResponse {
28
+ access_token: string;
29
+ token_type: 'Bearer';
30
+ expires_in: number;
31
+ refresh_token: string;
32
+ scope: string;
33
+ }
34
+ export interface AuthorizeParams {
35
+ clientId: string;
36
+ redirectUri: string;
37
+ codeChallenge: string;
38
+ codeChallengeMethod: string;
39
+ state?: string;
40
+ scope?: string;
41
+ }
42
+ export type AuthorizeValidation = {
43
+ ok: true;
44
+ parsed: AuthorizeParams;
45
+ } | {
46
+ ok: false;
47
+ error: string;
48
+ description: string;
49
+ };
50
+ /**
51
+ * Validate the inbound /authorize parameters and the dynamically-registered
52
+ * client. Returns a discriminated result rather than throwing so both the GET
53
+ * (render) and POST (submit) paths can branch cleanly.
54
+ */
55
+ export declare function validateAuthorizeRequest(params: Record<string, string | undefined>): Promise<AuthorizeValidation>;
56
+ /** Mint a short-lived authorization code embedding the PAT + PKCE challenge. */
57
+ export declare function createAuthorizationCode(input: {
58
+ pat: string;
59
+ clientId: string;
60
+ redirectUri: string;
61
+ codeChallenge: string;
62
+ }): Promise<string>;
63
+ /** authorization_code grant: verify the code + PKCE, return fresh tokens. */
64
+ export declare function exchangeAuthorizationCode(form: Record<string, string>): Promise<TokenResponse>;
65
+ /** refresh_token grant: re-issue an access token (and rotate the refresh token). */
66
+ export declare function refreshTokens(form: Record<string, string>): Promise<TokenResponse>;
67
+ /**
68
+ * Recover the underlying PAT from an inbound Authorization Bearer value.
69
+ *
70
+ * Accepts either a raw `dain_pat_...` (Claude Code path, unchanged) or an
71
+ * OAuth access token (encrypted JWT minted by /token). Returns null when the
72
+ * value is neither, so the caller can emit a 401 + WWW-Authenticate challenge.
73
+ */
74
+ export declare function recoverPat(bearer: string | null): Promise<string | null>;
75
+ /** Accept https on an allowlisted host, or http on loopback (local testing). */
76
+ export declare function isAllowedRedirectUri(uri: string): boolean;
77
+ export declare function handleProtectedResourceMetadata(res: ServerResponse, baseUrl: string): void;
78
+ export declare function handleAuthorizationServerMetadata(res: ServerResponse, baseUrl: string): void;
79
+ export declare function handleRegister(req: IncomingMessage & {
80
+ body?: unknown;
81
+ }, res: ServerResponse): Promise<void>;
82
+ /** GET shows the paste-PAT page; POST validates the PAT and issues a code. */
83
+ export declare function handleAuthorize(req: IncomingMessage & {
84
+ body?: unknown;
85
+ }, res: ServerResponse, query: Record<string, string | undefined>): Promise<void>;
86
+ export declare function handleToken(req: IncomingMessage & {
87
+ body?: unknown;
88
+ }, res: ServerResponse): Promise<void>;
89
+ //# sourceMappingURL=handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/oauth/handlers.ts"],"names":[],"mappings":"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"}